diff options
79 files changed, 2395 insertions, 600 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 785d78b047..f4d1862dc7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -58,6 +58,7 @@ ENDIF() if(ENABLE_GR_CTRLPORT) set(GR_CTRLPORT True) + add_definitions(-DGR_CTRLPORT) endif(ENABLE_GR_CTRLPORT) ######################################################################## @@ -325,7 +326,7 @@ configure_file( ${CMAKE_BINARY_DIR}/cmake/Modules/GnuradioConfigVersion.cmake @ONLY) -SET(cmake_configs +SET(cmake_configs ${CMAKE_SOURCE_DIR}/cmake/Modules/GnuradioConfig.cmake ${CMAKE_BINARY_DIR}/cmake/Modules/GnuradioConfigVersion.cmake ) @@ -360,13 +361,13 @@ message(STATUS "Building for version: ${VERSION} / ${LIBVER}") # Create a config.h with some definitions to export to other projects. CONFIGURE_FILE( ${CMAKE_CURRENT_SOURCE_DIR}/config.h.in - ${CMAKE_CURRENT_BINARY_DIR}/config.h + ${CMAKE_CURRENT_BINARY_DIR}/gnuradio-runtime/include/gnuradio/config.h ) # Install config.h in include/gnuradio install( FILES - ${CMAKE_CURRENT_BINARY_DIR}/config.h + ${CMAKE_CURRENT_BINARY_DIR}/gnuradio-runtime/include/gnuradio/config.h DESTINATION ${GR_INCLUDE_DIR}/gnuradio COMPONENT "runtime_devel" ) diff --git a/docs/doxygen/other/stream_tags.dox b/docs/doxygen/other/stream_tags.dox index 2a03c9629c..851740984e 100644 --- a/docs/doxygen/other/stream_tags.dox +++ b/docs/doxygen/other/stream_tags.dox @@ -63,15 +63,26 @@ at <em>nitems_written(0)+i</em> for the 0th output port. The stream tags API consists of four functions, two to add and two to get the stream tags. These functions are: -\li add_item_tag: Adds an item tag to a particular output port using a +\li gr::block::add_item_tag: Adds an item tag to a particular output port using a gr::tag_t data type. -\li add_item_tag: Adds an item tag to a particular output port where -each value of the tag is explicitly given. -\li get_tags_in_range: Gets all tags from a particular input port between -a certain range of items (in absolute item time). -\li 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::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 add_item_tag Adding a Tag to a Stream diff --git a/gnuradio-runtime/include/gnuradio/block.h b/gnuradio-runtime/include/gnuradio/block.h index 112d251587..7390e93bc0 100644 --- a/gnuradio-runtime/include/gnuradio/block.h +++ b/gnuradio-runtime/include/gnuradio/block.h @@ -562,7 +562,7 @@ namespace gr { int set_thread_priority(int priority); bool update_rate() const; - + // ---------------------------------------------------------------------------- private: @@ -699,6 +699,50 @@ namespace gr { uint64_t abs_end, const pmt::pmt_t &key); + /*! + * \brief Gets all tags within the relative window of the current call to work. + * + * \details + * + * This opperates much like get_tags_in_range but allows us to + * work within the current window of items. Item range is + * therefore within the possible range of 0 to + * ninput_items[whic_input]. + * + * Range of items counts from \p rel_start to \p rel_end-1 within + * current window. + * + * 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 rel_start a uint64 count of the start of the range of interest + * \param rel_end a uint64 count of the end of the range of interest + */ + void get_tags_in_window(std::vector<tag_t> &v, + unsigned int which_input, + uint64_t rel_start, + uint64_t rel_end); + + /*! + * \brief Operates like gr::block::get_tags_in_window with the + * ability to only return tags with the specified \p key. + * + * \details + * + * \param v a vector reference to return tags into + * \param which_input an integer of which input stream to pull from + * \param rel_start a uint64 count of the start of the range of interest + * \param rel_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_window(std::vector<tag_t> &v, + unsigned int which_input, + uint64_t rel_start, + uint64_t rel_end, + const pmt::pmt_t &key); + void enable_update_rate(bool en); std::vector<long> d_max_output_buffer; @@ -708,11 +752,11 @@ namespace gr { * setting/resetting of parameters thread-safe. * * Used by calling gr::thread::scoped_lock l(d_setlock); - */ + */ gr::thread::mutex d_setlock; /*! Used by blocks to access the logger system. - */ + */ gr::logger_ptr d_logger; gr::logger_ptr d_debug_logger; diff --git a/gnuradio-runtime/lib/block.cc b/gnuradio-runtime/lib/block.cc index 442f8dde77..7a1364def9 100644 --- a/gnuradio-runtime/lib/block.cc +++ b/gnuradio-runtime/lib/block.cc @@ -29,6 +29,7 @@ #include <gnuradio/block_detail.h> #include <gnuradio/buffer.h> #include <gnuradio/prefs.h> +#include <gnuradio/config.h> #include <stdexcept> #include <iostream> @@ -288,19 +289,42 @@ namespace gr { void block::get_tags_in_range(std::vector<tag_t> &v, - unsigned int which_output, + unsigned int which_input, uint64_t start, uint64_t end) { - d_detail->get_tags_in_range(v, which_output, start, end, unique_id()); + d_detail->get_tags_in_range(v, which_input, start, end, unique_id()); } void block::get_tags_in_range(std::vector<tag_t> &v, - unsigned int which_output, + unsigned int which_input, uint64_t start, uint64_t end, const pmt::pmt_t &key) { - d_detail->get_tags_in_range(v, which_output, start, end, key, unique_id()); + d_detail->get_tags_in_range(v, which_input, start, end, key, unique_id()); + } + + void + block::get_tags_in_window(std::vector<tag_t> &v, + unsigned int which_input, + uint64_t start, uint64_t end) + { + d_detail->get_tags_in_range(v, which_input, + nitems_read(which_input) + start, + nitems_read(which_input) + end, + unique_id()); + } + + void + block::get_tags_in_window(std::vector<tag_t> &v, + unsigned int which_input, + uint64_t start, uint64_t end, + const pmt::pmt_t &key) + { + d_detail->get_tags_in_range(v, which_input, + nitems_read(which_input) + start, + nitems_read(which_input) + end, + key, unique_id()); } block::tag_propagation_policy_t @@ -361,7 +385,7 @@ namespace gr { } } - int + int block::active_thread_priority() { if(d_detail) { @@ -370,13 +394,13 @@ namespace gr { return -1; } - int + int block::thread_priority() { return d_priority; } - int + int block::set_thread_priority(int priority) { d_priority = priority; @@ -402,10 +426,10 @@ namespace gr { throw std::invalid_argument("basic_block::max_output_buffer: port out of range."); return d_max_output_buffer[i]; } - + void block::set_max_output_buffer(long max_output_buffer) - { + { for(int i = 0; i < output_signature()->max_streams(); i++) { set_max_output_buffer(i, max_output_buffer); } @@ -417,9 +441,9 @@ namespace gr { if((size_t)port >= d_max_output_buffer.size()) d_max_output_buffer.push_back(max_output_buffer); else - d_max_output_buffer[port] = max_output_buffer; + d_max_output_buffer[port] = max_output_buffer; } - + long block::min_output_buffer(size_t i) { @@ -427,7 +451,7 @@ namespace gr { throw std::invalid_argument("basic_block::min_output_buffer: port out of range."); return d_min_output_buffer[i]; } - + void block::set_min_output_buffer(long min_output_buffer) { @@ -436,14 +460,14 @@ namespace gr { set_min_output_buffer(i, min_output_buffer); } } - + void block::set_min_output_buffer(int port, long min_output_buffer) { if((size_t)port >= d_min_output_buffer.size()) d_min_output_buffer.push_back(min_output_buffer); else - d_min_output_buffer[port] = min_output_buffer; + d_min_output_buffer[port] = min_output_buffer; } @@ -713,7 +737,7 @@ namespace gr { block::setup_pc_rpc() { d_pc_rpc_set = true; -#ifdef GR_CTRLPORT +#if defined(GR_CTRLPORT) && defined(GR_PERFORMANCE_COUNTERS) d_rpc_vars.push_back( rpcbasic_sptr(new rpcbasic_register_get<block, float>( alias(), "noutput_items", &block::pc_noutput_items, @@ -825,7 +849,7 @@ namespace gr { pmt::make_c32vector(0,0), pmt::make_c32vector(0,1), pmt::make_c32vector(0,0), "", "Var. of how full output buffers are", RPC_PRIVLVL_MIN, DISPTIME | DISPOPTSTRIP))); -#endif /* GR_CTRLPORT */ +#endif /* defined(GR_CTRLPORT) && defined(GR_PERFORMANCE_COUNTERS) */ } std::ostream& diff --git a/gnuradio-runtime/python/gnuradio/gr/qa_tag_utils.py b/gnuradio-runtime/python/gnuradio/gr/qa_tag_utils.py index 56797a46dd..1a08ac5ae6 100755 --- a/gnuradio-runtime/python/gnuradio/gr/qa_tag_utils.py +++ b/gnuradio-runtime/python/gnuradio/gr/qa_tag_utils.py @@ -47,6 +47,36 @@ class test_tag_utils (gr_unittest.TestCase): self.assertEqual(pt.value, 23) self.assertEqual(pt.offset, 10) + def test_002(self): + offset = 10 + key = pmt.string_to_symbol('key') + value = pmt.from_long(23) + srcid = pmt.from_bool(False) + + format_dict = {'offset': offset, + 'key': key, + 'value': value, + 'srcid': srcid} + format_list = [offset, key, value, srcid] + format_tuple = (offset, key, value, srcid) + + t_dict = gr.python_to_tag(format_dict) + t_list = gr.python_to_tag(format_list) + t_tuple = gr.python_to_tag(format_tuple) + + self.assertTrue(pmt.equal(t_dict.key, key)) + self.assertTrue(pmt.equal(t_dict.value, value)) + self.assertEqual(t_dict.offset, offset) + + self.assertTrue(pmt.equal(t_list.key, key)) + self.assertTrue(pmt.equal(t_list.value, value)) + self.assertEqual(t_list.offset, offset) + + self.assertTrue(pmt.equal(t_tuple.key, key)) + self.assertTrue(pmt.equal(t_tuple.value, value)) + self.assertEqual(t_tuple.offset, offset) + + if __name__ == '__main__': print 'hi' diff --git a/gnuradio-runtime/python/gnuradio/gr/tag_utils.py b/gnuradio-runtime/python/gnuradio/gr/tag_utils.py index ad80bff23a..cbca9d44ae 100644 --- a/gnuradio-runtime/python/gnuradio/gr/tag_utils.py +++ b/gnuradio-runtime/python/gnuradio/gr/tag_utils.py @@ -28,4 +28,70 @@ def tag_to_pmt(tag): newtag.srcid = pmt.from_python(tag.srcid) return newtag +def python_to_tag(tag_struct): + """ + Convert a Python list/tuple/dictionary to a stream tag. + + When using a list or tuple format, this function expects the format: + tag_struct[0] --> tag's offset (as an integer) + tag_struct[1] --> tag's key (as a PMT) + tag_struct[2] --> tag's value (as a PMT) + tag_struct[3] --> tag's srcid (as a PMT) + + When using a dictionary, we specify the dictionary keys using: + tag_struct['offset'] --> tag's offset (as an integer) + tag_struct['key'] --> tag's key (as a PMT) + tag_struct['value'] --> tag's value (as a PMT) + tag_struct['srcid'] --> tag's srcid (as a PMT) + + If the function can take the Python object and successfully + construct a tag, it will return the tag. Otherwise, it will return + None. + """ + good = False + tag = gr.tag_t() + if(type(tag_struct) == dict): + if(tag_struct.has_key('offset')): + if(isinstance(tag_struct['offset'], (int,long))): + tag.offset = tag_struct['offset'] + good = True + + if(tag_struct.has_key('key')): + if(isinstance(tag_struct['key'], pmt.pmt_swig.swig_int_ptr)): + tag.key = tag_struct['key'] + good = True + + if(tag_struct.has_key('value')): + if(isinstance(tag_struct['value'], pmt.pmt_swig.swig_int_ptr)): + tag.value = tag_struct['value'] + good = True + + if(tag_struct.has_key('srcid')): + if(isinstance(tag_struct['srcid'], pmt.pmt_swig.swig_int_ptr)): + tag.srcid = tag_struct['srcid'] + good = True + + elif(type(tag_struct) == list or type(tag_struct) == tuple): + if(len(tag_struct) == 4): + if(isinstance(tag_struct[0], (int,long))): + tag.offset = tag_struct[0] + good = True + + if(isinstance(tag_struct[1], pmt.pmt_swig.swig_int_ptr)): + tag.key = tag_struct[1] + good = True + + if(isinstance(tag_struct[2], pmt.pmt_swig.swig_int_ptr)): + tag.value = tag_struct[2] + good = True + + if(isinstance(tag_struct[3], pmt.pmt_swig.swig_int_ptr)): + tag.srcid = tag_struct[3] + good = True + + if(good): + return tag + else: + return None + diff --git a/gr-blocks/grc/blocks_abs_xx.xml b/gr-blocks/grc/blocks_abs_xx.xml new file mode 100644 index 0000000000..65ef97ddff --- /dev/null +++ b/gr-blocks/grc/blocks_abs_xx.xml @@ -0,0 +1,49 @@ +<?xml version="1.0"?> +<!-- +################################################### +## Abs Block +################################################### + --> +<block> + <name>Abs</name> + <key>blocks_abs_xx</key> + <import>from gnuradio import blocks</import> + <make>blocks.abs_$(type.fcn)()</make> + <param> + <name>IO Type</name> + <key>type</key> + <type>enum</type> + <option> + <name>Int</name> + <key>int</key> + <opt>fcn:ii</opt> + </option> + <option> + <name>Short</name> + <key>short</key> + <opt>fcn:ss</opt> + </option> + <option> + <name>Float</name> + <key>float</key> + <opt>fcn:ff</opt> + </option> + </param> + <param> + <name>Num Inputs</name> + <key>num_inputs</key> + <value>1</value> + <type>int</type> + </param> + <check>$num_inputs >= 1</check> + <sink> + <name>in</name> + <type>$type</type> + <nports>$num_inputs</nports> + </sink> + <source> + <name>out</name> + <type>$type</type> + <nports>$num_inputs</nports> + </source> +</block> diff --git a/gr-blocks/grc/blocks_block_tree.xml b/gr-blocks/grc/blocks_block_tree.xml index bf0c55ff2e..22d0e5c7c8 100644 --- a/gr-blocks/grc/blocks_block_tree.xml +++ b/gr-blocks/grc/blocks_block_tree.xml @@ -87,6 +87,7 @@ </cat> <cat> <name>Math Operators</name> + <block>blocks_abs_xx</block> <block>blocks_add_xx</block> <block>blocks_add_const_vxx</block> <block>blocks_divide_xx</block> diff --git a/gr-blocks/grc/blocks_null_sink.xml b/gr-blocks/grc/blocks_null_sink.xml index 2ae20e619a..f2907d20bd 100644 --- a/gr-blocks/grc/blocks_null_sink.xml +++ b/gr-blocks/grc/blocks_null_sink.xml @@ -45,10 +45,27 @@ <value>1</value> <type>int</type> </param> + <param> + <name>Num Inputs</name> + <key>num_inputs</key> + <value>1</value> + <type>int</type> + </param> + <param> + <name>Bus Connections</name> + <key>bus_conns</key> + <value>[[0,],]</value> + <type>raw</type> + <hide>part</hide> + </param> + <check>$num_inputs >= 1</check> <check>$vlen > 0</check> <sink> <name>in</name> <type>$type</type> <vlen>$vlen</vlen> + <nports>$num_inputs</nports> </sink> + <bus_structure_sink>$bus_conns</bus_structure_sink> </block> + diff --git a/gr-blocks/grc/blocks_null_source.xml b/gr-blocks/grc/blocks_null_source.xml index 01d3905cab..9c109e651f 100644 --- a/gr-blocks/grc/blocks_null_source.xml +++ b/gr-blocks/grc/blocks_null_source.xml @@ -45,10 +45,26 @@ <value>1</value> <type>int</type> </param> + <param> + <name>Num Outputs</name> + <key>num_outputs</key> + <value>1</value> + <type>int</type> + </param> + <param> + <name>Bus Connections</name> + <key>bus_conns</key> + <value>[[0,],]</value> + <type>raw</type> + <hide>part</hide> + </param> + <check>$num_outputs >= 1</check> <check>$vlen > 0</check> <source> <name>out</name> <type>$type</type> <vlen>$vlen</vlen> + <nports>$num_outputs</nports> </source> + <bus_structure_source>$bus_conns</bus_structure_source> </block> diff --git a/gr-blocks/grc/blocks_sub_xx.xml b/gr-blocks/grc/blocks_sub_xx.xml index ae01cf74aa..88b5ccbb57 100644 --- a/gr-blocks/grc/blocks_sub_xx.xml +++ b/gr-blocks/grc/blocks_sub_xx.xml @@ -48,7 +48,7 @@ <type>int</type> </param> <check>$vlen > 0</check> - <check>$num_inputs >= 2</check> + <check>$num_inputs >= 1</check> <sink> <name>in</name> <type>$type</type> diff --git a/gr-blocks/include/gnuradio/blocks/CMakeLists.txt b/gr-blocks/include/gnuradio/blocks/CMakeLists.txt index c120242c01..7b8a0d6dc4 100644 --- a/gr-blocks/include/gnuradio/blocks/CMakeLists.txt +++ b/gr-blocks/include/gnuradio/blocks/CMakeLists.txt @@ -64,6 +64,7 @@ endmacro(expand_h) ######################################################################## # Invoke macro to generate various sources ######################################################################## +expand_h(abs_XX ss ii ff) expand_h(add_XX ss ii cc) expand_h(add_const_XX bb ss ii ff cc) expand_h(add_const_vXX bb ss ii ff cc) diff --git a/gr-blocks/include/gnuradio/blocks/abs_XX.h.t b/gr-blocks/include/gnuradio/blocks/abs_XX.h.t new file mode 100644 index 0000000000..a30e752b4b --- /dev/null +++ b/gr-blocks/include/gnuradio/blocks/abs_XX.h.t @@ -0,0 +1,58 @@ +/* -*- c++ -*- */ +/* + * Copyright 2012 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. + */ + +// @WARNING@ + +#ifndef @GUARD_NAME@ +#define @GUARD_NAME@ + +#include <gnuradio/blocks/api.h> +#include <gnuradio/sync_block.h> + +namespace gr { + namespace blocks { + + /*! + * \brief output[m] = abs(input[m]) for all M streams. + * \ingroup boolean_operators_blk + * + * \details + * absolute value of data stream (Strip sign) + */ + class BLOCKS_API @NAME@ : virtual public sync_block + { + + public: + + // gr::blocks::@NAME@::sptr + typedef boost::shared_ptr<@NAME@> sptr; + + /*! + * \brief Create an instance of @NAME@ + */ + static sptr make(); + }; + + } /* namespace blocks */ +} /* namespace gr */ + +#endif /* @GUARD_NAME@ */ diff --git a/gr-blocks/include/gnuradio/blocks/message_strobe_random.h b/gr-blocks/include/gnuradio/blocks/message_strobe_random.h index f399f637d1..8f5a20e1b3 100644 --- a/gr-blocks/include/gnuradio/blocks/message_strobe_random.h +++ b/gr-blocks/include/gnuradio/blocks/message_strobe_random.h @@ -43,8 +43,11 @@ namespace gr { * \ingroup message_tools_blk * * \details - * Takes a PMT message and sends it out every \p period_ms - * milliseconds. Useful for testing/debugging the message system. + + * Takes a PMT message and sends it out every at random + * intervals. The interval is basedon a random distribution, \p + * dist, with specified mean (\p mean_ms) and variance (\p + * std_ms). Useful for testing/debugging the message system. */ class BLOCKS_API message_strobe_random : virtual public block { @@ -53,12 +56,14 @@ namespace gr { typedef boost::shared_ptr<message_strobe_random> sptr; /*! - * Make a message stobe block to send message \p msg every \p - * period_ms milliseconds. + * Make a message stobe block to sends message \p msg at random + * intervals defined by the distribution \p dist with mean \p + * mean_ms and standard deviation \p std_ms. * * \param msg The message to send as a PMT. - * \param period_ms the time period in milliseconds in which to - * send \p msg. + * \param dist The random distribution from which to draw events. + * \param mean_ms The mean of the distribution. + * \param std_ms The standard deviation of the distribution. */ static sptr make(pmt::pmt_t msg, message_strobe_random_distribution_t dist, float mean_ms, float std_ms); @@ -104,7 +109,7 @@ namespace gr { * Get the std of strobe_random. */ virtual float std() const = 0; - + }; } /* namespace blocks */ diff --git a/gr-blocks/include/gnuradio/blocks/tagged_stream_to_pdu.h b/gr-blocks/include/gnuradio/blocks/tagged_stream_to_pdu.h index 9d5f509952..af3279217e 100644 --- a/gr-blocks/include/gnuradio/blocks/tagged_stream_to_pdu.h +++ b/gr-blocks/include/gnuradio/blocks/tagged_stream_to_pdu.h @@ -1,6 +1,6 @@ /* -*- c++ -*- */ /* - * Copyright 2013 Free Software Foundation, Inc. + * Copyright 2013,2014 Free Software Foundation, Inc. * * This file is part of GNU Radio * @@ -25,16 +25,21 @@ #include <gnuradio/blocks/api.h> #include <gnuradio/blocks/pdu.h> -#include <gnuradio/sync_block.h> +#include <gnuradio/tagged_stream_block.h> namespace gr { namespace blocks { /*! - * \brief Turns received stream data and tags into PDUs + * \brief Turns received stream data and tags into PDUs and sends + * them through a message port. * \ingroup message_tools_blk + * + * The sent message is a PMT-pair (created by pmt::cons()). The + * first element is a dictionary containing all the tags. The + * second is a vector containing the actual data. */ - class BLOCKS_API tagged_stream_to_pdu : virtual public sync_block + class BLOCKS_API tagged_stream_to_pdu : virtual public tagged_stream_block { public: // gr::blocks::tagged_stream_to_pdu::sptr @@ -43,8 +48,8 @@ namespace gr { /*! * \brief Construct a tagged_stream_to_pdu block * \param type PDU type of pdu::vector_type - * \param lengthtagname The name of the tag that specifies how long the packet is. - * Defaults to 'packet_len'. + * \param lengthtagname The name of the tag that specifies + * how long the packet is. */ static sptr make(pdu::vector_type type, const std::string& lengthtagname="packet_len"); diff --git a/gr-blocks/include/gnuradio/blocks/vector_source_X.h.t b/gr-blocks/include/gnuradio/blocks/vector_source_X.h.t index 7ff18ad083..d5298e8b47 100644 --- a/gr-blocks/include/gnuradio/blocks/vector_source_X.h.t +++ b/gr-blocks/include/gnuradio/blocks/vector_source_X.h.t @@ -32,8 +32,36 @@ namespace gr { namespace blocks { /*! - * \brief source of @TYPE@'s that gets its data from a vector + * \brief Source that streams @TYPE@ items based on the input \p data vector. * \ingroup misc_blk + * + * \details + * This block produces a stream of samples based on an input + * vector. In C++, this is a std::vector<@TYPE@>, and in Python, + * this is either a list or tuple. The data can repeat infinitely + * until the flowgraph is terminated by some other event or, the + * default, run the data once and stop. + * + * The vector source can also produce stream tags with the + * data. Pass in a vector of gr::tag_t objects and they will be + * emitted based on the specified offset of the tag. + * + * GNU Radio provides a utility Python module in gr.tag_utils to + * convert between tags and Python objects: + * gr.tag_utils.python_to_tag. + * + * We can create tags as Python lists (or tuples) using the list + * structure [int offset, pmt key, pmt value, pmt srcid]. It is + * important to define the list/tuple with the values in the + * correct order and with the correct data type. A python + * dictionary can also be used using the keys: "offset", "key", + * "value", and "srcid" with the same data types as for the lists. + * + * When given a list of tags, the vector source will emit the tags + * repeatedly by updating the offset relative to the vector stream + * length. That is, if the vector has 500 items and a tag has an + * offset of 0, that tag will be placed on item 0, 500, 1000, + * 1500, etc. */ class BLOCKS_API @NAME@ : virtual public sync_block { diff --git a/gr-blocks/lib/CMakeLists.txt b/gr-blocks/lib/CMakeLists.txt index fba0e4b771..0e7aab5e7c 100644 --- a/gr-blocks/lib/CMakeLists.txt +++ b/gr-blocks/lib/CMakeLists.txt @@ -90,6 +90,7 @@ endmacro(expand_cc_h_impl) ######################################################################## # Invoke macro to generate various sources ######################################################################## +expand_cc_h_impl(abs_XX ss ii ff) expand_cc_h_impl(add_XX ss ii cc) expand_cc_h_impl(add_const_XX bb ss ii ff cc) expand_cc_h_impl(add_const_vXX bb ss ii ff cc) diff --git a/gr-blocks/lib/abs_XX_impl.cc.t b/gr-blocks/lib/abs_XX_impl.cc.t new file mode 100644 index 0000000000..2a701add19 --- /dev/null +++ b/gr-blocks/lib/abs_XX_impl.cc.t @@ -0,0 +1,62 @@ +/* -*- c++ -*- */ +/* + * Copyright 2012 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. + */ + +// @WARNING@ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <@NAME_IMPL@.h> +#include <gnuradio/io_signature.h> + +namespace gr { + namespace blocks { + + @NAME@::sptr @NAME@::make() + { + return gnuradio::get_initial_sptr(new @NAME_IMPL@()); + } + + @NAME_IMPL@::@NAME_IMPL@() + : sync_block ("@NAME@", + io_signature::make (1, 1, sizeof (@I_TYPE@)), + io_signature::make (1, 1, sizeof (@O_TYPE@))) + { + } + + int + @NAME_IMPL@::work(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items) + { + @I_TYPE@ *iptr = (@I_TYPE@ *) input_items[0]; + @O_TYPE@ *optr = (@O_TYPE@ *) output_items[0]; + + for(int i=0; i<noutput_items; i++) + optr[i] = abs(iptr[i]); + + return noutput_items; + } + + } /* namespace blocks */ +} /* namespace gr */ diff --git a/gr-blocks/lib/abs_XX_impl.h.t b/gr-blocks/lib/abs_XX_impl.h.t new file mode 100644 index 0000000000..b31ef5fb31 --- /dev/null +++ b/gr-blocks/lib/abs_XX_impl.h.t @@ -0,0 +1,46 @@ +/* -*- c++ -*- */ +/* + * Copyright 2012 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. + */ + +// @WARNING@ + +#ifndef @GUARD_NAME_IMPL@ +#define @GUARD_NAME_IMPL@ + +#include <gnuradio/blocks/@NAME@.h> + +namespace gr { + namespace blocks { + + class BLOCKS_API @NAME_IMPL@ : public @NAME@ + { + public: + @NAME_IMPL@(); + + int work(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items); + }; + + } /* namespace blocks */ +} /* namespace gr */ + +#endif /* @GUARD_NAME_IMPL@ */ diff --git a/gr-blocks/lib/null_sink_impl.cc b/gr-blocks/lib/null_sink_impl.cc index 41adeea0fd..14dd5eff24 100644 --- a/gr-blocks/lib/null_sink_impl.cc +++ b/gr-blocks/lib/null_sink_impl.cc @@ -39,8 +39,8 @@ namespace gr { null_sink_impl::null_sink_impl(size_t sizeof_stream_item) : sync_block("null_sink", - io_signature::make(1, 1, sizeof_stream_item), - io_signature::make(0, 0, 0)) + io_signature::make(1, -1, sizeof_stream_item), + io_signature::make(0, 0, 0)) { } diff --git a/gr-blocks/lib/null_source_impl.cc b/gr-blocks/lib/null_source_impl.cc index edf0104da1..9550dd6bce 100644 --- a/gr-blocks/lib/null_source_impl.cc +++ b/gr-blocks/lib/null_source_impl.cc @@ -41,7 +41,7 @@ namespace gr { null_source_impl::null_source_impl (size_t sizeof_stream_item) : sync_block("null_source", io_signature::make(0, 0, 0), - io_signature::make(1, 1, sizeof_stream_item)) + io_signature::make(1, -1, sizeof_stream_item)) { } @@ -54,8 +54,11 @@ namespace gr { gr_vector_const_void_star &input_items, gr_vector_void_star &output_items) { - void *optr = (void*)output_items[0]; - memset(optr, 0, noutput_items * output_signature()->sizeof_stream_item(0)); + void *optr; + for(size_t n = 0; n < input_items.size(); n++) { + optr = (void*)output_items[n]; + memset(optr, 0, noutput_items * output_signature()->sizeof_stream_item(n)); + } return noutput_items; } diff --git a/gr-blocks/lib/tagged_stream_to_pdu_impl.cc b/gr-blocks/lib/tagged_stream_to_pdu_impl.cc index da8940359a..04871aef8f 100644 --- a/gr-blocks/lib/tagged_stream_to_pdu_impl.cc +++ b/gr-blocks/lib/tagged_stream_to_pdu_impl.cc @@ -38,100 +38,43 @@ namespace gr { } tagged_stream_to_pdu_impl::tagged_stream_to_pdu_impl(pdu::vector_type type, const std::string& lengthtagname) - : sync_block("tagged_stream_to_pdu", + : tagged_stream_block("tagged_stream_to_pdu", io_signature::make(1, 1, pdu::itemsize(type)), - io_signature::make(0, 0, 0)), - d_itemsize(pdu::itemsize(type)), - d_inpdu(false), + io_signature::make(0, 0, 0), lengthtagname), d_type(type), d_pdu_meta(pmt::PMT_NIL), - d_pdu_vector(pmt::PMT_NIL), - d_tag(pmt::mp(lengthtagname)) + d_pdu_vector(pmt::PMT_NIL) { message_port_register_out(PDU_PORT_ID); } int - tagged_stream_to_pdu_impl::work(int noutput_items, - gr_vector_const_void_star &input_items, - gr_vector_void_star &output_items) + tagged_stream_to_pdu_impl::work (int noutput_items, + gr_vector_int &ninput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items) { const uint8_t *in = (const uint8_t*) input_items[0]; - uint64_t abs_N = nitems_read(0); - // if we are not in a pdu already, start a new one - if (!d_inpdu) { - bool found_length_tag(false); - - get_tags_in_range(d_tags, 0, abs_N, abs_N+1); - - for (d_tags_itr = d_tags.begin(); (d_tags_itr != d_tags.end()) && (!found_length_tag); d_tags_itr++) { - if (pmt::eq((*d_tags_itr).key, d_tag)) { - - if ((*d_tags_itr).offset != abs_N ) - throw std::runtime_error("expected next pdu length tag on a different item..."); - - found_length_tag = true; - d_pdu_length = pmt::to_long((*d_tags_itr).value); - d_pdu_remain = d_pdu_length; - d_pdu_meta = pmt::make_dict(); - break; - } // if have length tag - } // iter over tags - - if (!found_length_tag) - throw std::runtime_error("tagged stream does not contain a pdu_length tag"); + // Grab tags, throw them into dict + get_tags_in_range(d_tags, 0, + nitems_read(0), + nitems_read(0) + ninput_items[0] + ); + d_pdu_meta = pmt::make_dict(); + for (d_tags_itr = d_tags.begin(); d_tags_itr != d_tags.end(); d_tags_itr++) { + d_pdu_meta = dict_add(d_pdu_meta, (*d_tags_itr).key, (*d_tags_itr).value); } - size_t ncopy = std::min((size_t)noutput_items, d_pdu_remain); - - // copy any tags in this range into our meta object - get_tags_in_range(d_tags, 0, abs_N, abs_N+ncopy); - for (d_tags_itr = d_tags.begin(); d_tags_itr != d_tags.end(); d_tags_itr++) - if(!pmt::eq((*d_tags_itr).key, d_tag )) - d_pdu_meta = dict_add(d_pdu_meta, (*d_tags_itr).key, (*d_tags_itr).value); + // Grab data, throw into vector + d_pdu_vector = pdu::make_pdu_vector(d_type, in, ninput_items[0]); - // copy samples for this vector into either a pmt or our save buffer - if (ncopy == d_pdu_remain) { // we will send this pdu - if (d_save.size() == 0) { - d_pdu_vector = pdu::make_pdu_vector(d_type, in, ncopy); - send_message(); - } - else { - size_t oldsize = d_save.size()/d_itemsize; - d_save.resize((oldsize + ncopy)*d_itemsize, 0); - memcpy(&d_save[oldsize*d_itemsize], in, ncopy*d_itemsize); - d_pdu_vector = pdu::make_pdu_vector(d_type, &d_save[0], d_pdu_length); - send_message(); - d_save.clear(); - } - } - else { - size_t oldsize = d_save.size()/d_itemsize; - d_inpdu = true; - d_save.resize((oldsize+ncopy)*d_itemsize); - memcpy(&d_save[oldsize*d_itemsize], in, ncopy*d_itemsize); - d_pdu_remain -= ncopy; - } - - return ncopy; - } - - void - tagged_stream_to_pdu_impl::send_message() - { - if (pmt::length(d_pdu_vector) != d_pdu_length) - throw std::runtime_error("msg length not correct"); - + // Send msg pmt::pmt_t msg = pmt::cons(d_pdu_meta, d_pdu_vector); message_port_pub(PDU_PORT_ID, msg); - - d_pdu_meta = pmt::PMT_NIL; - d_pdu_vector = pmt::PMT_NIL; - d_pdu_length = 0; - d_pdu_remain = 0; - d_inpdu = false; + + return ninput_items[0]; } - + } /* namespace blocks */ } /* namespace gr */ diff --git a/gr-blocks/lib/tagged_stream_to_pdu_impl.h b/gr-blocks/lib/tagged_stream_to_pdu_impl.h index 9620778a91..0a83a87beb 100644 --- a/gr-blocks/lib/tagged_stream_to_pdu_impl.h +++ b/gr-blocks/lib/tagged_stream_to_pdu_impl.h @@ -30,27 +30,20 @@ namespace gr { class BLOCKS_API tagged_stream_to_pdu_impl : public tagged_stream_to_pdu { - size_t d_itemsize; - size_t d_pdu_length; - size_t d_pdu_remain; - bool d_inpdu; pdu::vector_type d_type; - std::vector<uint8_t> d_save; pmt::pmt_t d_pdu_meta; pmt::pmt_t d_pdu_vector; - - pmt::pmt_t d_tag; std::vector<tag_t>::iterator d_tags_itr; - std::vector<tag_t> d_tags; - + std::vector<tag_t> d_tags; + public: tagged_stream_to_pdu_impl(pdu::vector_type type, const std::string& lengthtagname); int work(int noutput_items, + gr_vector_int &ninput_items, gr_vector_const_void_star &input_items, gr_vector_void_star &output_items); - void send_message(); }; } /* namespace blocks */ diff --git a/gr-blocks/python/blocks/qa_pdu.py b/gr-blocks/python/blocks/qa_pdu.py index 0bd838b9c0..81cd8cf71c 100755 --- a/gr-blocks/python/blocks/qa_pdu.py +++ b/gr-blocks/python/blocks/qa_pdu.py @@ -86,27 +86,58 @@ class test_pdu(gr_unittest.TestCase): actual_data = 16*[0xFF,] self.assertEqual(actual_data, list(result_data)) self.assertEqual(actual_data, msg_data) - + def test_001(self): #Test the overflow buffer in pdu_to_tagged_stream src_data = [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0] src = blocks.pdu_to_tagged_stream(blocks.float_t) snk = blocks.vector_sink_f() - + self.tb.connect(src, snk) port = pmt.intern("pdus") - + msg = pmt.cons( pmt.PMT_NIL, pmt.init_f32vector(10, src_data)) src.to_basic_block()._post(port, msg) - + src.set_max_noutput_items(5) - + self.tb.start() #ideally, would wait until we get ten samples time.sleep(0.2) self.tb.stop() - + self.assertEqual(src_data, list(snk.data()) ) + + def test_002_tags_plus_data(self): + packet_len = 16 + src_data = range(packet_len) + tag1 = gr.tag_t() + tag1.offset = 0 + tag1.key = pmt.string_to_symbol('spam') + tag1.value = pmt.from_long(23) + tag2 = gr.tag_t() + tag2.offset = 10 # Must be < packet_len + tag2.key = pmt.string_to_symbol('eggs') + tag2.value = pmt.from_long(42) + src = blocks.vector_source_f(src_data, tags=(tag1, tag2)) + s2ts = blocks.stream_to_tagged_stream(gr.sizeof_float, vlen=1, packet_len=packet_len, len_tag_key="packet_len") + ts2pdu = blocks.tagged_stream_to_pdu(blocks.float_t, "packet_len") + dbg = blocks.message_debug() + self.tb.connect(src, s2ts, ts2pdu) + self.tb.msg_connect(ts2pdu, "pdus", dbg, "store") + self.tb.start() + while dbg.num_messages() < 1: + time.sleep(0.1) + self.tb.stop() + self.tb.wait() + result_msg = dbg.get_message(0) + metadata = pmt.to_python(pmt.car(result_msg)) + vector = pmt.f32vector_elements(pmt.cdr(result_msg)) + self.assertEqual(metadata, {'eggs': 42, 'spam': 23}) + self.assertFloatTuplesAlmostEqual(tuple(vector), src_data) + + if __name__ == '__main__': gr_unittest.run(test_pdu, "test_pdu.xml") + diff --git a/gr-blocks/swig/blocks_swig2.i b/gr-blocks/swig/blocks_swig2.i index f22af806e6..fddeab401d 100644 --- a/gr-blocks/swig/blocks_swig2.i +++ b/gr-blocks/swig/blocks_swig2.i @@ -31,6 +31,9 @@ %include "feval.i" %{ +#include "gnuradio/blocks/abs_ff.h" +#include "gnuradio/blocks/abs_ss.h" +#include "gnuradio/blocks/abs_ii.h" #include "gnuradio/blocks/add_ff.h" #include "gnuradio/blocks/add_ss.h" #include "gnuradio/blocks/add_ii.h" @@ -74,6 +77,9 @@ #include "gnuradio/blocks/divide_cc.h" %} +%include "gnuradio/blocks/abs_ff.h" +%include "gnuradio/blocks/abs_ss.h" +%include "gnuradio/blocks/abs_ii.h" %include "gnuradio/blocks/add_ff.h" %include "gnuradio/blocks/add_ss.h" %include "gnuradio/blocks/add_ii.h" @@ -116,6 +122,9 @@ %include "gnuradio/blocks/divide_ii.h" %include "gnuradio/blocks/divide_cc.h" +GR_SWIG_BLOCK_MAGIC2(blocks, abs_ff); +GR_SWIG_BLOCK_MAGIC2(blocks, abs_ss); +GR_SWIG_BLOCK_MAGIC2(blocks, abs_ii); GR_SWIG_BLOCK_MAGIC2(blocks, add_ff); GR_SWIG_BLOCK_MAGIC2(blocks, add_ss); GR_SWIG_BLOCK_MAGIC2(blocks, add_ii); diff --git a/gr-filter/examples/reconstruction.py b/gr-filter/examples/reconstruction.py index fd8ba87a2f..0a83b5a4e0 100755 --- a/gr-filter/examples/reconstruction.py +++ b/gr-filter/examples/reconstruction.py @@ -72,7 +72,7 @@ def main(): src = blocks.vector_source_b(data.astype(scipy.uint8).tolist(), False) mod = digital.bpsk_mod(samples_per_symbol=2) - chan = filter.channel_model(npwr) + chan = channels.channel_model(npwr) rrc = filter.fft_filter_ccc(1, rrc_taps) # Split it up into pieces @@ -122,7 +122,7 @@ def main(): s12.set_title("Original Signal in Time") start = 1 - skip = 4 + skip = 2 s13 = f1.add_subplot(2,2,3) s13.plot(sin.real[start::skip], sin.imag[start::skip], "o") s13.set_title("Constellation") @@ -153,7 +153,7 @@ def main(): s32.plot(sout.imag[1000:1500], "o-r") s32.set_title("Reconstructed Signal in Time") - start = 2 + start = 0 skip = 4 s33 = f3.add_subplot(2,2,3) s33.plot(sout.real[start::skip], sout.imag[start::skip], "o") diff --git a/gr-filter/grc/filter_fft_filter_xxx.xml b/gr-filter/grc/filter_fft_filter_xxx.xml index 99b02cbab8..f843af2024 100644 --- a/gr-filter/grc/filter_fft_filter_xxx.xml +++ b/gr-filter/grc/filter_fft_filter_xxx.xml @@ -26,11 +26,18 @@ self.$(id).declare_sample_delay($samp_delay) <opt>taps:complex_vector</opt> </option> <option> + <name>Complex->Complex (Real Taps)</name> + <key>ccf</key> + <opt>input:complex</opt> + <opt>output:complex</opt> + <opt>taps:float_vector</opt> + </option> + <option> <name>Float->Float (Real Taps)</name> <key>fff</key> <opt>input:float</opt> <opt>output:float</opt> - <opt>taps:real_vector</opt> + <opt>taps:float_vector</opt> </option> </param> <param> diff --git a/gr-filter/grc/filter_pfb_channelizer.xml b/gr-filter/grc/filter_pfb_channelizer.xml index 26349cd10d..f8a51d2204 100644 --- a/gr-filter/grc/filter_pfb_channelizer.xml +++ b/gr-filter/grc/filter_pfb_channelizer.xml @@ -16,14 +16,13 @@ $atten) self.$(id).set_channel_map($ch_map) </make> - <!-- Set taps not implemented yet - <callback>set_taps($taps)</callback> - --> + <callback>set_taps($taps)</callback> <callback>set_channel_map($ch_map)</callback> <param> <name>Channels</name> <key>nchans</key> + <value>1</value> <type>int</type> </param> <param> @@ -50,6 +49,13 @@ self.$(id).set_channel_map($ch_map) <value>[]</value> <type>int_vector</type> </param> + <param> + <name>Bus Connections</name> + <key>bus_conns</key> + <value>[[0,],]</value> + <type>raw</type> + <hide>part</hide> + </param> <sink> <name>in</name> <type>complex</type> @@ -59,4 +65,5 @@ self.$(id).set_channel_map($ch_map) <type>complex</type> <nports>$nchans</nports> </source> + <bus_structure_source>$bus_conns</bus_structure_source> </block> diff --git a/gr-filter/grc/filter_pfb_decimator.xml b/gr-filter/grc/filter_pfb_decimator.xml index d57119636c..3a828762a0 100644 --- a/gr-filter/grc/filter_pfb_decimator.xml +++ b/gr-filter/grc/filter_pfb_decimator.xml @@ -13,7 +13,9 @@ $decim, $taps, $channel, - $atten) + $atten, + $fft_rot, + $fft_filts) </make> <callback>set_taps($taps)</callback> <callback>set_channel(int($channel))</callback> @@ -40,6 +42,36 @@ <value>100</value> <type>real</type> </param> + <param> + <name>Use FFT Rotator</name> + <key>fft_rot</key> + <value>True</value> + <type>raw</type> + <hide>part</hide> + <option> + <name>True</name> + <key>True</key> + </option> + <option> + <name>False</name> + <key>False</key> + </option> + </param> + <param> + <name>Use FFT Filters</name> + <key>fft_filts</key> + <value>True</value> + <type>raw</type> + <hide>part</hide> + <option> + <name>True</name> + <key>True</key> + </option> + <option> + <name>False</name> + <key>False</key> + </option> + </param> <sink> <name>in</name> <type>complex</type> diff --git a/gr-filter/grc/filter_pfb_synthesizer.xml b/gr-filter/grc/filter_pfb_synthesizer.xml index e84b25e62e..e7e1ae3951 100644 --- a/gr-filter/grc/filter_pfb_synthesizer.xml +++ b/gr-filter/grc/filter_pfb_synthesizer.xml @@ -45,6 +45,13 @@ self.$(id).set_channel_map($ch_map) <value>[]</value> <type>int_vector</type> </param> + <param> + <name>Bus Connections</name> + <key>bus_conns</key> + <value>[[0,],]</value> + <type>raw</type> + <hide>part</hide> + </param> <sink> <name>in</name> <type>complex</type> @@ -54,4 +61,5 @@ self.$(id).set_channel_map($ch_map) <name>out</name> <type>complex</type> </source> + <bus_structure_sink>$bus_conns</bus_structure_sink> </block> diff --git a/gr-filter/include/gnuradio/filter/CMakeLists.txt b/gr-filter/include/gnuradio/filter/CMakeLists.txt index d039af9b00..9dbc227887 100644 --- a/gr-filter/include/gnuradio/filter/CMakeLists.txt +++ b/gr-filter/include/gnuradio/filter/CMakeLists.txt @@ -94,6 +94,7 @@ install(FILES dc_blocker_ff.h filter_delay_fc.h fft_filter_ccc.h + fft_filter_ccf.h fft_filter_fff.h fractional_interpolator_cc.h fractional_interpolator_ff.h diff --git a/gr-filter/include/gnuradio/filter/fft_filter.h b/gr-filter/include/gnuradio/filter/fft_filter.h index 76271d9abf..24f6ce3134 100644 --- a/gr-filter/include/gnuradio/filter/fft_filter.h +++ b/gr-filter/include/gnuradio/filter/fft_filter.h @@ -1,6 +1,6 @@ /* -*- c++ -*- */ /* - * Copyright 2010,2012 Free Software Foundation, Inc. + * Copyright 2010,2012,2014 Free Software Foundation, Inc. * * This file is part of GNU Radio * @@ -49,7 +49,7 @@ namespace gr { std::vector<float> d_tail; // state carried between blocks for overlap-add std::vector<float> d_taps; // stores time domain taps gr_complex *d_xformed_taps; // Fourier xformed taps - + void compute_sizes(int ntaps); int tailsize() const { return d_ntaps - 1; } @@ -77,7 +77,7 @@ namespace gr { * \param taps The filter taps (complex) */ int set_taps(const std::vector<float> &taps); - + /*! * \brief Set number of threads to use. */ @@ -92,12 +92,12 @@ namespace gr { * \brief Returns the number of taps in the filter. */ unsigned int ntaps() const; - + /*! * \brief Get number of threads being used. */ int nthreads() const; - + /*! l * \brief Perform the filter operation * @@ -108,7 +108,7 @@ l * \brief Perform the filter operation int filter(int nitems, const float *input, float *output); }; - + /*! * \brief Fast FFT filter with gr_complex input, gr_complex output and gr_complex taps * \ingroup filter_blk @@ -126,7 +126,7 @@ l * \brief Perform the filter operation std::vector<gr_complex> d_tail; // state carried between blocks for overlap-add std::vector<gr_complex> d_taps; // stores time domain taps gr_complex *d_xformed_taps; // Fourier xformed taps - + void compute_sizes(int ntaps); int tailsize() const { return d_ntaps - 1; } @@ -154,12 +154,12 @@ l * \brief Perform the filter operation * \param taps The filter taps (complex) */ int set_taps(const std::vector<gr_complex> &taps); - + /*! * \brief Set number of threads to use. */ void set_nthreads(int n); - + /*! * \brief Returns the taps. */ @@ -169,12 +169,100 @@ l * \brief Perform the filter operation * \brief Returns the number of taps in the filter. */ unsigned int ntaps() const; - + + /*! + * \brief Get number of threads being used. + */ + int nthreads() const; + + /*! + * \brief Perform the filter operation + * + * \param nitems The number of items to produce + * \param input The input vector to be filtered + * \param output The result of the filter operation + */ + int filter(int nitems, const gr_complex *input, gr_complex *output); + }; + + + + /*! + * \brief Fast FFT filter with gr_complex input, gr_complex output and float taps + * \ingroup filter_blk + */ + class FILTER_API fft_filter_ccf + { + private: + int d_ntaps; + int d_nsamples; + int d_fftsize; // fftsize = ntaps + nsamples - 1 + int d_decimation; + fft::fft_complex *d_fwdfft; // forward "plan" + fft::fft_complex *d_invfft; // inverse "plan" + int d_nthreads; // number of FFTW threads to use + std::vector<gr_complex> d_tail; // state carried between blocks for overlap-add + std::vector<float> d_taps; // stores time domain taps + gr_complex *d_xformed_taps; // Fourier xformed taps + + void compute_sizes(int ntaps); + int tailsize() const { return d_ntaps - 1; } + + public: + /*! + * \brief Construct an FFT filter for complex vectors with the given taps and decimation rate. + * + * This is the basic implementation for performing FFT filter for fast convolution + * in other blocks for complex vectors (such as fft_filter_ccf). + * + * \param decimation The decimation rate of the filter (int) + * \param taps The filter taps (complex) + * \param nthreads The number of threads for the FFT to use (int) + */ + fft_filter_ccf(int decimation, + const std::vector<float> &taps, + int nthreads=1); + + ~fft_filter_ccf(); + + /*! + * \brief Set new taps for the filter. + * + * Sets new taps and resets the class properties to handle different sizes + * \param taps The filter taps (complex) + */ + int set_taps(const std::vector<float> &taps); + + /*! + * \brief Set number of threads to use. + */ + void set_nthreads(int n); + + /*! + * \brief Returns the taps. + */ + std::vector<float> taps() const; + + /*! + * \brief Returns the number of taps in the filter. + */ + unsigned int ntaps() const; + + /*! + * \brief Returns the actual size of the filter. + * + * \details This value could be equal to ntaps, but we ofter + * build a longer filter to allow us to calculate a more + * efficient FFT. This value is the actual size of the filters + * used in the calculation of the overlap-and-save operation. + */ + unsigned int filtersize() const; + /*! * \brief Get number of threads being used. */ int nthreads() const; - + /*! * \brief Perform the filter operation * diff --git a/gr-filter/include/gnuradio/filter/fft_filter_ccf.h b/gr-filter/include/gnuradio/filter/fft_filter_ccf.h new file mode 100644 index 0000000000..b0230f8263 --- /dev/null +++ b/gr-filter/include/gnuradio/filter/fft_filter_ccf.h @@ -0,0 +1,89 @@ +/* -*- c++ -*- */ +/* + * Copyright 2014 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_FILTER_FFT_FILTER_CCF_H +#define INCLUDED_FILTER_FFT_FILTER_CCF_H + +#include <gnuradio/filter/api.h> +#include <gnuradio/sync_decimator.h> + +namespace gr { + namespace filter { + + /*! + * \brief Fast FFT filter with gr_complex input, gr_complex output and float taps + * \ingroup filter_blk + * + * \details + * This block implements a complex decimating filter using the + * fast convolution method via an FFT. The decimation factor is an + * interger that is greater than or equal to 1. + * + * The filter takes a set of complex (or real) taps to use in the + * filtering operation. These taps can be defined as anything that + * satisfies the user's filtering needs. For standard filters such + * as lowpass, highpass, bandpass, etc., the filter.firdes and + * filter.optfir classes provide convenient generating methods. + * + * This filter is implemented by using the FFTW package to perform + * the required FFTs. An optional argument, nthreads, may be + * passed to the constructor (or set using the set_nthreads member + * function) to split the FFT among N number of threads. This can + * improve performance on very large FFTs (that is, if the number + * of taps used is very large) if you have enough threads/cores to + * support it. + */ + class FILTER_API fft_filter_ccf : virtual public sync_decimator + { + public: + // gr::filter::fft_filter_ccf::sptr + typedef boost::shared_ptr<fft_filter_ccf> sptr; + + /*! + * Build an FFT filter blocks. + * + * \param decimation >= 1 + * \param taps complex filter taps + * \param nthreads number of threads for the FFT to use + */ + static sptr make(int decimation, + const std::vector<float> &taps, + int nthreads=1); + + virtual void set_taps(const std::vector<float> &taps) = 0; + virtual std::vector<float> taps() const = 0; + + /*! + * \brief Set number of threads to use. + */ + virtual void set_nthreads(int n) = 0; + + /*! + * \brief Get number of threads being used. + */ + virtual int nthreads() const = 0; + }; + + } /* namespace filter */ +} /* namespace gr */ + +#endif /* INCLUDED_FILTER_FFT_FILTER_CCF_H */ diff --git a/gr-filter/include/gnuradio/filter/pfb_channelizer_ccf.h b/gr-filter/include/gnuradio/filter/pfb_channelizer_ccf.h index f199f85b83..96ffd60daa 100644 --- a/gr-filter/include/gnuradio/filter/pfb_channelizer_ccf.h +++ b/gr-filter/include/gnuradio/filter/pfb_channelizer_ccf.h @@ -101,8 +101,14 @@ namespace gr { * <B><EM>f. harris, "Multirate Signal Processing for Communication * Systems," Upper Saddle River, NJ: Prentice Hall, Inc. 2004.</EM></B> * + * When dealing with oversampling, the above book is still a good + * reference along with this paper: + * + * <B><EM>E. Venosa, X. Chen, and fred harris, “Polyphase analysis + * filter bank down-converts unequal channel bandwidths with + * arbitrary center frequencies - design I,” in SDR’10-WinnComm, + * 2010.</EM></B> */ - class FILTER_API pfb_channelizer_ccf : virtual public block { public: @@ -151,7 +157,7 @@ namespace gr { * Print all of the filterbank taps to screen. */ virtual void print_taps() = 0; - + /*! * Return a vector<vector<>> of the filterbank taps */ @@ -189,7 +195,7 @@ namespace gr { * the map is [0...M-1] with M = N. */ virtual void set_channel_map(const std::vector<int> &map) = 0; - + /*! * Gets the current channel map. */ diff --git a/gr-filter/include/gnuradio/filter/pfb_decimator_ccf.h b/gr-filter/include/gnuradio/filter/pfb_decimator_ccf.h index da4eb2bd34..06f329a03e 100644 --- a/gr-filter/include/gnuradio/filter/pfb_decimator_ccf.h +++ b/gr-filter/include/gnuradio/filter/pfb_decimator_ccf.h @@ -99,10 +99,19 @@ namespace gr { * \param decim (unsigned integer) Specifies the decimation rate to use * \param taps (vector/list of floats) The prototype filter to populate the filterbank. * \param channel (unsigned integer) Selects the channel to return [default=0]. + * \param use_fft_rotator (bool) Rotate channels using FFT method instead of exp(phi). + * For larger values of \p channel, the FFT method will perform better. + * Generally, this value of \p channel is small (~5), but could be + * architecture-specific (Default: true). + * \param use_fft_filters (bool) Use FFT filters (fast convolution) instead of FIR filters. + * FFT filters perform better for larger numbers of taps but is + * architecture-specific (Default: true). */ static sptr make(unsigned int decim, - const std::vector<float> &taps, - unsigned int channel); + const std::vector<float> &taps, + unsigned int channel, + bool use_fft_rotator=true, + bool use_fft_filters=true); /*! * Resets the filterbank's filter taps with the new prototype filter diff --git a/gr-filter/include/gnuradio/filter/pfb_synthesizer_ccf.h b/gr-filter/include/gnuradio/filter/pfb_synthesizer_ccf.h index f1fbad7272..32465b61ac 100644 --- a/gr-filter/include/gnuradio/filter/pfb_synthesizer_ccf.h +++ b/gr-filter/include/gnuradio/filter/pfb_synthesizer_ccf.h @@ -34,6 +34,57 @@ namespace gr { * \brief Polyphase synthesis filterbank with * gr_complex input, gr_complex output and float taps * \ingroup channelizers_blk + * + * \details + * + * The PFB sythesis filterbank combines multiple baseband signals + * into a single channelized signal. Each input stream is, + * essentially, modulated onto an output channel according the the + * channel mapping (see set_channel_map for details). + * + * Setting this filterbank up means selecting the number of output + * channels, the prototype filter, and whether to handle channels + * at 2x the sample rate (this is generally used only for + * reconstruction filtering). + * + * The number of channels sets the maximum number of channels to + * use, but not all input streams must be connected. For M total + * channels, we can connect inputs 0 to N where N < M-1. Because + * of the way GNU Radio handles stream connections, we must + * connect the channels consecutively, and so we must use the + * set_channel_map if the desired output channels are not the same + * as the the default mapping. This features gives us the + * flexibility to output to any given channel. Generally, we try + * to not use the channels at the edge of the spectrum to avoid + * issues with filtering and roll-off of the transmitter or + * receiver. + * + * When using the 2x sample rate mode, we specify the number of + * channels that will be used. However, the actual output signal + * will be twice this number of channels. This is mainly important + * to know when setting the channel map. For M channels, the + * channel mapping can specy from 0 to 2M-1 channels to output + * onto. + * + * For more details about this and the concepts of reconstruction + * filtering, see: + * + * <B><EM>f. harris, "Multirate Signal Processing for Communication + * Systems," Upper Saddle River, NJ: Prentice Hall, Inc. 2004.</EM></B> + * + * <B><EM>X. Chen, E. Venosa, and fred harris, “Polyphase analysis + * filter bank up-converts unequal channel bandwidths with + * arbitrary center frequencies - design ii,” in SDR’10-WinnComm, + * 2010.</EM></B> + * + * <B><EM>E. Venosa, X. Chen, and fred harris, “Polyphase analysis + * filter bank down-converts unequal channel bandwidths with + * arbitrary center frequencies - design I,” in SDR’10-WinnComm, + * 2010.</EM></B> + * + * <B><EM>f. j. harris, C. Dick, X. Chen, and E. Venosa, “Wideband 160- + * channel polyphase filter bank cable TV channeliser,” in IET + * Signal Processing, 2010.</EM></B> */ class FILTER_API pfb_synthesizer_ccf : virtual public sync_interpolator { @@ -50,8 +101,8 @@ namespace gr { * \param twox (bool) use 2x oversampling or not (default is no) */ static sptr make(unsigned int numchans, - const std::vector<float> &taps, - bool twox=false); + const std::vector<float> &taps, + bool twox=false); /*! * Resets the filterbank's filter taps with the new prototype filter diff --git a/gr-filter/include/gnuradio/filter/polyphase_filterbank.h b/gr-filter/include/gnuradio/filter/polyphase_filterbank.h index fad04e3294..1745a470ab 100644 --- a/gr-filter/include/gnuradio/filter/polyphase_filterbank.h +++ b/gr-filter/include/gnuradio/filter/polyphase_filterbank.h @@ -26,6 +26,7 @@ #include <gnuradio/filter/api.h> #include <gnuradio/filter/fir_filter.h> +#include <gnuradio/filter/fft_filter.h> #include <gnuradio/fft/fft.h> namespace gr { @@ -101,11 +102,12 @@ namespace gr { { protected: unsigned int d_nfilts; - std::vector<kernel::fir_filter_ccf*> d_filters; + std::vector<kernel::fir_filter_ccf*> d_fir_filters; + std::vector<kernel::fft_filter_ccf*> d_fft_filters; std::vector< std::vector<float> > d_taps; unsigned int d_taps_per_filter; fft::fft_complex *d_fft; - + public: /*! * Build the polyphase filterbank decimator. @@ -113,9 +115,11 @@ namespace gr { * channels <EM>M</EM> * \param taps (vector/list of floats) The prototype filter to * populate the filterbank. + * \param fft_forward (bool) use a forward or inverse FFT (default=false). */ polyphase_filterbank(unsigned int nfilts, - const std::vector<float> &taps); + const std::vector<float> &taps, + bool fft_forward=false); ~polyphase_filterbank(); @@ -126,7 +130,7 @@ namespace gr { * \param taps (vector/list of floats) The prototype filter to * populate the filterbank. */ - void set_taps(const std::vector<float> &taps); + virtual void set_taps(const std::vector<float> &taps); /*! * Print all of the filterbank taps to screen. diff --git a/gr-filter/lib/CMakeLists.txt b/gr-filter/lib/CMakeLists.txt index 9f17092bb1..9889a4ddc1 100644 --- a/gr-filter/lib/CMakeLists.txt +++ b/gr-filter/lib/CMakeLists.txt @@ -128,6 +128,7 @@ list(APPEND filter_sources dc_blocker_ff_impl.cc filter_delay_fc_impl.cc fft_filter_ccc_impl.cc + fft_filter_ccf_impl.cc fft_filter_fff_impl.cc fractional_interpolator_cc_impl.cc fractional_interpolator_ff_impl.cc diff --git a/gr-filter/lib/fft_filter.cc b/gr-filter/lib/fft_filter.cc index 94f0a8894b..433b1a4d97 100644 --- a/gr-filter/lib/fft_filter.cc +++ b/gr-filter/lib/fft_filter.cc @@ -1,6 +1,6 @@ /* -*- c++ -*- */ /* - * Copyright 2010,2012 Free Software Foundation, Inc. + * Copyright 2010,2012,2014 Free Software Foundation, Inc. * * This file is part of GNU Radio * @@ -345,6 +345,170 @@ namespace gr { return nitems; } + + /**************************************************************/ + + + fft_filter_ccf::fft_filter_ccf(int decimation, + const std::vector<float> &taps, + int nthreads) + : d_fftsize(-1), d_decimation(decimation), d_fwdfft(NULL), + d_invfft(NULL), d_nthreads(nthreads), d_xformed_taps(NULL) + { + set_taps(taps); + } + + fft_filter_ccf::~fft_filter_ccf() + { + delete d_fwdfft; + delete d_invfft; + if(d_xformed_taps != NULL) + volk_free(d_xformed_taps); + } + + /* + * determines d_ntaps, d_nsamples, d_fftsize, d_xformed_taps + */ + int + fft_filter_ccf::set_taps(const std::vector<float> &taps) + { + int i = 0; + d_taps = taps; + compute_sizes(taps.size()); + + d_tail.resize(tailsize()); + for(i = 0; i < tailsize(); i++) + d_tail[i] = 0; + + gr_complex *in = d_fwdfft->get_inbuf(); + gr_complex *out = d_fwdfft->get_outbuf(); + + float scale = 1.0 / d_fftsize; + + // Compute forward xform of taps. + // Copy taps into first ntaps slots, then pad with zeros + for(i = 0; i < d_ntaps; i++) + in[i] = gr_complex(taps[i] * scale, 0.0f); + + for(; i < d_fftsize; i++) + in[i] = gr_complex(0.0f, 0.0f); + + d_fwdfft->execute(); // do the xform + + // now copy output to d_xformed_taps + for(i = 0; i < d_fftsize; i++) + d_xformed_taps[i] = out[i]; + + return d_nsamples; + } + + // determine and set d_ntaps, d_nsamples, d_fftsize + void + fft_filter_ccf::compute_sizes(int ntaps) + { + int old_fftsize = d_fftsize; + d_ntaps = ntaps; + d_fftsize = (int) (2 * pow(2.0, ceil(log(double(ntaps)) / log(2.0)))); + d_nsamples = d_fftsize - d_ntaps + 1; + + if(VERBOSE) { + std::cerr << "fft_filter_ccf: ntaps = " << d_ntaps + << " fftsize = " << d_fftsize + << " nsamples = " << d_nsamples << std::endl; + } + + // compute new plans + if(d_fftsize != old_fftsize) { + delete d_fwdfft; + delete d_invfft; + if(d_xformed_taps != NULL) + volk_free(d_xformed_taps); + d_fwdfft = new fft::fft_complex(d_fftsize, true, d_nthreads); + d_invfft = new fft::fft_complex(d_fftsize, false, d_nthreads); + d_xformed_taps = (gr_complex*)volk_malloc(sizeof(gr_complex)*d_fftsize, + volk_get_alignment()); + } + } + + void + fft_filter_ccf::set_nthreads(int n) + { + d_nthreads = n; + if(d_fwdfft) + d_fwdfft->set_nthreads(n); + if(d_invfft) + d_invfft->set_nthreads(n); + } + + std::vector<float> + fft_filter_ccf::taps() const + { + return d_taps; + } + + unsigned int + fft_filter_ccf::ntaps() const + { + return d_ntaps; + } + + unsigned int + fft_filter_ccf::filtersize() const + { + return d_fftsize; + } + + int + fft_filter_ccf::nthreads() const + { + return d_nthreads; + } + + int + fft_filter_ccf::filter(int nitems, const gr_complex *input, gr_complex *output) + { + int dec_ctr = 0; + int j = 0; + int ninput_items = nitems * d_decimation; + + for(int i = 0; i < ninput_items; i += d_nsamples) { + memcpy(d_fwdfft->get_inbuf(), &input[i], d_nsamples * sizeof(gr_complex)); + + for(j = d_nsamples; j < d_fftsize; j++) + d_fwdfft->get_inbuf()[j] = 0; + + d_fwdfft->execute(); // compute fwd xform + + gr_complex *a = d_fwdfft->get_outbuf(); + gr_complex *b = d_xformed_taps; + gr_complex *c = d_invfft->get_inbuf(); + + volk_32fc_x2_multiply_32fc_a(c, a, b, d_fftsize); + + d_invfft->execute(); // compute inv xform + + // add in the overlapping tail + + for(j = 0; j < tailsize(); j++) + d_invfft->get_outbuf()[j] += d_tail[j]; + + // copy nsamples to output + j = dec_ctr; + while(j < d_nsamples) { + *output++ = d_invfft->get_outbuf()[j]; + j += d_decimation; + } + dec_ctr = (j - d_nsamples); + + // stash the tail + memcpy(&d_tail[0], d_invfft->get_outbuf() + d_nsamples, + tailsize() * sizeof(gr_complex)); + } + + return nitems; + } + + } /* namespace kernel */ } /* namespace filter */ } /* namespace gr */ diff --git a/gr-filter/lib/fft_filter_ccf_impl.cc b/gr-filter/lib/fft_filter_ccf_impl.cc new file mode 100644 index 0000000000..00e1842fff --- /dev/null +++ b/gr-filter/lib/fft_filter_ccf_impl.cc @@ -0,0 +1,120 @@ +/* -*- c++ -*- */ +/* + * Copyright 2014 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 "fft_filter_ccf_impl.h" +#include <gnuradio/io_signature.h> + +#include <math.h> +#include <assert.h> +#include <stdexcept> + +namespace gr { + namespace filter { + + fft_filter_ccf::sptr + fft_filter_ccf::make(int decimation, + const std::vector<float> &taps, + int nthreads) + { + return gnuradio::get_initial_sptr + (new fft_filter_ccf_impl + (decimation, taps, nthreads)); + } + + fft_filter_ccf_impl::fft_filter_ccf_impl(int decimation, + const std::vector<float> &taps, + int nthreads) + : sync_decimator("fft_filter_ccf", + io_signature::make(1, 1, sizeof(gr_complex)), + io_signature::make(1, 1, sizeof(gr_complex)), + decimation), + d_updated(false) + { + set_history(1); + + d_filter = new kernel::fft_filter_ccf(decimation, taps, nthreads); + + d_new_taps = taps; + d_nsamples = d_filter->set_taps(taps); + set_output_multiple(d_nsamples); + } + + fft_filter_ccf_impl::~fft_filter_ccf_impl() + { + delete d_filter; + } + + void + fft_filter_ccf_impl::set_taps(const std::vector<float> &taps) + { + d_new_taps = taps; + d_updated = true; + } + + std::vector<float> + fft_filter_ccf_impl::taps() const + { + return d_new_taps; + } + + void + fft_filter_ccf_impl::set_nthreads(int n) + { + if(d_filter) + d_filter->set_nthreads(n); + } + + int + fft_filter_ccf_impl::nthreads() const + { + if(d_filter) + return d_filter->nthreads(); + else + return 0; + } + + int + fft_filter_ccf_impl::work(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items) + { + const gr_complex *in = (const gr_complex *) input_items[0]; + gr_complex *out = (gr_complex *) output_items[0]; + + if (d_updated){ + d_nsamples = d_filter->set_taps(d_new_taps); + d_updated = false; + set_output_multiple(d_nsamples); + return 0; // output multiple may have changed + } + + d_filter->filter(noutput_items, in, out); + + return noutput_items; + } + + } /* namespace filter */ +} /* namespace gr */ diff --git a/gr-filter/lib/fft_filter_ccf_impl.h b/gr-filter/lib/fft_filter_ccf_impl.h new file mode 100644 index 0000000000..2e0ddd46fb --- /dev/null +++ b/gr-filter/lib/fft_filter_ccf_impl.h @@ -0,0 +1,62 @@ +/* -*- c++ -*- */ +/* + * Copyright 2014 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_FILTER_FFT_FILTER_CCF_IMPL_H +#define INCLUDED_FILTER_FFT_FILTER_CCF_IMPL_H + +#include <gnuradio/filter/api.h> +#include <gnuradio/filter/fft_filter.h> +#include <gnuradio/filter/fft_filter_ccf.h> + +namespace gr { + namespace filter { + + class FILTER_API fft_filter_ccf_impl : public fft_filter_ccf + { + private: + int d_nsamples; + bool d_updated; + kernel::fft_filter_ccf *d_filter; + std::vector<float> d_new_taps; + + public: + fft_filter_ccf_impl(int decimation, + const std::vector<float> &taps, + int nthreads=1); + + ~fft_filter_ccf_impl(); + + void set_taps(const std::vector<float> &taps); + std::vector<float> taps() const; + + void set_nthreads(int n); + int nthreads() const; + + int work(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items); + }; + + } /* namespace filter */ +} /* namespace gr */ + +#endif /* INCLUDED_FILTER_FFT_FILTER_CCF_IMPL_H */ diff --git a/gr-filter/lib/pfb_channelizer_ccf_impl.cc b/gr-filter/lib/pfb_channelizer_ccf_impl.cc index c28434b6cb..62223e40da 100644 --- a/gr-filter/lib/pfb_channelizer_ccf_impl.cc +++ b/gr-filter/lib/pfb_channelizer_ccf_impl.cc @@ -1,6 +1,6 @@ /* -*- c++ -*- */ /* - * Copyright 2009,2010,2012 Free Software Foundation, Inc. + * Copyright 2009,2010,2012,2014 Free Software Foundation, Inc. * * This file is part of GNU Radio * @@ -26,25 +26,28 @@ #include "pfb_channelizer_ccf_impl.h" #include <gnuradio/io_signature.h> +#include <stdio.h> namespace gr { namespace filter { - - pfb_channelizer_ccf::sptr pfb_channelizer_ccf::make(unsigned int nfilts, - const std::vector<float> &taps, - float oversample_rate) + + pfb_channelizer_ccf::sptr + pfb_channelizer_ccf::make(unsigned int nfilts, + const std::vector<float> &taps, + float oversample_rate) { - return gnuradio::get_initial_sptr(new pfb_channelizer_ccf_impl(nfilts, taps, - oversample_rate)); + return gnuradio::get_initial_sptr + (new pfb_channelizer_ccf_impl(nfilts, taps, + oversample_rate)); } pfb_channelizer_ccf_impl::pfb_channelizer_ccf_impl(unsigned int nfilts, const std::vector<float> &taps, float oversample_rate) : block("pfb_channelizer_ccf", - io_signature::make(nfilts, nfilts, sizeof(gr_complex)), - io_signature::make(1, nfilts, sizeof(gr_complex))), - polyphase_filterbank(nfilts, taps), + io_signature::make(nfilts, nfilts, sizeof(gr_complex)), + io_signature::make(1, nfilts, sizeof(gr_complex))), + polyphase_filterbank(nfilts, taps, false), d_updated(false), d_oversample_rate(oversample_rate) { // The over sampling rate must be rationally related to the number of channels @@ -59,15 +62,22 @@ namespace gr { set_relative_rate(1.0/intp); - // Default channel map + // Default channel map. The channel map specifies which input + // goes to which output channel; so out[0] comes from + // channel_map[0]. d_channel_map.resize(d_nfilts); for(unsigned int i = 0; i < d_nfilts; i++) { d_channel_map[i] = i; } - // Although the filters change, we use this look up table - // to set the index of the FFT input buffer, which equivalently - // performs the FFT shift operation on every other turn. + // We use a look up table to set the index of the FFT input + // buffer, which equivalently performs the FFT shift operation + // on every other turn when the rate_ratio>1. Also, this + // performs the index 'flip' where the first input goes into the + // last filter. In the pfb_decimator_ccf, we directly index the + // input_items buffers starting with this last; here we start + // with the first and put it into the fft object properly for + // the same effect. d_rate_ratio = (int)rintf(d_nfilts / d_oversample_rate); d_idxlut = new int[d_nfilts]; for(unsigned int i = 0; i < d_nfilts; i++) { @@ -81,10 +91,8 @@ namespace gr { d_output_multiple++; set_output_multiple(d_output_multiple); - // History is the length of each filter arm plus 1. - // The +1 comes from the channel mapping in the work function - // where we start n=1 so that we can look at in[n-1] - set_history(d_taps_per_filter+1); + // Use set_taps to also set the history requirement + set_taps(taps); } pfb_channelizer_ccf_impl::~pfb_channelizer_ccf_impl() @@ -118,7 +126,7 @@ namespace gr { pfb_channelizer_ccf_impl::set_channel_map(const std::vector<int> &map) { gr::thread::scoped_lock guard(d_mutex); - + if(map.size() > 0) { unsigned int max = (unsigned int)*std::max_element(map.begin(), map.end()); if(max >= d_nfilts) { @@ -142,9 +150,9 @@ namespace gr { { gr::thread::scoped_lock guard(d_mutex); - gr_complex *in = (gr_complex *) input_items[0]; - gr_complex *out = (gr_complex *) output_items[0]; - + gr_complex *in = (gr_complex*)input_items[0]; + gr_complex *out = (gr_complex*)output_items[0]; + if(d_updated) { d_updated = false; return 0; // history requirements may have changed. @@ -152,6 +160,18 @@ namespace gr { size_t noutputs = output_items.size(); + // The following algorithm looks more complex in order to handle + // the cases where we want more that 1 sps for each + // channel. Otherwise, this would boil down into a single loop + // that operates from input_items[0] to [d_nfilts]. + + // When dealing with osps>1, we start not at the last filter, + // but nfilts/osps and then wrap around to the next symbol into + // the other set of filters. + // For details of this operation, see: + // fred harris, Multirate Signal Processing For Communication + // Systems. Upper Saddle River, NJ: Prentice Hall, 2004. + int n=1, i=-1, j=0, oo=0, last; int toconsume = (int)rintf(noutput_items/d_oversample_rate); while(n <= toconsume) { @@ -159,16 +179,16 @@ namespace gr { i = (i + d_rate_ratio) % d_nfilts; last = i; while(i >= 0) { - in = (gr_complex*)input_items[j]; - d_fft->get_inbuf()[d_idxlut[j]] = d_filters[i]->filter(&in[n]); + in = (gr_complex*)input_items[j]; + d_fft->get_inbuf()[d_idxlut[j]] = d_fir_filters[i]->filter(&in[n]); j++; i--; } i = d_nfilts-1; while(i > last) { - in = (gr_complex*)input_items[j]; - d_fft->get_inbuf()[d_idxlut[j]] = d_filters[i]->filter(&in[n-1]); + in = (gr_complex*)input_items[j]; + d_fft->get_inbuf()[d_idxlut[j]] = d_fir_filters[i]->filter(&in[n-1]); j++; i--; } diff --git a/gr-filter/lib/pfb_channelizer_ccf_impl.h b/gr-filter/lib/pfb_channelizer_ccf_impl.h index 6d727676b2..bfc53f8848 100644 --- a/gr-filter/lib/pfb_channelizer_ccf_impl.h +++ b/gr-filter/lib/pfb_channelizer_ccf_impl.h @@ -31,7 +31,7 @@ namespace gr { namespace filter { - + class FILTER_API pfb_channelizer_ccf_impl : public pfb_channelizer_ccf, kernel::polyphase_filterbank { private: diff --git a/gr-filter/lib/pfb_decimator_ccf_impl.cc b/gr-filter/lib/pfb_decimator_ccf_impl.cc index 19cf20f410..7b4bf73b39 100644 --- a/gr-filter/lib/pfb_decimator_ccf_impl.cc +++ b/gr-filter/lib/pfb_decimator_ccf_impl.cc @@ -1,6 +1,6 @@ /* -*- c++ -*- */ /* - * Copyright 2009,2010,2012 Free Software Foundation, Inc. + * Copyright 2009,2010,2012,2014 Free Software Foundation, Inc. * * This file is part of GNU Radio * @@ -26,34 +26,53 @@ #include "pfb_decimator_ccf_impl.h" #include <gnuradio/io_signature.h> +#include <gnuradio/expj.h> +#include <volk/volk.h> namespace gr { namespace filter { - + pfb_decimator_ccf::sptr pfb_decimator_ccf::make(unsigned int decim, const std::vector<float> &taps, - unsigned int channel) + unsigned int channel, + bool use_fft_rotator, + bool use_fft_filters) { return gnuradio::get_initial_sptr - (new pfb_decimator_ccf_impl(decim, taps, channel)); + (new pfb_decimator_ccf_impl(decim, taps, channel, + use_fft_rotator, + use_fft_filters)); } - pfb_decimator_ccf_impl::pfb_decimator_ccf_impl(unsigned int decim, const std::vector<float> &taps, - unsigned int channel) + unsigned int channel, + bool use_fft_rotator, + bool use_fft_filters) : sync_block("pfb_decimator_ccf", - io_signature::make(decim, decim, sizeof(gr_complex)), - io_signature::make(1, 1, sizeof(gr_complex))), + io_signature::make(decim, decim, sizeof(gr_complex)), + io_signature::make(1, 1, sizeof(gr_complex))), polyphase_filterbank(decim, taps), - d_updated(false), d_chan(channel) + d_updated(false), d_chan(channel), + d_use_fft_rotator(use_fft_rotator), + d_use_fft_filters(use_fft_filters) { d_rate = decim; d_rotator = new gr_complex[d_rate]; + for(unsigned int i = 0; i < d_rate; i++) { + d_rotator[i] = gr_expj(i*d_chan*2*M_PI/d_rate); + } set_relative_rate(1.0/(float)decim); - set_history(d_taps_per_filter); + + if(d_use_fft_filters) { + set_history(1); + set_output_multiple(d_fft_filters[0]->filtersize() - d_fft_filters[0]->ntaps() + 1); + } + else { + set_history(d_taps_per_filter); + } } pfb_decimator_ccf_impl::~pfb_decimator_ccf_impl() @@ -90,8 +109,6 @@ namespace gr { d_chan = chan; } -#define ROTATEFFT - int pfb_decimator_ccf_impl::work(int noutput_items, gr_vector_const_void_star &input_items, @@ -99,47 +116,145 @@ namespace gr { { gr::thread::scoped_lock guard(d_mutex); - gr_complex *in; - gr_complex *out = (gr_complex *)output_items[0]; - if(d_updated) { d_updated = false; return 0; // history requirements may have changed. } + if(d_use_fft_rotator) { + if(d_use_fft_filters) { + return work_fft_fft(noutput_items, input_items, output_items); + } + else { + return work_fir_fft(noutput_items, input_items, output_items); + } + } + else { + if(d_use_fft_filters) { + return work_fft_exp(noutput_items, input_items, output_items); + } + else { + return work_fir_exp(noutput_items, input_items, output_items); + } + } + } + + int + pfb_decimator_ccf_impl::work_fir_exp(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items) + { + gr_complex *in; + gr_complex *out = (gr_complex *)output_items[0]; + int i; for(i = 0; i < noutput_items; i++) { // Move through filters from bottom to top out[i] = 0; for(int j = d_rate-1; j >= 0; j--) { - // Take in the items from the first input stream to d_rate + // Take items from M-1 to 0; filter and rotate in = (gr_complex*)input_items[d_rate - 1 - j]; + out[i] += d_fir_filters[j]->filter(&in[i])*d_rotator[j]; + } + } - // Filter current input stream from bottom filter to top - // The rotate them by expj(j*k*2pi/M) where M is the number of filters - // (the decimation rate) and k is the channel number to extract - - // This is the real math that goes on; we abuse the FFT to do this quickly - // for decimation rates > N where N is a small number (~5): - // out[i] += d_filters[j]->filter(&in[i])*gr_expj(j*d_chan*2*M_PI/d_rate); -#ifdef ROTATEFFT - d_fft->get_inbuf()[j] = d_filters[j]->filter(&in[i]); -#else - out[i] += d_filters[j]->filter(&in[i])*d_rotator[i]; -#endif + return noutput_items; + } + + int + pfb_decimator_ccf_impl::work_fir_fft(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items) + { + gr_complex *in; + gr_complex *out = (gr_complex *)output_items[0]; + + int i; + for(i = 0; i < noutput_items; i++) { + // Move through filters from bottom to top + out[i] = 0; + for(unsigned int j = 0; j < d_rate; j++) { + // Take in the items from the first input stream to d_rate + in = (gr_complex*)input_items[d_rate-1-j]; + d_fft->get_inbuf()[j] = d_fir_filters[j]->filter(&in[i]); } -#ifdef ROTATEFFT - // Perform the FFT to do the complex multiply despinning for all channels - d_fft->execute(); + // Perform the FFT to do the complex multiply despinning for all channels + d_fft->execute(); - // Select only the desired channel out - out[i] = d_fft->get_outbuf()[d_chan]; -#endif + // Select only the desired channel out + out[i] = d_fft->get_outbuf()[d_chan]; + } + + return noutput_items; + } + + int + pfb_decimator_ccf_impl::work_fft_exp(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items) + { + gr_complex *in; + gr_complex *out = (gr_complex *)output_items[0]; + + int i; + gr_complex *tmp = fft::malloc_complex(noutput_items*d_rate); + + // Filter each input stream by the FFT filters; do all + // noutput_items at once to avoid repeated calls to the FFT + // setup and operation. + for(unsigned int j = 0; j < d_rate; j++) { + in = (gr_complex*)input_items[d_rate-j-1]; + d_fft_filters[j]->filter(noutput_items, in, &(tmp[j*noutput_items])); + } + + // Rotate and add filter outputs (k=channel number; M=number of + // channels; and x[j] is the output of filter j. + // y[i] = \sum_{j=0}{M-1} (x[j][i]*exp(2j \pi j k / M)) + for(i = 0; i < noutput_items; i++) { + out[i] = 0; + for(unsigned int j = 0; j < d_rate; j++) { + out[i] += tmp[j*noutput_items+i]*d_rotator[j]; + } } return noutput_items; } + int + pfb_decimator_ccf_impl::work_fft_fft(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items) + { + gr_complex *in; + gr_complex *out = (gr_complex *)output_items[0]; + + int i; + gr_complex *tmp = fft::malloc_complex(noutput_items*d_rate); + + for(unsigned int j = 0; j < d_rate; j++) { + in = (gr_complex*)input_items[d_rate-j-1]; + d_fft_filters[j]->filter(noutput_items, in, &tmp[j*noutput_items]); + } + + // Performs the rotate and add operations by implementing it as + // an FFT. + for(i = 0; i < noutput_items; i++) { + for(unsigned int j = 0; j < d_rate; j++) { + d_fft->get_inbuf()[j] = tmp[j*noutput_items + i]; + } + + // Perform the FFT to do the complex multiply despinning for all channels + d_fft->execute(); + + // Select only the desired channel out + out[i] = d_fft->get_outbuf()[d_chan]; + } + + fft::free(tmp); + return noutput_items; + } + + } /* namespace filter */ } /* namespace gr */ diff --git a/gr-filter/lib/pfb_decimator_ccf_impl.h b/gr-filter/lib/pfb_decimator_ccf_impl.h index 2df8a506f0..3397701cf9 100644 --- a/gr-filter/lib/pfb_decimator_ccf_impl.h +++ b/gr-filter/lib/pfb_decimator_ccf_impl.h @@ -26,26 +26,42 @@ #include <gnuradio/filter/pfb_decimator_ccf.h> #include <gnuradio/filter/polyphase_filterbank.h> -#include <gnuradio/filter/fir_filter.h> -#include <gnuradio/fft/fft.h> #include <gnuradio/thread/thread.h> namespace gr { namespace filter { - + class FILTER_API pfb_decimator_ccf_impl : public pfb_decimator_ccf, kernel::polyphase_filterbank { private: bool d_updated; unsigned int d_rate; unsigned int d_chan; + bool d_use_fft_rotator; + bool d_use_fft_filters; gr_complex *d_rotator; gr::thread::mutex d_mutex; // mutex to protect set/work access - + + inline int work_fir_exp(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items); + inline int work_fir_fft(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items); + inline int work_fft_exp(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items); + inline int work_fft_fft(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items); + + public: pfb_decimator_ccf_impl(unsigned int decim, const std::vector<float> &taps, - unsigned int channel); + unsigned int channel, + bool use_fft_rotator=true, + bool use_fft_filters=true); ~pfb_decimator_ccf_impl(); diff --git a/gr-filter/lib/pfb_interpolator_ccf_impl.cc b/gr-filter/lib/pfb_interpolator_ccf_impl.cc index a0ed73fd4e..cf79c103d3 100644 --- a/gr-filter/lib/pfb_interpolator_ccf_impl.cc +++ b/gr-filter/lib/pfb_interpolator_ccf_impl.cc @@ -29,7 +29,7 @@ namespace gr { namespace filter { - + pfb_interpolator_ccf::sptr pfb_interpolator_ccf::make(unsigned int interp, const std::vector<float> &taps) @@ -94,7 +94,7 @@ namespace gr { while(i < noutput_items) { for(unsigned int j = 0; j < d_rate; j++) { - out[i] = d_filters[j]->filter(&in[count]); + out[i] = d_fir_filters[j]->filter(&in[count]); i++; } count++; diff --git a/gr-filter/lib/pfb_synthesizer_ccf_impl.cc b/gr-filter/lib/pfb_synthesizer_ccf_impl.cc index 5f0e330ab4..88c3fcedf6 100644 --- a/gr-filter/lib/pfb_synthesizer_ccf_impl.cc +++ b/gr-filter/lib/pfb_synthesizer_ccf_impl.cc @@ -1,6 +1,6 @@ /* -*- c++ -*- */ /* - * Copyright 2010,2012 Free Software Foundation, Inc. + * Copyright 2010,2012,2014 Free Software Foundation, Inc. * * This file is part of GNU Radio * @@ -33,45 +33,43 @@ namespace gr { pfb_synthesizer_ccf::sptr pfb_synthesizer_ccf::make(unsigned int numchans, - const std::vector<float> &taps, - bool twox) + const std::vector<float> &taps, bool twox) { return gnuradio::get_initial_sptr - (new pfb_synthesizer_ccf_impl(numchans, taps, twox)); + (new pfb_synthesizer_ccf_impl(numchans, taps, twox)); } - - pfb_synthesizer_ccf_impl::pfb_synthesizer_ccf_impl(unsigned int numchans, - const std::vector<float> &taps, - bool twox) + pfb_synthesizer_ccf_impl::pfb_synthesizer_ccf_impl + (unsigned int numchans, const std::vector<float> &taps, bool twox) : sync_interpolator("pfb_synthesizer_ccf", - io_signature::make(1, numchans, sizeof(gr_complex)), - io_signature::make(1, 1, sizeof(gr_complex)), - (twox ? numchans/2 : numchans)), - d_updated(false), d_numchans(numchans), d_state(0) + io_signature::make(1, numchans, sizeof(gr_complex)), + io_signature::make(1, 1, sizeof(gr_complex)), + numchans), + d_updated(false), d_numchans(numchans), d_state(0) { // set up 2x multiplier; if twox==True, set to 2, otherwise to 1 d_twox = (twox ? 2 : 1); if(d_numchans % d_twox != 0) { - throw std::invalid_argument("pfb_synthesizer_ccf: number of channels must be even for 2x oversampling.\n"); + throw std::invalid_argument("pfb_synthesizer_ccf_impl: number of channels must be even for 2x oversampling.\n"); } - d_filters = std::vector<kernel::fir_filter_with_buffer_ccf*>(d_numchans); - d_channel_map.resize(d_numchans); + d_filters = std::vector<kernel::fir_filter_with_buffer_ccf*>(d_twox*d_numchans); + d_channel_map.resize(d_twox*d_numchans); - // Create a FIR filter for each channel and zero out the taps - std::vector<float> vtaps(0, d_numchans); - for(unsigned int i = 0; i < d_numchans; i++) { - d_filters[i] = new kernel::fir_filter_with_buffer_ccf(vtaps); - d_channel_map[i] = i; + // Create an FIR filter for each channel and zero out the taps + // and set the default channel map + std::vector<float> vtaps(0, d_twox*d_numchans); + for(unsigned int i = 0; i < d_twox*d_numchans; i++) { + d_filters[i] = new kernel::fir_filter_with_buffer_ccf(vtaps); + d_channel_map[i] = i; } // Now, actually set the filters' taps set_taps(taps); // Create the IFFT to handle the input channel rotations - d_fft = new fft::fft_complex(d_numchans, false); - memset(d_fft->get_inbuf(), 0, d_numchans*sizeof(gr_complex)); + d_fft = new fft::fft_complex(d_twox*d_numchans, false); + memset(d_fft->get_inbuf(), 0, d_twox*d_numchans*sizeof(gr_complex)); set_output_multiple(d_numchans); } @@ -79,8 +77,8 @@ namespace gr { pfb_synthesizer_ccf_impl::~pfb_synthesizer_ccf_impl() { delete d_fft; - for(unsigned int i = 0; i < d_numchans; i++) { - delete d_filters[i]; + for(unsigned int i = 0; i < d_twox*d_numchans; i++) { + delete d_filters[i]; } } @@ -89,20 +87,29 @@ namespace gr { { gr::thread::scoped_lock guard(d_mutex); + // The different modes, 1x or 2x the sampling rate, have + // different filtering partitions. if(d_twox == 1) - set_taps1(taps); + set_taps1(taps); else - set_taps2(taps); - - // Set the history to ensure enough input items for each filter - set_history(d_taps_per_filter+1); + set_taps2(taps); + // Because we keep our own buffers inside the filters, we don't + // need history. + set_history(1); d_updated = true; } void pfb_synthesizer_ccf_impl::set_taps1(const std::vector<float> &taps) { + // In this partitioning, we do a normal polyphase paritioning by + // deinterleaving the taps into each filter: + // + // Prototype filter: [t0, t1, t2, t3, t4, t5, t6] + // filter 0: [t0, t3, t6] + // filter 1: [t1, t4, 0 ] + // filter 2: [t2, t5, 0 ] unsigned int i,j; unsigned int ntaps = taps.size(); @@ -113,70 +120,77 @@ namespace gr { // Make a vector of the taps plus fill it out with 0's to fill // each polyphase filter with exactly d_taps_per_filter - std::vector<float> tmp_taps; - tmp_taps = taps; + std::vector<float> tmp_taps = taps; while((float)(tmp_taps.size()) < d_numchans*d_taps_per_filter) { - tmp_taps.push_back(0.0); + tmp_taps.push_back(0.0); } // Partition the filter for(i = 0; i < d_numchans; i++) { - // Each channel uses all d_taps_per_filter with 0's if not enough taps to fill out - d_taps[i] = std::vector<float>(d_taps_per_filter, 0); - for(j = 0; j < d_taps_per_filter; j++) { - d_taps[i][j] = tmp_taps[i + j*d_numchans]; // add taps to channels in reverse order - } - - // Build a filter for each channel and add it's taps to it - d_filters[i]->set_taps(d_taps[i]); + // Each channel uses all d_taps_per_filter with 0's if not enough taps to fill out + d_taps[i] = std::vector<float>(d_taps_per_filter, 0); + for(j = 0; j < d_taps_per_filter; j++) { + d_taps[i][j] = tmp_taps[i + j*d_numchans]; // add taps to channels in reverse order + } + + // Set the filter taps for each channel + d_filters[i]->set_taps(d_taps[i]); } } void - pfb_synthesizer_ccf_impl::set_taps2 (const std::vector<float> &taps) + pfb_synthesizer_ccf_impl::set_taps2(const std::vector<float> &taps) { + // In this partitioning, create 2M filters each in Z^2; the + // second half of the filters are also delayed by Z^1: + // + // Prototype filter: [t0, t1, t2, t3, t4, t5, t6] + // filter 0: [t0, 0, t6] + // filter 1: [t2, 0, 0] + // filter 2: [t4, 0, 0] + // filter 3: [ 0, t1, 0] + // filter 4: [ 0, t3, 0 ] + // filter 5: [ 0, t5, 0 ] + unsigned int i,j; int state = 0; - unsigned int ntaps = 2*taps.size(); + unsigned int ntaps = taps.size(); d_taps_per_filter = (unsigned int)ceil((double)ntaps/(double)d_numchans); // Create d_numchan vectors to store each channel's taps - d_taps.resize(d_numchans); + d_taps.resize(d_twox*d_numchans); // Make a vector of the taps plus fill it out with 0's to fill // each polyphase filter with exactly d_taps_per_filter - std::vector<float> tmp_taps; - tmp_taps = taps; + std::vector<float> tmp_taps = taps; while((float)(tmp_taps.size()) < d_numchans*d_taps_per_filter) { - tmp_taps.push_back(0.0); + tmp_taps.push_back(0.0); + //tmp_taps.insert(tmp_taps.begin(), 0.0); } // Partition the filter - unsigned int halfchans = d_numchans/2; - for(i = 0; i < halfchans; i++) { - // Each channel uses all d_taps_per_filter with 0's if not enough taps to fill out - d_taps[i] = std::vector<float>(d_taps_per_filter, 0); - d_taps[halfchans+i] = std::vector<float>(d_taps_per_filter, 0); - state = 0; - for(j = 0; j < d_taps_per_filter; j++) { - // add taps to channels in reverse order - // Zero out every other tap - if(state == 0) { - d_taps[i][j] = tmp_taps[i + j*halfchans]; - d_taps[halfchans + i][j] = 0; - state = 1; - } - else { - d_taps[i][j] = 0; - d_taps[halfchans + i][j] = tmp_taps[i + j*halfchans]; - state = 0; - } - } - - // Build a filter for each channel and add it's taps to it - d_filters[i]->set_taps(d_taps[i]); - d_filters[halfchans + i]->set_taps(d_taps[halfchans + i]); + for(i = 0; i < d_numchans; i++) { + // Each channel uses all d_taps_per_filter with 0's if not enough taps to fill out + d_taps[i] = std::vector<float>(d_taps_per_filter, 0); + d_taps[d_numchans+i] = std::vector<float>(d_taps_per_filter, 0); + state = 0; + for(j = 0; j < d_taps_per_filter; j++) { + if(state == 0) { + d_taps[i][j] = 0; + d_taps[d_numchans + i][j] = tmp_taps[i + j*d_numchans]; + state = 1; + } + else { + d_taps[i][j] = tmp_taps[i + j*d_numchans]; + d_taps[d_numchans + i][j] = 0; + state = 0; + } + } + + // Set the filter taps for each channel + d_filters[i]->set_taps(d_taps[i]); + d_filters[d_numchans + i]->set_taps(d_taps[d_numchans + i]); } } @@ -184,15 +198,16 @@ namespace gr { pfb_synthesizer_ccf_impl::print_taps() { unsigned int i, j; - for(i = 0; i < d_numchans; i++) { - printf("filter[%d]: [", i); - for(j = 0; j < d_taps_per_filter; j++) { - printf(" %.4e", d_taps[i][j]); - } - printf("]\n\n"); + for(i = 0; i < d_twox*d_numchans; i++) { + printf("filter[%d]: [", i); + for(j = 0; j < d_taps_per_filter; j++) { + printf(" %.4e", d_taps[i][j]); + } + printf("]\n\n"); } } + std::vector< std::vector<float> > pfb_synthesizer_ccf_impl::taps() const { @@ -205,14 +220,15 @@ namespace gr { gr::thread::scoped_lock guard(d_mutex); if(map.size() > 0) { - unsigned int max = (unsigned int)*std::max_element(map.begin(), map.end()); - if(max >= d_numchans) { - throw std::invalid_argument("gr_pfb_synthesizer_ccf::set_channel_map: map range out of bounds.\n"); - } - d_channel_map = map; - - // Zero out fft buffer so that unused channels are always 0 - memset(d_fft->get_inbuf(), 0, d_numchans*sizeof(gr_complex)); + int max = *std::max_element(map.begin(), map.end()); + int min = *std::min_element(map.begin(), map.end()); + if((max >= static_cast<int>(d_twox*d_numchans)) || (min < 0)) { + throw std::invalid_argument("pfb_synthesizer_ccf_impl::set_channel_map: map range out of bounds.\n"); + } + d_channel_map = map; + + // Zero out fft buffer so that unused channels are always 0 + memset(d_fft->get_inbuf(), 0, d_twox*d_numchans*sizeof(gr_complex)); } } @@ -224,63 +240,63 @@ namespace gr { int pfb_synthesizer_ccf_impl::work(int noutput_items, - gr_vector_const_void_star &input_items, - gr_vector_void_star &output_items) + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items) { gr::thread::scoped_lock guard(d_mutex); - gr_complex *in = (gr_complex*)input_items[0]; - gr_complex *out = (gr_complex*)output_items[0]; + gr_complex *in = (gr_complex*) input_items[0]; + gr_complex *out = (gr_complex *) output_items[0]; if(d_updated) { - d_updated = false; - return 0; // history requirements may have changed. + d_updated = false; + return 0; // history requirements may have changed. } unsigned int n, i; size_t ninputs = input_items.size(); - // Algoritm for critically sampled channels + // Algorithm for critically sampled channels if(d_twox == 1) { - for(n = 0; n < noutput_items/d_numchans; n++) { - for(i = 0; i < ninputs; i++) { - in = (gr_complex*)input_items[i]; - d_fft->get_inbuf()[d_channel_map[i]] = in[n]; - } - - // spin through IFFT - d_fft->execute(); - - for(i = 0; i < d_numchans; i++) { - out[i] = d_filters[i]->filter(d_fft->get_outbuf()[i]); - } - out += d_numchans; - } + for(n = 0; n < noutput_items/d_numchans; n++) { + for(i = 0; i < ninputs; i++) { + in = (gr_complex*)input_items[i]; + d_fft->get_inbuf()[d_channel_map[i]] = in[n]; + } + + // spin through IFFT + d_fft->execute(); + + for(i = 0; i < d_numchans; i++) { + out[i] = d_filters[i]->filter(d_fft->get_outbuf()[i]); + } + out += d_numchans; + } } // Algorithm for oversampling by 2x else { - unsigned int halfchans = d_numchans/2; - for(n = 0; n < noutput_items/halfchans; n++) { - for(i = 0; i < ninputs; i++) { - in = (gr_complex*)input_items[i]; - d_fft->get_inbuf()[d_channel_map[i]] = in[n]; - } - - // spin through IFFT - d_fft->execute(); - - // Output is sum of two filters, but the input buffer to the filters must be circularly - // shifted by numchans every time through, done by using d_state to determine which IFFT - // buffer position to pull from. - for(i = 0; i < halfchans; i++) { - out[i] = d_filters[i]->filter(d_fft->get_outbuf()[d_state*halfchans+i]); - out[i] += d_filters[halfchans+i]->filter(d_fft->get_outbuf()[(d_state^1)*halfchans+i]); - } - d_state ^= 1; - - out += halfchans; - } + for(n = 0; n < noutput_items/d_numchans; n++) { + for(i = 0; i < ninputs; i++) { + //in = (gr_complex*)input_items[ninputs-i-1]; + in = (gr_complex*)input_items[i]; + d_fft->get_inbuf()[d_channel_map[i]] = in[n]; + } + + // spin through IFFT + d_fft->execute(); + + // Output is sum of two filters, but the input buffer to the filters must be circularly + // shifted by numchans every time through, done by using d_state to determine which IFFT + // buffer position to pull from. + for(i = 0; i < d_numchans; i++) { + out[i] = d_filters[i]->filter(d_fft->get_outbuf()[d_state*d_numchans+i]); + out[i] += d_filters[d_numchans+i]->filter(d_fft->get_outbuf()[(d_state^1)*d_numchans+i]); + } + d_state ^= 1; + + out += d_numchans; + } } return noutput_items; diff --git a/gr-filter/lib/polyphase_filterbank.cc b/gr-filter/lib/polyphase_filterbank.cc index ab978e8aaf..5fffc02dce 100644 --- a/gr-filter/lib/polyphase_filterbank.cc +++ b/gr-filter/lib/polyphase_filterbank.cc @@ -1,6 +1,6 @@ /* -*- c++ -*- */ /* - * Copyright 2012 Free Software Foundation, Inc. + * Copyright 2012,2014 Free Software Foundation, Inc. * * This file is part of GNU Radio * @@ -32,29 +32,33 @@ namespace gr { namespace kernel { polyphase_filterbank::polyphase_filterbank(unsigned int nfilts, - const std::vector<float> &taps) + const std::vector<float> &taps, + bool fft_forward) : d_nfilts(nfilts) { - d_filters = std::vector<kernel::fir_filter_ccf*>(d_nfilts); + d_fir_filters = std::vector<kernel::fir_filter_ccf*>(d_nfilts); + d_fft_filters = std::vector<kernel::fft_filter_ccf*>(d_nfilts); // Create an FIR filter for each channel and zero out the taps - std::vector<float> vtaps(0, d_nfilts); + std::vector<float> vtaps(1, 0.0f); for(unsigned int i = 0; i < d_nfilts; i++) { - d_filters[i] = new kernel::fir_filter_ccf(1, vtaps); + d_fir_filters[i] = new kernel::fir_filter_ccf(1, vtaps); + d_fft_filters[i] = new kernel::fft_filter_ccf(1, vtaps); } // Now, actually set the filters' taps set_taps(taps); // Create the FFT to handle the output de-spinning of the channels - d_fft = new fft::fft_complex(d_nfilts, false); + d_fft = new fft::fft_complex(d_nfilts, fft_forward); } polyphase_filterbank::~polyphase_filterbank() { delete d_fft; for(unsigned int i = 0; i < d_nfilts; i++) { - delete d_filters[i]; + delete d_fir_filters[i]; + delete d_fft_filters[i]; } } @@ -62,7 +66,6 @@ namespace gr { polyphase_filterbank::set_taps(const std::vector<float> &taps) { unsigned int i,j; - unsigned int ntaps = taps.size(); d_taps_per_filter = (unsigned int)ceil((double)ntaps/(double)d_nfilts); @@ -71,16 +74,11 @@ namespace gr { // Make a vector of the taps plus fill it out with 0's to fill // each polyphase filter with exactly d_taps_per_filter - std::vector<float> tmp_taps; - tmp_taps = taps; + std::vector<float> tmp_taps = taps; while((float)(tmp_taps.size()) < d_nfilts*d_taps_per_filter) { - tmp_taps.push_back(0.0); + tmp_taps.push_back(0.0); } - // Reverse taps here; set_taps in filter will then re-reverse, - // but order them propely, anyways. - std::reverse(tmp_taps.begin(), tmp_taps.end()); - // Partition the filter for(i = 0; i < d_nfilts; i++) { // Each channel uses all d_taps_per_filter with 0's if not enough taps to fill out @@ -89,8 +87,9 @@ namespace gr { d_taps[i][j] = tmp_taps[i + j*d_nfilts]; } - // Build a filter for each channel and add it's taps to it - d_filters[i]->set_taps(d_taps[i]); + // Set the filter taps for each channel + d_fir_filters[i]->set_taps(d_taps[i]); + d_fft_filters[i]->set_taps(d_taps[i]); } } diff --git a/gr-filter/python/filter/pfb.py b/gr-filter/python/filter/pfb.py index 7f7375cb7f..732812ef2a 100644 --- a/gr-filter/python/filter/pfb.py +++ b/gr-filter/python/filter/pfb.py @@ -77,7 +77,11 @@ class channelizer_ccf(gr.hier_block2): def set_channel_map(self, newmap): self.pfb.set_channel_map(newmap) + def set_taps(self, taps): + self.pfb.set_taps(taps) + def taps(self): + return self.pfb.taps() class interpolator_ccf(gr.hier_block2): ''' @@ -122,6 +126,8 @@ class interpolator_ccf(gr.hier_block2): self.connect(self, self.pfb) self.connect(self.pfb, self) + def set_taps(self, taps): + self.pfb.set_taps(taps) class decimator_ccf(gr.hier_block2): ''' @@ -130,10 +136,11 @@ class decimator_ccf(gr.hier_block2): This simplifies the interface by allowing a single input stream to connect to this block. It will then output a stream that is the decimated output stream. ''' - def __init__(self, decim, taps=None, channel=0, atten=100): - gr.hier_block2.__init__(self, "pfb_decimator_ccf", - gr.io_signature(1, 1, gr.sizeof_gr_complex), - gr.io_signature(1, 1, gr.sizeof_gr_complex)) + def __init__(self, decim, taps=None, channel=0, atten=100, + use_fft_rotators=True, use_fft_filters=True): + gr.hier_block2.__init__(self, "pfb_decimator_ccf", + gr.io_signature(1, 1, gr.sizeof_gr_complex), + gr.io_signature(1, 1, gr.sizeof_gr_complex)) self._decim = decim self._channel = channel @@ -160,7 +167,8 @@ class decimator_ccf(gr.hier_block2): raise RuntimeError("optfir could not generate an appropriate filter.") self.s2ss = blocks.stream_to_streams(gr.sizeof_gr_complex, self._decim) - self.pfb = filter.pfb_decimator_ccf(self._decim, self._taps, self._channel) + self.pfb = filter.pfb_decimator_ccf(self._decim, self._taps, self._channel, + use_fft_rotators, use_fft_filters) self.connect(self, self.s2ss) diff --git a/gr-filter/python/filter/qa_fft_filter.py b/gr-filter/python/filter/qa_fft_filter.py index fafd2330ae..172b945441 100755 --- a/gr-filter/python/filter/qa_fft_filter.py +++ b/gr-filter/python/filter/qa_fft_filter.py @@ -65,6 +65,18 @@ def reference_filter_fff(dec, taps, input): tb.run() return dst.data() +def reference_filter_ccf(dec, taps, input): + """ + compute result using conventional fir filter + """ + tb = gr.top_block() + src = blocks.vector_source_c(input) + op = filter.fir_filter_ccf(dec, taps) + dst = blocks.vector_sink_c() + tb.connect(src, op, dst) + tb.run() + return dst.data() + def print_complex(x): for i in x: @@ -207,6 +219,125 @@ class test_fft_filter(gr_unittest.TestCase): self.assert_fft_ok2(expected_result, result_data) # ---------------------------------------------------------------- + # test _ccf version + # ---------------------------------------------------------------- + + def test_ccf_001(self): + tb = gr.top_block() + src_data = (0,1,2,3,4,5,6,7) + taps = (1,) + expected_result = tuple([complex(x) for x in (0,1,2,3,4,5,6,7)]) + src = blocks.vector_source_c(src_data) + op = filter.fft_filter_ccf(1, taps) + dst = blocks.vector_sink_c() + tb.connect(src, op, dst) + tb.run() + result_data = dst.data() + #print 'expected:', expected_result + #print 'results: ', result_data + self.assertComplexTuplesAlmostEqual (expected_result, result_data, 5) + + + def test_ccf_002(self): + # Test nthreads + tb = gr.top_block() + src_data = (0,1,2,3,4,5,6,7) + taps = (2,) + nthreads = 2 + expected_result = tuple([2 * complex(x) for x in (0,1,2,3,4,5,6,7)]) + src = blocks.vector_source_c(src_data) + op = filter.fft_filter_ccf(1, taps, nthreads) + dst = blocks.vector_sink_c() + tb.connect(src, op, dst) + tb.run() + result_data = dst.data() + #print 'expected:', expected_result + #print 'results: ', result_data + self.assertComplexTuplesAlmostEqual (expected_result, result_data, 5) + + def test_ccf_003(self): + tb = gr.top_block() + src_data = (0,1,2,3,4,5,6,7) + taps = (2,) + expected_result = tuple([2 * complex(x) for x in (0,1,2,3,4,5,6,7)]) + src = blocks.vector_source_c(src_data) + op = filter.fft_filter_ccf(1, taps) + dst = blocks.vector_sink_c() + tb.connect(src, op, dst) + tb.run() + result_data = dst.data() + #print 'expected:', expected_result + #print 'results: ', result_data + self.assertComplexTuplesAlmostEqual (expected_result, result_data, 5) + + + def test_ccf_004(self): + random.seed(0) + for i in xrange(25): + # sys.stderr.write("\n>>> Loop = %d\n" % (i,)) + src_len = 4*1024 + src_data = make_random_complex_tuple(src_len) + ntaps = int(random.uniform(2, 1000)) + taps = make_random_float_tuple(ntaps) + expected_result = reference_filter_ccf(1, taps, src_data) + + src = blocks.vector_source_c(src_data) + op = filter.fft_filter_ccf(1, taps) + dst = blocks.vector_sink_c() + tb = gr.top_block() + tb.connect(src, op, dst) + tb.run() + result_data = dst.data() + del tb + self.assert_fft_ok2(expected_result, result_data) + + def test_ccf_005(self): + random.seed(0) + for i in xrange(25): + # sys.stderr.write("\n>>> Loop = %d\n" % (i,)) + dec = i + 1 + src_len = 4*1024 + src_data = make_random_complex_tuple(src_len) + ntaps = int(random.uniform(2, 100)) + taps = make_random_float_tuple(ntaps) + expected_result = reference_filter_ccf(dec, taps, src_data) + + src = blocks.vector_source_c(src_data) + op = filter.fft_filter_ccf(dec, taps) + dst = blocks.vector_sink_c() + tb = gr.top_block() + tb.connect(src, op, dst) + tb.run() + del tb + result_data = dst.data() + + self.assert_fft_ok2(expected_result, result_data) + + def test_ccf_006(self): + # Test decimating with nthreads=2 + random.seed(0) + nthreads = 2 + for i in xrange(25): + # sys.stderr.write("\n>>> Loop = %d\n" % (i,)) + dec = i + 1 + src_len = 4*1024 + src_data = make_random_complex_tuple(src_len) + ntaps = int(random.uniform(2, 100)) + taps = make_random_float_tuple(ntaps) + expected_result = reference_filter_ccf(dec, taps, src_data) + + src = blocks.vector_source_c(src_data) + op = filter.fft_filter_ccc(dec, taps, nthreads) + dst = blocks.vector_sink_c() + tb = gr.top_block() + tb.connect(src, op, dst) + tb.run() + del tb + result_data = dst.data() + + self.assert_fft_ok2(expected_result, result_data) + + # ---------------------------------------------------------------- # test _fff version # ---------------------------------------------------------------- diff --git a/gr-filter/python/filter/qa_pfb_channelizer.py b/gr-filter/python/filter/qa_pfb_channelizer.py index 4c108e8443..46c6e7b5ae 100755 --- a/gr-filter/python/filter/qa_pfb_channelizer.py +++ b/gr-filter/python/filter/qa_pfb_channelizer.py @@ -1,6 +1,6 @@ #!/usr/bin/env python # -# Copyright 2012,2013 Free Software Foundation, Inc. +# Copyright 2012-2014 Free Software Foundation, Inc. # # This file is part of GNU Radio # @@ -67,18 +67,18 @@ class test_pfb_channelizer(gr_unittest.TestCase): self.tb.connect((s2ss,i), (pfb,i)) self.tb.connect((pfb, i), snks[i]) - self.tb.run() + self.tb.run() Ntest = 50 L = len(snks[0].data()) # Adjusted phase rotations for data - p0 = -2*math.pi * 0 / M - p1 = -2*math.pi * 1 / M - p2 = -2*math.pi * 2 / M - p3 = -2*math.pi * 3 / M - p4 = -2*math.pi * 4 / M - + p0 = 0.11058379158914133 + p1 = 4.5108246571401693 + p2 = 3.9739891674564594 + p3 = 2.2820531095511924 + p4 = 1.3782797467397869 + # Filter delay is the normal delay of each arm tpf = math.ceil(len(taps) / float(M)) delay = -(tpf - 1.0) / 2.0 diff --git a/gr-filter/python/filter/qa_pfb_decimator.py b/gr-filter/python/filter/qa_pfb_decimator.py index ea7a15570c..4366e85eec 100755 --- a/gr-filter/python/filter/qa_pfb_decimator.py +++ b/gr-filter/python/filter/qa_pfb_decimator.py @@ -1,6 +1,6 @@ #!/usr/bin/env python # -# Copyright 2012,2013 Free Software Foundation, Inc. +# Copyright 2012-2014 Free Software Foundation, Inc. # # This file is part of GNU Radio # @@ -29,7 +29,7 @@ def sig_source_c(samp_rate, freq, amp, N): 1j*math.sin(2.*math.pi*freq*x), t) return y -def run_test(tb, channel): +def run_test(tb, channel, fft_rotate, fft_filter): N = 1000 # number of samples to use M = 5 # Number of channels fs = 5000.0 # baseband sampling rate @@ -50,19 +50,24 @@ def run_test(tb, channel): tb.connect(signals[i], (add,i)) s2ss = blocks.stream_to_streams(gr.sizeof_gr_complex, M) - pfb = filter.pfb_decimator_ccf(M, taps, channel) + pfb = filter.pfb_decimator_ccf(M, taps, channel, fft_rotate, fft_filter) snk = blocks.vector_sink_c() tb.connect(add, s2ss) for i in xrange(M): tb.connect((s2ss,i), (pfb,i)) tb.connect(pfb, snk) - tb.run() + tb.run() L = len(snk.data()) - # Each channel is rotated by 2pi/M - phase = -2*math.pi * channel / M + # Adjusted phase rotations for data + phase = [ 0.11058476216852586, + 4.5108246571401693, + 3.9739891674564594, + 2.2820531095511924, + 1.3782797467397869] + phase = phase[channel] # Filter delay is the normal delay of each arm tpf = math.ceil(len(taps) / float(M)) @@ -90,33 +95,63 @@ class test_pfb_decimator(gr_unittest.TestCase): def test_000(self): Ntest = 50 - dst_data, expected_data = run_test(self.tb, 0) - - self.assertComplexTuplesAlmostEqual(expected_data[-Ntest:], dst_data[-Ntest:], 4) + dst_data0, expected_data0 = run_test(self.tb, 0, False, False) + dst_data1, expected_data1 = run_test(self.tb, 0, False, True) + dst_data2, expected_data2 = run_test(self.tb, 0, True, False) + dst_data3, expected_data3 = run_test(self.tb, 0, True, True) + + self.assertComplexTuplesAlmostEqual(expected_data0[-Ntest:], dst_data0[-Ntest:], 4) + self.assertComplexTuplesAlmostEqual(expected_data1[-Ntest:], dst_data1[-Ntest:], 4) + self.assertComplexTuplesAlmostEqual(expected_data2[-Ntest:], dst_data2[-Ntest:], 4) + self.assertComplexTuplesAlmostEqual(expected_data3[-Ntest:], dst_data3[-Ntest:], 4) def test_001(self): Ntest = 50 - dst_data, expected_data = run_test(self.tb, 1) - - self.assertComplexTuplesAlmostEqual(expected_data[-Ntest:], dst_data[-Ntest:], 4) + dst_data0, expected_data0 = run_test(self.tb, 1, False, False) + dst_data1, expected_data1 = run_test(self.tb, 1, False, True) + dst_data2, expected_data2 = run_test(self.tb, 1, True, False) + dst_data3, expected_data3 = run_test(self.tb, 1, True, True) + + self.assertComplexTuplesAlmostEqual(expected_data0[-Ntest:], dst_data0[-Ntest:], 4) + self.assertComplexTuplesAlmostEqual(expected_data1[-Ntest:], dst_data1[-Ntest:], 4) + self.assertComplexTuplesAlmostEqual(expected_data2[-Ntest:], dst_data2[-Ntest:], 4) + self.assertComplexTuplesAlmostEqual(expected_data3[-Ntest:], dst_data3[-Ntest:], 4) def test_002(self): Ntest = 50 - dst_data, expected_data = run_test(self.tb, 2) - - self.assertComplexTuplesAlmostEqual(expected_data[-Ntest:], dst_data[-Ntest:], 4) + dst_data0, expected_data0 = run_test(self.tb, 2, False, False) + dst_data1, expected_data1 = run_test(self.tb, 2, False, True) + dst_data2, expected_data2 = run_test(self.tb, 2, True, False) + dst_data3, expected_data3 = run_test(self.tb, 2, True, True) + + self.assertComplexTuplesAlmostEqual(expected_data0[-Ntest:], dst_data0[-Ntest:], 4) + self.assertComplexTuplesAlmostEqual(expected_data1[-Ntest:], dst_data1[-Ntest:], 4) + self.assertComplexTuplesAlmostEqual(expected_data2[-Ntest:], dst_data2[-Ntest:], 4) + self.assertComplexTuplesAlmostEqual(expected_data3[-Ntest:], dst_data3[-Ntest:], 4) def test_003(self): Ntest = 50 - dst_data, expected_data = run_test(self.tb, 3) - - self.assertComplexTuplesAlmostEqual(expected_data[-Ntest:], dst_data[-Ntest:], 4) + dst_data0, expected_data0 = run_test(self.tb, 3, False, False) + dst_data1, expected_data1 = run_test(self.tb, 3, False, True) + dst_data2, expected_data2 = run_test(self.tb, 3, True, False) + dst_data3, expected_data3 = run_test(self.tb, 3, True, True) + + self.assertComplexTuplesAlmostEqual(expected_data0[-Ntest:], dst_data0[-Ntest:], 4) + self.assertComplexTuplesAlmostEqual(expected_data1[-Ntest:], dst_data1[-Ntest:], 4) + self.assertComplexTuplesAlmostEqual(expected_data2[-Ntest:], dst_data2[-Ntest:], 4) + self.assertComplexTuplesAlmostEqual(expected_data3[-Ntest:], dst_data3[-Ntest:], 4) def test_004(self): Ntest = 50 - dst_data, expected_data = run_test(self.tb, 4) - - self.assertComplexTuplesAlmostEqual(expected_data[-Ntest:], dst_data[-Ntest:], 4) + dst_data0, expected_data0 = run_test(self.tb, 4, False, False) + dst_data1, expected_data1 = run_test(self.tb, 4, False, True) + dst_data2, expected_data2 = run_test(self.tb, 4, True, False) + dst_data3, expected_data3 = run_test(self.tb, 4, True, True) + + self.assertComplexTuplesAlmostEqual(expected_data0[-Ntest:], dst_data0[-Ntest:], 4) + self.assertComplexTuplesAlmostEqual(expected_data1[-Ntest:], dst_data1[-Ntest:], 4) + self.assertComplexTuplesAlmostEqual(expected_data2[-Ntest:], dst_data2[-Ntest:], 4) + self.assertComplexTuplesAlmostEqual(expected_data3[-Ntest:], dst_data3[-Ntest:], 4) if __name__ == '__main__': gr_unittest.run(test_pfb_decimator, "test_pfb_decimator.xml") diff --git a/gr-filter/python/filter/qa_pfb_interpolator.py b/gr-filter/python/filter/qa_pfb_interpolator.py index 33c22675af..b7ed4feef6 100755 --- a/gr-filter/python/filter/qa_pfb_interpolator.py +++ b/gr-filter/python/filter/qa_pfb_interpolator.py @@ -1,6 +1,6 @@ #!/usr/bin/env python # -# Copyright 2012,2013 Free Software Foundation, Inc. +# Copyright 2012-2014 Free Software Foundation, Inc. # # This file is part of GNU Radio # @@ -42,9 +42,9 @@ class test_pfb_interpolator(gr_unittest.TestCase): N = 1000 # number of samples to use M = 5 # Number of channels fs = 1000 # baseband sampling rate - ifs = M*fs # input samp rate to decimator + ofs = M*fs # output samp rate of interpolator - taps = filter.firdes.low_pass_2(M, ifs, fs/2, fs/10, + taps = filter.firdes.low_pass_2(M, ofs, fs/4, fs/10, attenuation_dB=80, window=filter.firdes.WIN_BLACKMAN_hARRIS) @@ -57,20 +57,16 @@ class test_pfb_interpolator(gr_unittest.TestCase): self.tb.connect(signal, pfb) self.tb.connect(pfb, snk) - self.tb.run() + self.tb.run() Ntest = 50 L = len(snk.data()) - # Can only get channel 0 out; no phase rotation - phase = 0 + # Phase rotation through the filters + phase = 4.8870112969978994 - # Calculate the filter delay - delay = -(len(taps) - 1) / 2.0 - (M-1) - delay = int(delay) - - # Create a time scale that's delayed to match the filter delay - t = map(lambda x: float(x)/ifs, xrange(delay, L+delay)) + # Create a time scale + t = map(lambda x: float(x)/ofs, xrange(0, L)) # Create known data as complex sinusoids for the baseband freq # of the extracted channel is due to decimator output order. diff --git a/gr-filter/python/filter/qa_pfb_synthesizer.py b/gr-filter/python/filter/qa_pfb_synthesizer.py index baba904088..0b3f8b27a2 100755 --- a/gr-filter/python/filter/qa_pfb_synthesizer.py +++ b/gr-filter/python/filter/qa_pfb_synthesizer.py @@ -1,6 +1,6 @@ #!/usr/bin/env python # -# Copyright 2012,2013 Free Software Foundation, Inc. +# Copyright 2012-2014 Free Software Foundation, Inc. # # This file is part of GNU Radio # @@ -62,7 +62,14 @@ class test_pfb_synthesizer(gr_unittest.TestCase): self.tb.connect(pfb, snk) - self.tb.run() + self.tb.run() + + # Adjusted phase rotations for data + p0 = 0 + p1 = 2*math.pi*1.0/M + p2 = 2*math.pi*2.0/M + p3 = 2*math.pi*3.0/M + p4 = 2*math.pi*4.0/M Ntest = 1000 L = len(snk.data()) @@ -73,20 +80,19 @@ class test_pfb_synthesizer(gr_unittest.TestCase): freqs = [-2200, -1100, 0, 1100, 2200] expected_data = len(t)*[0,] for i in xrange(len(t)): - expected_data[i] = math.cos(2.*math.pi*freqs[0]*t[i]) + \ - 1j*math.sin(2.*math.pi*freqs[0]*t[i]) + \ - math.cos(2.*math.pi*freqs[1]*t[i]) + \ - 1j*math.sin(2.*math.pi*freqs[1]*t[i]) + \ - math.cos(2.*math.pi*freqs[2]*t[i]) + \ - 1j*math.sin(2.*math.pi*freqs[2]*t[i]) + \ - math.cos(2.*math.pi*freqs[3]*t[i]) + \ - 1j*math.sin(2.*math.pi*freqs[3]*t[i]) + \ - math.cos(2.*math.pi*freqs[4]*t[i]) + \ - 1j*math.sin(2.*math.pi*freqs[4]*t[i]) + expected_data[i] = math.cos(2.*math.pi*freqs[0]*t[i] + p3) + \ + 1j*math.sin(2.*math.pi*freqs[0]*t[i] + p3) + \ + math.cos(2.*math.pi*freqs[1]*t[i] + p4) + \ + 1j*math.sin(2.*math.pi*freqs[1]*t[i] + p4) + \ + math.cos(2.*math.pi*freqs[2]*t[i] + p0) + \ + 1j*math.sin(2.*math.pi*freqs[2]*t[i] + p0) + \ + math.cos(2.*math.pi*freqs[3]*t[i] + p1) + \ + 1j*math.sin(2.*math.pi*freqs[3]*t[i] + p1) + \ + math.cos(2.*math.pi*freqs[4]*t[i] + p2) + \ + 1j*math.sin(2.*math.pi*freqs[4]*t[i] + p2) dst_data = snk.data() - offset = 25 - self.assertComplexTuplesAlmostEqual(expected_data[2000-offset:2000-offset+Ntest], + self.assertComplexTuplesAlmostEqual(expected_data[2000:2000+Ntest], dst_data[2000:2000+Ntest], 4) if __name__ == '__main__': diff --git a/gr-filter/swig/filter_swig.i b/gr-filter/swig/filter_swig.i index bc9f204850..3eb3f3c634 100644 --- a/gr-filter/swig/filter_swig.i +++ b/gr-filter/swig/filter_swig.i @@ -40,6 +40,7 @@ #include "gnuradio/filter/fir_filter_fsf.h" #include "gnuradio/filter/fir_filter_scc.h" #include "gnuradio/filter/fft_filter_ccc.h" +#include "gnuradio/filter/fft_filter_ccf.h" #include "gnuradio/filter/fft_filter_fff.h" #include "gnuradio/filter/fractional_interpolator_cc.h" #include "gnuradio/filter/fractional_interpolator_ff.h" @@ -88,6 +89,7 @@ %include "gnuradio/filter/fir_filter_fsf.h" %include "gnuradio/filter/fir_filter_scc.h" %include "gnuradio/filter/fft_filter_ccc.h" +%include "gnuradio/filter/fft_filter_ccf.h" %include "gnuradio/filter/fft_filter_fff.h" %include "gnuradio/filter/fractional_interpolator_cc.h" %include "gnuradio/filter/fractional_interpolator_ff.h" @@ -133,6 +135,7 @@ GR_SWIG_BLOCK_MAGIC2(filter, fir_filter_fff); GR_SWIG_BLOCK_MAGIC2(filter, fir_filter_fsf); GR_SWIG_BLOCK_MAGIC2(filter, fir_filter_scc); GR_SWIG_BLOCK_MAGIC2(filter, fft_filter_ccc); +GR_SWIG_BLOCK_MAGIC2(filter, fft_filter_ccf); GR_SWIG_BLOCK_MAGIC2(filter, fft_filter_fff); GR_SWIG_BLOCK_MAGIC2(filter, fractional_interpolator_cc); GR_SWIG_BLOCK_MAGIC2(filter, fractional_interpolator_ff); diff --git a/gr-qtgui/CMakeLists.txt b/gr-qtgui/CMakeLists.txt index a3a72dce43..13cbfb2e1f 100644 --- a/gr-qtgui/CMakeLists.txt +++ b/gr-qtgui/CMakeLists.txt @@ -132,4 +132,13 @@ install( COMPONENT "qtgui_devel" ) +######################################################################## +# Install the conf file +######################################################################## +install( + FILES ${CMAKE_CURRENT_SOURCE_DIR}/gr-qtgui.conf + DESTINATION ${GR_PREFSDIR} + COMPONENT "qtgui" +) + endif(ENABLE_GR_QTGUI) diff --git a/gr-qtgui/doc/qtgui.dox b/gr-qtgui/doc/qtgui.dox index beb1055e90..cbe5b831fb 100644 --- a/gr-qtgui/doc/qtgui.dox +++ b/gr-qtgui/doc/qtgui.dox @@ -181,4 +181,26 @@ style, markers, etc.), the ability to start and stop the display, the ability to save to a file, and other plot-specific controls (FFT size for the frequency and waterfall plots, etc.). +\section Configuration + +There is currently a single configuration option in the preferences +files to set the rendering engine of the QTGUI sinks. Located in +etc/gnuradio/conf.d/gr-qtgui.conf: + +\verbatim +[qtgui] +style = raster +\endverbatim + +The available styles are: + +\li opengl: the fastest but not likely to always work +\li raster: fast and stable; terrible X forwarding performance +\li native: most compute intensive; very good over X + +We default this setting to raster for the mix of performance and +usability. When using QTGUI sinks through an X-forwarding session over +SSH, switch to using 'native' for a significant speed boost on the +remote end. + */ diff --git a/gr-qtgui/gr-qtgui.conf b/gr-qtgui/gr-qtgui.conf new file mode 100644 index 0000000000..66354cfd4b --- /dev/null +++ b/gr-qtgui/gr-qtgui.conf @@ -0,0 +1,10 @@ +# This file contains system wide configuration data for GNU Radio. +# You may override any setting on a per-user basis by editing +# ~/.gnuradio/config.conf + +[qtgui] +# 'raster', 'native', or 'opengl' +# - 'opengl' is the fastest but not likely to always work +# - 'raster' is fast and stable; terrible X forwarding performance +# - 'native' most compute intensive; very good over X +style = raster diff --git a/gr-qtgui/lib/const_sink_c_impl.cc b/gr-qtgui/lib/const_sink_c_impl.cc index fd9338f111..8b1a804fd2 100644 --- a/gr-qtgui/lib/const_sink_c_impl.cc +++ b/gr-qtgui/lib/const_sink_c_impl.cc @@ -26,14 +26,14 @@ #include "const_sink_c_impl.h" #include <gnuradio/io_signature.h> +#include <gnuradio/prefs.h> #include <string.h> #include <volk/volk.h> -#include <gnuradio/fft/fft.h> #include <qwt_symbol.h> namespace gr { namespace qtgui { - + const_sink_c::sptr const_sink_c::make(int size, const std::string &name, @@ -110,6 +110,8 @@ namespace gr { d_qApplication = qApp; } else { + std::string style = prefs::singleton()->get_string("qtgui", "style", "raster"); + QApplication::setGraphicsSystem(QString(style.c_str())); d_qApplication = new QApplication(d_argc, &d_argv); } @@ -272,8 +274,8 @@ namespace gr { memset(d_residbufs_imag[i], 0, newsize*sizeof(double)); } - // Set new size and reset buffer index - // (throws away any currently held data, but who cares?) + // Set new size and reset buffer index + // (throws away any currently held data, but who cares?) d_size = newsize; d_index = 0; diff --git a/gr-qtgui/lib/freq_sink_c_impl.cc b/gr-qtgui/lib/freq_sink_c_impl.cc index e86974fa31..ff375976fb 100644 --- a/gr-qtgui/lib/freq_sink_c_impl.cc +++ b/gr-qtgui/lib/freq_sink_c_impl.cc @@ -26,6 +26,7 @@ #include "freq_sink_c_impl.h" #include <gnuradio/io_signature.h> +#include <gnuradio/prefs.h> #include <string.h> #include <volk/volk.h> #include <qwt_symbol.h> @@ -132,7 +133,9 @@ namespace gr { d_qApplication = qApp; } else { - d_qApplication = new QApplication(d_argc, &d_argv); + std::string style = prefs::singleton()->get_string("qtgui", "style", "raster"); + QApplication::setGraphicsSystem(QString(style.c_str())); + d_qApplication = new QApplication(d_argc, &d_argv); } d_main_gui = new FreqDisplayForm(d_nconnections, d_parent); diff --git a/gr-qtgui/lib/freq_sink_c_impl.h b/gr-qtgui/lib/freq_sink_c_impl.h index 8079c816f6..f068e657f5 100644 --- a/gr-qtgui/lib/freq_sink_c_impl.h +++ b/gr-qtgui/lib/freq_sink_c_impl.h @@ -32,7 +32,7 @@ namespace gr { namespace qtgui { - + class QTGUI_API freq_sink_c_impl : public freq_sink_c { private: diff --git a/gr-qtgui/lib/freq_sink_f_impl.cc b/gr-qtgui/lib/freq_sink_f_impl.cc index 8dca8151d7..f2dfb4f56c 100644 --- a/gr-qtgui/lib/freq_sink_f_impl.cc +++ b/gr-qtgui/lib/freq_sink_f_impl.cc @@ -26,6 +26,7 @@ #include "freq_sink_f_impl.h" #include <gnuradio/io_signature.h> +#include <gnuradio/prefs.h> #include <string.h> #include <volk/volk.h> #include <qwt_symbol.h> @@ -132,6 +133,8 @@ namespace gr { d_qApplication = qApp; } else { + std::string style = prefs::singleton()->get_string("qtgui", "style", "raster"); + QApplication::setGraphicsSystem(QString(style.c_str())); d_qApplication = new QApplication(d_argc, &d_argv); } diff --git a/gr-qtgui/lib/histogram_sink_f_impl.cc b/gr-qtgui/lib/histogram_sink_f_impl.cc index 8a3ba6987e..07620a1246 100644 --- a/gr-qtgui/lib/histogram_sink_f_impl.cc +++ b/gr-qtgui/lib/histogram_sink_f_impl.cc @@ -26,14 +26,14 @@ #include "histogram_sink_f_impl.h" #include <gnuradio/io_signature.h> +#include <gnuradio/prefs.h> #include <string.h> #include <volk/volk.h> -#include <gnuradio/fft/fft.h> #include <qwt_symbol.h> namespace gr { namespace qtgui { - + histogram_sink_f::sptr histogram_sink_f::make(int size, int bins, double xmin, double xmax, @@ -109,6 +109,8 @@ namespace gr { d_qApplication = qApp; } else { + std::string style = prefs::singleton()->get_string("qtgui", "style", "raster"); + QApplication::setGraphicsSystem(QString(style.c_str())); d_qApplication = new QApplication(d_argc, &d_argv); } @@ -270,8 +272,8 @@ namespace gr { memset(d_residbufs[i], 0, newsize*sizeof(double)); } - // Set new size and reset buffer index - // (throws away any currently held data, but who cares?) + // Set new size and reset buffer index + // (throws away any currently held data, but who cares?) d_size = newsize; d_index = 0; diff --git a/gr-qtgui/lib/sink_c_impl.cc b/gr-qtgui/lib/sink_c_impl.cc index d9341bab88..b810a450b6 100644 --- a/gr-qtgui/lib/sink_c_impl.cc +++ b/gr-qtgui/lib/sink_c_impl.cc @@ -26,6 +26,7 @@ #include "sink_c_impl.h" #include <gnuradio/io_signature.h> +#include <gnuradio/prefs.h> #include <string.h> #include <volk/volk.h> @@ -122,6 +123,8 @@ namespace gr { d_qApplication = qApp; } else { + std::string style = prefs::singleton()->get_string("qtgui", "style", "raster"); + QApplication::setGraphicsSystem(QString(style.c_str())); d_qApplication = new QApplication(d_argc, &d_argv); } diff --git a/gr-qtgui/lib/sink_f_impl.cc b/gr-qtgui/lib/sink_f_impl.cc index 178eb1b971..ef194f9ead 100644 --- a/gr-qtgui/lib/sink_f_impl.cc +++ b/gr-qtgui/lib/sink_f_impl.cc @@ -26,6 +26,7 @@ #include "sink_f_impl.h" #include <gnuradio/io_signature.h> +#include <gnuradio/prefs.h> #include <string.h> #include <volk/volk.h> @@ -122,6 +123,8 @@ namespace gr { d_qApplication = qApp; } else { + std::string style = prefs::singleton()->get_string("qtgui", "style", "raster"); + QApplication::setGraphicsSystem(QString(style.c_str())); d_qApplication = new QApplication(d_argc, &d_argv); } diff --git a/gr-qtgui/lib/time_raster_sink_b_impl.cc b/gr-qtgui/lib/time_raster_sink_b_impl.cc index 26daffe7bb..84f702b3d5 100644 --- a/gr-qtgui/lib/time_raster_sink_b_impl.cc +++ b/gr-qtgui/lib/time_raster_sink_b_impl.cc @@ -26,12 +26,13 @@ #include "time_raster_sink_b_impl.h" #include <gnuradio/io_signature.h> +#include <gnuradio/prefs.h> #include <string.h> #include <volk/volk.h> namespace gr { namespace qtgui { - + time_raster_sink_b::sptr time_raster_sink_b::make(double samp_rate, double rows, double cols, @@ -84,7 +85,7 @@ namespace gr { d_tmpflt = (float*)volk_malloc(d_icols*sizeof(float), volk_get_alignment()); memset(d_tmpflt, 0, d_icols*sizeof(float)); - + for(int i = 0; i < d_nconnections; i++) { d_residbufs.push_back((double*)volk_malloc(d_icols*sizeof(double), volk_get_alignment())); @@ -123,6 +124,8 @@ namespace gr { d_qApplication = qApp; } else { + std::string style = prefs::singleton()->get_string("qtgui", "style", "raster"); + QApplication::setGraphicsSystem(QString(style.c_str())); d_qApplication = new QApplication(d_argc, &d_argv); } @@ -417,7 +420,7 @@ namespace gr { volk_32f_convert_64f_u(&d_residbufs[n][d_index], d_tmpflt, resid); } - + if(gr::high_res_timer_now() - d_last_time > d_update_time) { d_last_time = gr::high_res_timer_now(); d_qApplication->postEvent(d_main_gui, diff --git a/gr-qtgui/lib/time_raster_sink_f_impl.cc b/gr-qtgui/lib/time_raster_sink_f_impl.cc index 10b7f762ca..beb2937b83 100644 --- a/gr-qtgui/lib/time_raster_sink_f_impl.cc +++ b/gr-qtgui/lib/time_raster_sink_f_impl.cc @@ -26,12 +26,13 @@ #include "time_raster_sink_f_impl.h" #include <gnuradio/io_signature.h> +#include <gnuradio/prefs.h> #include <string.h> #include <volk/volk.h> namespace gr { namespace qtgui { - + time_raster_sink_f::sptr time_raster_sink_f::make(double samp_rate, double rows, double cols, @@ -82,7 +83,7 @@ namespace gr { d_tmpflt = (float*)volk_malloc(d_icols*sizeof(float), volk_get_alignment()); memset(d_tmpflt, 0, d_icols*sizeof(float)); - + for(int i = 0; i < d_nconnections; i++) { d_residbufs.push_back((double*)volk_malloc(d_icols*sizeof(double), volk_get_alignment())); @@ -121,6 +122,8 @@ namespace gr { d_qApplication = qApp; } else { + std::string style = prefs::singleton()->get_string("qtgui", "style", "raster"); + QApplication::setGraphicsSystem(QString(style.c_str())); d_qApplication = new QApplication(d_argc, &d_argv); } @@ -413,7 +416,7 @@ namespace gr { volk_32f_convert_64f_u(&d_residbufs[n][d_index], d_tmpflt, resid); } - + // Update the plot if its time if(gr::high_res_timer_now() - d_last_time > d_update_time) { d_last_time = gr::high_res_timer_now(); diff --git a/gr-qtgui/lib/time_sink_c_impl.cc b/gr-qtgui/lib/time_sink_c_impl.cc index f24657830f..6cd314697c 100644 --- a/gr-qtgui/lib/time_sink_c_impl.cc +++ b/gr-qtgui/lib/time_sink_c_impl.cc @@ -26,6 +26,7 @@ #include "time_sink_c_impl.h" #include <gnuradio/io_signature.h> +#include <gnuradio/prefs.h> #include <string.h> #include <volk/volk.h> #include <gnuradio/fft/fft.h> @@ -33,7 +34,7 @@ namespace gr { namespace qtgui { - + time_sink_c::sptr time_sink_c::make(int size, double samp_rate, const std::string &name, @@ -112,6 +113,8 @@ namespace gr { d_qApplication = qApp; } else { + std::string style = prefs::singleton()->get_string("qtgui", "style", "raster"); + QApplication::setGraphicsSystem(QString(style.c_str())); d_qApplication = new QApplication(d_argc, &d_argv); } @@ -292,8 +295,8 @@ namespace gr { if(newsize != d_size) { gr::thread::scoped_lock lock(d_mutex); - // Set new size and reset buffer index - // (throws away any currently held data, but who cares?) + // Set new size and reset buffer index + // (throws away any currently held data, but who cares?) d_size = newsize; d_buffer_size = 2*d_size; diff --git a/gr-qtgui/lib/time_sink_f_impl.cc b/gr-qtgui/lib/time_sink_f_impl.cc index 596a416541..7bbda19d13 100644 --- a/gr-qtgui/lib/time_sink_f_impl.cc +++ b/gr-qtgui/lib/time_sink_f_impl.cc @@ -28,6 +28,7 @@ #include <gnuradio/io_signature.h> #include <gnuradio/block_detail.h> #include <gnuradio/buffer.h> +#include <gnuradio/prefs.h> #include <string.h> #include <volk/volk.h> #include <gnuradio/fft/fft.h> @@ -35,7 +36,7 @@ namespace gr { namespace qtgui { - + time_sink_f::sptr time_sink_f::make(int size, double samp_rate, const std::string &name, @@ -114,6 +115,8 @@ namespace gr { d_qApplication = qApp; } else { + std::string style = prefs::singleton()->get_string("qtgui", "style", "raster"); + QApplication::setGraphicsSystem(QString(style.c_str())); d_qApplication = new QApplication(d_argc, &d_argv); } @@ -294,8 +297,8 @@ namespace gr { if(newsize != d_size) { gr::thread::scoped_lock lock(d_mutex); - // Set new size and reset buffer index - // (throws away any currently held data, but who cares?) + // Set new size and reset buffer index + // (throws away any currently held data, but who cares?) d_size = newsize; d_buffer_size = 2*d_size; diff --git a/gr-qtgui/lib/waterfall_sink_c_impl.cc b/gr-qtgui/lib/waterfall_sink_c_impl.cc index e3ac3cc8e2..54e4c9532a 100644 --- a/gr-qtgui/lib/waterfall_sink_c_impl.cc +++ b/gr-qtgui/lib/waterfall_sink_c_impl.cc @@ -26,6 +26,7 @@ #include "waterfall_sink_c_impl.h" #include <gnuradio/io_signature.h> +#include <gnuradio/prefs.h> #include <string.h> #include <volk/volk.h> #include <qwt_symbol.h> @@ -131,6 +132,8 @@ namespace gr { d_qApplication = qApp; } else { + std::string style = prefs::singleton()->get_string("qtgui", "style", "raster"); + QApplication::setGraphicsSystem(QString(style.c_str())); d_qApplication = new QApplication(d_argc, &d_argv); } @@ -332,7 +335,6 @@ namespace gr { volk_32fc_s32f_x2_power_spectral_density_32f(data_out, d_fft->get_outbuf(), size, 1.0, size); - // Perform shift operation unsigned int len = (unsigned int)(floor(size/2.0)); float *tmp = (float*)malloc(sizeof(float)*len); diff --git a/gr-qtgui/lib/waterfall_sink_f_impl.cc b/gr-qtgui/lib/waterfall_sink_f_impl.cc index 4164a1844f..9f7bb37f95 100644 --- a/gr-qtgui/lib/waterfall_sink_f_impl.cc +++ b/gr-qtgui/lib/waterfall_sink_f_impl.cc @@ -26,6 +26,7 @@ #include "waterfall_sink_f_impl.h" #include <gnuradio/io_signature.h> +#include <gnuradio/prefs.h> #include <string.h> #include <volk/volk.h> @@ -130,6 +131,8 @@ namespace gr { d_qApplication = qApp; } else { + std::string style = prefs::singleton()->get_string("qtgui", "style", "raster"); + QApplication::setGraphicsSystem(QString(style.c_str())); d_qApplication = new QApplication(d_argc, &d_argv); } diff --git a/grc/python/flow_graph.tmpl b/grc/python/flow_graph.tmpl index fe1155bb50..7c6998ab73 100644 --- a/grc/python/flow_graph.tmpl +++ b/grc/python/flow_graph.tmpl @@ -315,6 +315,7 @@ if __name__ == '__main__': tb.Wait() #end if #elif $generate_options == 'qt_gui' + Qt.QApplication.setGraphicsSystem(gr.prefs().get_string('qtgui','style','raster')) qapp = Qt.QApplication(sys.argv) tb = $(class_name)($(', '.join($params_eq_list))) #if $flow_graph.get_option('run') diff --git a/volk/apps/volk_profile.cc b/volk/apps/volk_profile.cc index 3986cb6e62..97b0a5b7a8 100644 --- a/volk/apps/volk_profile.cc +++ b/volk/apps/volk_profile.cc @@ -16,147 +16,175 @@ namespace fs = boost::filesystem; int main(int argc, char *argv[]) { + // Adding program options boost::program_options::options_description desc("Options"); desc.add_options() ("help,h", "Print help messages") - ("benchmark,b", boost::program_options::value<bool>()->default_value( false ), "run all kernels (benchmark mode)"); - //("benchmark,b", boost::program_options::value<bool>(&benchmark_mode)->default_value( false ), "run all kernels (benchmark mode)"); + ("benchmark,b", + boost::program_options::value<bool>()->default_value( false ) + ->implicit_value( true ), + "Run all kernels (benchmark mode)") + ("tests-regex,R", + boost::program_options::value<std::string>(), + "Run tests matching regular expression.") + ; + + // Handle the options that were given boost::program_options::variables_map vm; - bool benchmark_mode = false; + bool benchmark_mode; + std::string kernel_regex; + bool store_results = true; try { - boost::program_options::store(boost::program_options::parse_command_line(argc, argv, desc), vm); + boost::program_options::store(boost::program_options::parse_command_line(argc, argv, desc), vm); boost::program_options::notify(vm); benchmark_mode = vm.count("benchmark")?vm["benchmark"].as<bool>():false; + if ( vm.count("tests-regex" ) ) { + kernel_regex = vm["tests-regex"].as<std::string>(); + store_results = false; + std::cout << "Warning: using a regexp will not save results to a config" << std::endl; + } + else { + kernel_regex = ".*"; + store_results = true; + } } catch (boost::program_options::error& error) { std::cerr << "Error: " << error.what() << std::endl << std::endl; std::cerr << desc << std::endl; return 1; } /** --help option - */ - if ( vm.count("help") ) - { - std::cout << "Basic Command Line Parameter App" << std::endl - << desc << std::endl; - return 0; - } +*/ + if ( vm.count("help") ) + { + std::cout << "The VOLK profiler." << std::endl + << desc << std::endl; + return 0; + } + // Run tests std::vector<std::string> results; - //VOLK_PROFILE(volk_16i_x5_add_quad_16i_x4, 1e-4, 2046, 10000, &results, benchmark_mode); - //VOLK_PROFILE(volk_16i_branch_4_state_8, 1e-4, 2046, 10000, &results, benchmark_mode); - VOLK_PUPPET_PROFILE(volk_32fc_s32fc_rotatorpuppet_32fc, volk_32fc_s32fc_x2_rotator_32fc, 1e-2, (lv_32fc_t)lv_cmake(0.953939201, 0.3), 20462, 10000, &results, benchmark_mode); - VOLK_PROFILE(volk_16ic_s32f_deinterleave_real_32f, 1e-5, 32768.0, 204602, 10000, &results, benchmark_mode); - VOLK_PROFILE(volk_16ic_deinterleave_real_8i, 0, 0, 204602, 10000, &results, benchmark_mode); - VOLK_PROFILE(volk_16ic_deinterleave_16i_x2, 0, 0, 204602, 10000, &results, benchmark_mode); - VOLK_PROFILE(volk_16ic_s32f_deinterleave_32f_x2, 1e-4, 32768.0, 204602, 1000, &results, benchmark_mode); - VOLK_PROFILE(volk_16ic_deinterleave_real_16i, 0, 0, 204602, 10000, &results, benchmark_mode); - VOLK_PROFILE(volk_16ic_magnitude_16i, 1, 0, 204602, 100, &results, benchmark_mode); - VOLK_PROFILE(volk_16ic_s32f_magnitude_32f, 1e-5, 32768.0, 204602, 1000, &results, benchmark_mode); - VOLK_PROFILE(volk_16i_s32f_convert_32f, 1e-4, 32768.0, 204602, 10000, &results, benchmark_mode); - VOLK_PROFILE(volk_16i_convert_8i, 0, 0, 204602, 10000, &results, benchmark_mode); - //VOLK_PROFILE(volk_16i_max_star_16i, 0, 0, 204602, 10000, &results, benchmark_mode); - //VOLK_PROFILE(volk_16i_max_star_horizontal_16i, 0, 0, 204602, 10000, &results, benchmark_mode); - //VOLK_PROFILE(volk_16i_permute_and_scalar_add, 1e-4, 0, 2046, 10000, &results, benchmark_mode); - //VOLK_PROFILE(volk_16i_x4_quad_max_star_16i, 1e-4, 0, 2046, 10000, &results, benchmark_mode); - VOLK_PROFILE(volk_16u_byteswap, 0, 0, 204602, 10000, &results, benchmark_mode); - VOLK_PROFILE(volk_16i_32fc_dot_prod_32fc, 1e-4, 0, 204602, 10000, &results, benchmark_mode); - VOLK_PROFILE(volk_32f_accumulator_s32f, 1e-4, 0, 204602, 10000, &results, benchmark_mode); - VOLK_PROFILE(volk_32f_x2_add_32f, 1e-4, 0, 204602, 10000, &results, benchmark_mode); - VOLK_PROFILE(volk_32fc_32f_multiply_32fc, 1e-4, 0, 204602, 1000, &results, benchmark_mode); - VOLK_PROFILE(volk_32fc_s32f_power_32fc, 1e-4, 0, 204602, 50, &results, benchmark_mode); - VOLK_PROFILE(volk_32f_s32f_calc_spectral_noise_floor_32f, 1e-4, 20.0, 204602, 1000, &results, benchmark_mode); - VOLK_PROFILE(volk_32fc_s32f_atan2_32f, 1e-4, 10.0, 204602, 100, &results, benchmark_mode); - //VOLK_PROFILE(volk_32fc_x2_conjugate_dot_prod_32fc, 1e-4, 0, 2046, 10000, &results, benchmark_mode); - VOLK_PROFILE(volk_32fc_x2_conjugate_dot_prod_32fc, 1e-4, 0, 204602, 10000, &results, benchmark_mode); - VOLK_PROFILE(volk_32fc_deinterleave_32f_x2, 1e-4, 0, 204602, 1000, &results, benchmark_mode); - VOLK_PROFILE(volk_32fc_deinterleave_64f_x2, 1e-4, 0, 204602, 1000, &results, benchmark_mode); - VOLK_PROFILE(volk_32fc_s32f_deinterleave_real_16i, 0, 32768, 204602, 10000, &results, benchmark_mode); - VOLK_PROFILE(volk_32fc_deinterleave_imag_32f, 1e-4, 0, 204602, 5000, &results, benchmark_mode); - VOLK_PROFILE(volk_32fc_deinterleave_real_32f, 1e-4, 0, 204602, 5000, &results, benchmark_mode); - VOLK_PROFILE(volk_32fc_deinterleave_real_64f, 1e-4, 0, 204602, 1000, &results, benchmark_mode); - VOLK_PROFILE(volk_32fc_x2_dot_prod_32fc, 1e-4, 0, 204602, 10000, &results, benchmark_mode); - VOLK_PROFILE(volk_32fc_32f_dot_prod_32fc, 1e-4, 0, 204602, 10000, &results, benchmark_mode); - VOLK_PROFILE(volk_32fc_index_max_16u, 3, 0, 204602, 10000, &results, benchmark_mode); - VOLK_PROFILE(volk_32fc_s32f_magnitude_16i, 1, 32768, 204602, 100, &results, benchmark_mode); - VOLK_PROFILE(volk_32fc_magnitude_32f, 1e-4, 0, 204602, 1000, &results, benchmark_mode); - VOLK_PROFILE(volk_32fc_magnitude_squared_32f, 1e-4, 0, 204602, 1000, &results, benchmark_mode); - VOLK_PROFILE(volk_32fc_x2_multiply_32fc, 1e-4, 0, 204602, 1000, &results, benchmark_mode); - VOLK_PROFILE(volk_32fc_x2_multiply_conjugate_32fc, 1e-4, 0, 204602, 1000, &results, benchmark_mode); - VOLK_PROFILE(volk_32fc_conjugate_32fc, 1e-4, 0, 204602, 1000, &results, benchmark_mode); - VOLK_PROFILE(volk_32f_s32f_convert_16i, 1, 32768, 204602, 10000, &results, benchmark_mode); - VOLK_PROFILE(volk_32f_s32f_convert_32i, 1, 1<<31, 204602, 10000, &results, benchmark_mode); - VOLK_PROFILE(volk_32f_convert_64f, 1e-4, 0, 204602, 10000, &results, benchmark_mode); - VOLK_PROFILE(volk_32f_s32f_convert_8i, 1, 128, 204602, 10000, &results, benchmark_mode); - //VOLK_PROFILE(volk_32fc_s32f_x2_power_spectral_density_32f, 1e-4, 2046, 10000, &results, benchmark_mode); - VOLK_PROFILE(volk_32fc_s32f_power_spectrum_32f, 1e-4, 0, 20462, 100, &results, benchmark_mode); - VOLK_PROFILE(volk_32fc_x2_square_dist_32f, 1e-4, 0, 204602, 10000, &results, benchmark_mode); - VOLK_PROFILE(volk_32fc_x2_s32f_square_dist_scalar_mult_32f, 1e-4, 10, 204602, 10000, &results, benchmark_mode); - VOLK_PROFILE(volk_32f_x2_divide_32f, 1e-4, 0, 204602, 2000, &results, benchmark_mode); - VOLK_PROFILE(volk_32f_x2_dot_prod_32f, 1e-4, 0, 204602, 5000, &results, benchmark_mode); - VOLK_PROFILE(volk_32f_x2_dot_prod_16i, 1e-4, 0, 204602, 5000, &results, benchmark_mode); - //VOLK_PROFILE(volk_32f_s32f_32f_fm_detect_32f, 1e-4, 2046, 10000, &results, benchmark_mode); - VOLK_PROFILE(volk_32f_index_max_16u, 3, 0, 204602, 5000, &results, benchmark_mode); - VOLK_PROFILE(volk_32f_x2_s32f_interleave_16ic, 1, 32768, 204602, 3000, &results, benchmark_mode); - VOLK_PROFILE(volk_32f_x2_interleave_32fc, 0, 0, 204602, 5000, &results, benchmark_mode); - VOLK_PROFILE(volk_32f_x2_max_32f, 1e-4, 0, 204602, 2000, &results, benchmark_mode); - VOLK_PROFILE(volk_32f_x2_min_32f, 1e-4, 0, 204602, 2000, &results, benchmark_mode); - VOLK_PROFILE(volk_32f_x2_multiply_32f, 1e-4, 0, 204602, 10000, &results, benchmark_mode); - VOLK_PROFILE(volk_32f_s32f_normalize, 1e-4, 100, 204602, 10000, &results, benchmark_mode); - VOLK_PROFILE(volk_32f_s32f_power_32f, 1e-4, 4, 204602, 100, &results, benchmark_mode); - VOLK_PROFILE(volk_32f_sqrt_32f, 1e-4, 0, 204602, 100, &results, benchmark_mode); - VOLK_PROFILE(volk_32f_s32f_stddev_32f, 1e-4, 100, 204602, 3000, &results, benchmark_mode); - VOLK_PROFILE(volk_32f_stddev_and_mean_32f_x2, 1e-4, 0, 204602, 3000, &results, benchmark_mode); - VOLK_PROFILE(volk_32f_x2_subtract_32f, 1e-4, 0, 204602, 5000, &results, benchmark_mode); - VOLK_PROFILE(volk_32f_x3_sum_of_poly_32f, 1e-2, 0, 204602, 5000, &results, benchmark_mode); - VOLK_PROFILE(volk_32i_x2_and_32i, 0, 0, 204602, 10000, &results, benchmark_mode); - VOLK_PROFILE(volk_32i_s32f_convert_32f, 1e-4, 100, 204602, 10000, &results, benchmark_mode); - VOLK_PROFILE(volk_32i_x2_or_32i, 0, 0, 204602, 10000, &results, benchmark_mode); - VOLK_PROFILE(volk_32u_byteswap, 0, 0, 204602, 2000, &results, benchmark_mode); - //VOLK_PROFILE(volk_32u_popcnt, 0, 0, 2046, 10000, &results, benchmark_mode); - VOLK_PROFILE(volk_64f_convert_32f, 1e-4, 0, 204602, 10000, &results, benchmark_mode); - VOLK_PROFILE(volk_64f_x2_max_64f, 1e-4, 0, 204602, 1000, &results, benchmark_mode); - VOLK_PROFILE(volk_64f_x2_min_64f, 1e-4, 0, 204602, 1000, &results, benchmark_mode); - VOLK_PROFILE(volk_64u_byteswap, 0, 0, 204602, 1000, &results, benchmark_mode); - //VOLK_PROFILE(volk_64u_popcnt, 0, 0, 2046, 10000, &results, benchmark_mode); - VOLK_PROFILE(volk_8ic_deinterleave_16i_x2, 0, 0, 204602, 3000, &results, benchmark_mode); - VOLK_PROFILE(volk_8ic_s32f_deinterleave_32f_x2, 1e-4, 100, 204602, 3000, &results, benchmark_mode); - VOLK_PROFILE(volk_8ic_deinterleave_real_16i, 0, 256, 204602, 3000, &results, benchmark_mode); - VOLK_PROFILE(volk_8ic_s32f_deinterleave_real_32f, 1e-4, 100, 204602, 3000, &results, benchmark_mode); - VOLK_PROFILE(volk_8ic_deinterleave_real_8i, 0, 0, 204602, 10000, &results, benchmark_mode); - VOLK_PROFILE(volk_8ic_x2_multiply_conjugate_16ic, 0, 0, 204602, 400, &results, benchmark_mode); - VOLK_PROFILE(volk_8ic_x2_s32f_multiply_conjugate_32fc, 1e-4, 100, 204602, 400, &results, benchmark_mode); - VOLK_PROFILE(volk_8i_convert_16i, 0, 0, 204602, 20000, &results, benchmark_mode); - VOLK_PROFILE(volk_8i_s32f_convert_32f, 1e-4, 100, 204602, 2000, &results, benchmark_mode); - //VOLK_PROFILE(volk_32fc_s32fc_multiply_32fc, 1e-4, lv_32fc_t(1.0, 0.5), 204602, 1000, &results, benchmark_mode); - VOLK_PROFILE(volk_32fc_s32fc_multiply_32fc, 1e-4, 0, 204602, 1000, &results, benchmark_mode); - VOLK_PROFILE(volk_32f_s32f_multiply_32f, 1e-4, 1.0, 204602, 10000, &results, benchmark_mode); + //VOLK_PROFILE(volk_16i_x5_add_quad_16i_x4, 1e-4, 2046, 10000, &results, benchmark_mode, kernel_regex); + //VOLK_PROFILE(volk_16i_branch_4_state_8, 1e-4, 2046, 10000, &results, benchmark_mode, kernel_regex); + VOLK_PUPPET_PROFILE(volk_32fc_s32fc_rotatorpuppet_32fc, volk_32fc_s32fc_x2_rotator_32fc, 1e-2, (lv_32fc_t)lv_cmake(0.953939201, 0.3), 20462, 10000, &results, benchmark_mode, kernel_regex); + VOLK_PROFILE(volk_16ic_s32f_deinterleave_real_32f, 1e-5, 32768.0, 204602, 10000, &results, benchmark_mode, kernel_regex); + VOLK_PROFILE(volk_16ic_deinterleave_real_8i, 0, 0, 204602, 10000, &results, benchmark_mode, kernel_regex); + VOLK_PROFILE(volk_16ic_deinterleave_16i_x2, 0, 0, 204602, 10000, &results, benchmark_mode, kernel_regex); + VOLK_PROFILE(volk_16ic_s32f_deinterleave_32f_x2, 1e-4, 32768.0, 204602, 1000, &results, benchmark_mode, kernel_regex); + VOLK_PROFILE(volk_16ic_deinterleave_real_16i, 0, 0, 204602, 10000, &results, benchmark_mode, kernel_regex); + VOLK_PROFILE(volk_16ic_magnitude_16i, 1, 0, 204602, 100, &results, benchmark_mode, kernel_regex); + VOLK_PROFILE(volk_16ic_s32f_magnitude_32f, 1e-5, 32768.0, 204602, 1000, &results, benchmark_mode, kernel_regex); + VOLK_PROFILE(volk_16i_s32f_convert_32f, 1e-4, 32768.0, 204602, 10000, &results, benchmark_mode, kernel_regex); + VOLK_PROFILE(volk_16i_convert_8i, 0, 0, 204602, 10000, &results, benchmark_mode, kernel_regex); + //VOLK_PROFILE(volk_16i_max_star_16i, 0, 0, 204602, 10000, &results, benchmark_mode, kernel_regex); + //VOLK_PROFILE(volk_16i_max_star_horizontal_16i, 0, 0, 204602, 10000, &results, benchmark_mode, kernel_regex); + //VOLK_PROFILE(volk_16i_permute_and_scalar_add, 1e-4, 0, 2046, 10000, &results, benchmark_mode, kernel_regex); + //VOLK_PROFILE(volk_16i_x4_quad_max_star_16i, 1e-4, 0, 2046, 10000, &results, benchmark_mode, kernel_regex); + VOLK_PROFILE(volk_16u_byteswap, 0, 0, 204602, 10000, &results, benchmark_mode, kernel_regex); + VOLK_PROFILE(volk_16i_32fc_dot_prod_32fc, 1e-4, 0, 204602, 10000, &results, benchmark_mode, kernel_regex); + VOLK_PROFILE(volk_32f_accumulator_s32f, 1e-4, 0, 204602, 10000, &results, benchmark_mode, kernel_regex); + VOLK_PROFILE(volk_32f_x2_add_32f, 1e-4, 0, 204602, 10000, &results, benchmark_mode, kernel_regex); + VOLK_PROFILE(volk_32fc_32f_multiply_32fc, 1e-4, 0, 204602, 1000, &results, benchmark_mode, kernel_regex); + VOLK_PROFILE(volk_32fc_s32f_power_32fc, 1e-4, 0, 204602, 50, &results, benchmark_mode, kernel_regex); + VOLK_PROFILE(volk_32f_s32f_calc_spectral_noise_floor_32f, 1e-4, 20.0, 204602, 1000, &results, benchmark_mode, kernel_regex); + VOLK_PROFILE(volk_32fc_s32f_atan2_32f, 1e-4, 10.0, 204602, 100, &results, benchmark_mode, kernel_regex); + //VOLK_PROFILE(volk_32fc_x2_conjugate_dot_prod_32fc, 1e-4, 0, 2046, 10000, &results, benchmark_mode, kernel_regex); + VOLK_PROFILE(volk_32fc_x2_conjugate_dot_prod_32fc, 1e-4, 0, 204602, 10000, &results, benchmark_mode, kernel_regex); + VOLK_PROFILE(volk_32fc_deinterleave_32f_x2, 1e-4, 0, 204602, 1000, &results, benchmark_mode, kernel_regex); + VOLK_PROFILE(volk_32fc_deinterleave_64f_x2, 1e-4, 0, 204602, 1000, &results, benchmark_mode, kernel_regex); + VOLK_PROFILE(volk_32fc_s32f_deinterleave_real_16i, 0, 32768, 204602, 10000, &results, benchmark_mode, kernel_regex); + VOLK_PROFILE(volk_32fc_deinterleave_imag_32f, 1e-4, 0, 204602, 5000, &results, benchmark_mode, kernel_regex); + VOLK_PROFILE(volk_32fc_deinterleave_real_32f, 1e-4, 0, 204602, 5000, &results, benchmark_mode, kernel_regex); + VOLK_PROFILE(volk_32fc_deinterleave_real_64f, 1e-4, 0, 204602, 1000, &results, benchmark_mode, kernel_regex); + VOLK_PROFILE(volk_32fc_x2_dot_prod_32fc, 1e-4, 0, 204602, 10000, &results, benchmark_mode, kernel_regex); + VOLK_PROFILE(volk_32fc_32f_dot_prod_32fc, 1e-4, 0, 204602, 10000, &results, benchmark_mode, kernel_regex); + VOLK_PROFILE(volk_32fc_index_max_16u, 3, 0, 204602, 10000, &results, benchmark_mode, kernel_regex); + VOLK_PROFILE(volk_32fc_s32f_magnitude_16i, 1, 32768, 204602, 100, &results, benchmark_mode, kernel_regex); + VOLK_PROFILE(volk_32fc_magnitude_32f, 1e-4, 0, 204602, 1000, &results, benchmark_mode, kernel_regex); + VOLK_PROFILE(volk_32fc_magnitude_squared_32f, 1e-4, 0, 204602, 1000, &results, benchmark_mode, kernel_regex); + VOLK_PROFILE(volk_32fc_x2_multiply_32fc, 1e-4, 0, 204602, 1000, &results, benchmark_mode, kernel_regex); + VOLK_PROFILE(volk_32fc_x2_multiply_conjugate_32fc, 1e-4, 0, 204602, 1000, &results, benchmark_mode, kernel_regex); + VOLK_PROFILE(volk_32fc_conjugate_32fc, 1e-4, 0, 204602, 1000, &results, benchmark_mode, kernel_regex); + VOLK_PROFILE(volk_32f_s32f_convert_16i, 1, 32768, 204602, 10000, &results, benchmark_mode, kernel_regex); + VOLK_PROFILE(volk_32f_s32f_convert_32i, 1, 1<<31, 204602, 10000, &results, benchmark_mode, kernel_regex); + VOLK_PROFILE(volk_32f_convert_64f, 1e-4, 0, 204602, 10000, &results, benchmark_mode, kernel_regex); + VOLK_PROFILE(volk_32f_s32f_convert_8i, 1, 128, 204602, 10000, &results, benchmark_mode, kernel_regex); + //VOLK_PROFILE(volk_32fc_s32f_x2_power_spectral_density_32f, 1e-4, 2046, 10000, &results, benchmark_mode, kernel_regex); + VOLK_PROFILE(volk_32fc_s32f_power_spectrum_32f, 1e-4, 0, 20462, 100, &results, benchmark_mode, kernel_regex); + VOLK_PROFILE(volk_32fc_x2_square_dist_32f, 1e-4, 0, 204602, 10000, &results, benchmark_mode, kernel_regex); + VOLK_PROFILE(volk_32fc_x2_s32f_square_dist_scalar_mult_32f, 1e-4, 10, 204602, 10000, &results, benchmark_mode, kernel_regex); + VOLK_PROFILE(volk_32f_x2_divide_32f, 1e-4, 0, 204602, 2000, &results, benchmark_mode, kernel_regex); + VOLK_PROFILE(volk_32f_x2_dot_prod_32f, 1e-4, 0, 204602, 5000, &results, benchmark_mode, kernel_regex); + VOLK_PROFILE(volk_32f_x2_dot_prod_16i, 1e-4, 0, 204602, 5000, &results, benchmark_mode, kernel_regex); + //VOLK_PROFILE(volk_32f_s32f_32f_fm_detect_32f, 1e-4, 2046, 10000, &results, benchmark_mode, kernel_regex); + VOLK_PROFILE(volk_32f_index_max_16u, 3, 0, 204602, 5000, &results, benchmark_mode, kernel_regex); + VOLK_PROFILE(volk_32f_x2_s32f_interleave_16ic, 1, 32768, 204602, 3000, &results, benchmark_mode, kernel_regex); + VOLK_PROFILE(volk_32f_x2_interleave_32fc, 0, 0, 204602, 5000, &results, benchmark_mode, kernel_regex); + VOLK_PROFILE(volk_32f_x2_max_32f, 1e-4, 0, 204602, 2000, &results, benchmark_mode, kernel_regex); + VOLK_PROFILE(volk_32f_x2_min_32f, 1e-4, 0, 204602, 2000, &results, benchmark_mode, kernel_regex); + VOLK_PROFILE(volk_32f_x2_multiply_32f, 1e-4, 0, 204602, 10000, &results, benchmark_mode, kernel_regex); + VOLK_PROFILE(volk_32f_s32f_normalize, 1e-4, 100, 204602, 10000, &results, benchmark_mode, kernel_regex); + VOLK_PROFILE(volk_32f_s32f_power_32f, 1e-4, 4, 204602, 100, &results, benchmark_mode, kernel_regex); + VOLK_PROFILE(volk_32f_sqrt_32f, 1e-4, 0, 204602, 100, &results, benchmark_mode, kernel_regex); + VOLK_PROFILE(volk_32f_s32f_stddev_32f, 1e-4, 100, 204602, 3000, &results, benchmark_mode, kernel_regex); + VOLK_PROFILE(volk_32f_stddev_and_mean_32f_x2, 1e-4, 0, 204602, 3000, &results, benchmark_mode, kernel_regex); + VOLK_PROFILE(volk_32f_x2_subtract_32f, 1e-4, 0, 204602, 5000, &results, benchmark_mode, kernel_regex); + VOLK_PROFILE(volk_32f_x3_sum_of_poly_32f, 1e-2, 0, 204602, 5000, &results, benchmark_mode, kernel_regex); + VOLK_PROFILE(volk_32i_x2_and_32i, 0, 0, 204602, 10000, &results, benchmark_mode, kernel_regex); + VOLK_PROFILE(volk_32i_s32f_convert_32f, 1e-4, 100, 204602, 10000, &results, benchmark_mode, kernel_regex); + VOLK_PROFILE(volk_32i_x2_or_32i, 0, 0, 204602, 10000, &results, benchmark_mode, kernel_regex); + VOLK_PROFILE(volk_32u_byteswap, 0, 0, 204602, 2000, &results, benchmark_mode, kernel_regex); + //VOLK_PROFILE(volk_32u_popcnt, 0, 0, 2046, 10000, &results, benchmark_mode, kernel_regex); + VOLK_PROFILE(volk_64f_convert_32f, 1e-4, 0, 204602, 10000, &results, benchmark_mode, kernel_regex); + VOLK_PROFILE(volk_64f_x2_max_64f, 1e-4, 0, 204602, 1000, &results, benchmark_mode, kernel_regex); + VOLK_PROFILE(volk_64f_x2_min_64f, 1e-4, 0, 204602, 1000, &results, benchmark_mode, kernel_regex); + VOLK_PROFILE(volk_64u_byteswap, 0, 0, 204602, 1000, &results, benchmark_mode, kernel_regex); + //VOLK_PROFILE(volk_64u_popcnt, 0, 0, 2046, 10000, &results, benchmark_mode, kernel_regex); + VOLK_PROFILE(volk_8ic_deinterleave_16i_x2, 0, 0, 204602, 3000, &results, benchmark_mode, kernel_regex); + VOLK_PROFILE(volk_8ic_s32f_deinterleave_32f_x2, 1e-4, 100, 204602, 3000, &results, benchmark_mode, kernel_regex); + VOLK_PROFILE(volk_8ic_deinterleave_real_16i, 0, 256, 204602, 3000, &results, benchmark_mode, kernel_regex); + VOLK_PROFILE(volk_8ic_s32f_deinterleave_real_32f, 1e-4, 100, 204602, 3000, &results, benchmark_mode, kernel_regex); + VOLK_PROFILE(volk_8ic_deinterleave_real_8i, 0, 0, 204602, 10000, &results, benchmark_mode, kernel_regex); + VOLK_PROFILE(volk_8ic_x2_multiply_conjugate_16ic, 0, 0, 204602, 400, &results, benchmark_mode, kernel_regex); + VOLK_PROFILE(volk_8ic_x2_s32f_multiply_conjugate_32fc, 1e-4, 100, 204602, 400, &results, benchmark_mode, kernel_regex); + VOLK_PROFILE(volk_8i_convert_16i, 0, 0, 204602, 20000, &results, benchmark_mode, kernel_regex); + VOLK_PROFILE(volk_8i_s32f_convert_32f, 1e-4, 100, 204602, 2000, &results, benchmark_mode, kernel_regex); + //VOLK_PROFILE(volk_32fc_s32fc_multiply_32fc, 1e-4, lv_32fc_t(1.0, 0.5), 204602, 1000, &results, benchmark_mode, kernel_regex); + VOLK_PROFILE(volk_32fc_s32fc_multiply_32fc, 1e-4, 0, 204602, 1000, &results, benchmark_mode, kernel_regex); + VOLK_PROFILE(volk_32f_s32f_multiply_32f, 1e-4, 1.0, 204602, 10000, &results, benchmark_mode, kernel_regex); - char path[1024]; - volk_get_config_path(path); + // Until we can update the config on a kernel by kernel basis + // do not overwrite volk_config when using a regex. + if(store_results) { + char path[1024]; + volk_get_config_path(path); - const fs::path config_path(path); + const fs::path config_path(path); - if (not fs::exists(config_path.branch_path())) - { - std::cout << "Creating " << config_path.branch_path() << "..." << std::endl; - fs::create_directories(config_path.branch_path()); - } + if (not fs::exists(config_path.branch_path())) + { + std::cout << "Creating " << config_path.branch_path() << "..." << std::endl; + fs::create_directories(config_path.branch_path()); + } - std::cout << "Writing " << config_path << "..." << std::endl; - std::ofstream config(config_path.string().c_str()); - if(!config.is_open()) { //either we don't have write access or we don't have the dir yet - std::cout << "Error opening file " << config_path << std::endl; - } + std::cout << "Writing " << config_path << "..." << std::endl; + std::ofstream config(config_path.string().c_str()); + if(!config.is_open()) { //either we don't have write access or we don't have the dir yet + std::cout << "Error opening file " << config_path << std::endl; + } - config << "\ -#this file is generated by volk_profile.\n\ -#the function name is followed by the preferred architecture.\n\ + config << "\ +#thi s file is generated by volk_profile.\n\ +#the function name is followed by the preferred architecture.\n\ "; - BOOST_FOREACH(std::string result, results) { - config << result << std::endl; + BOOST_FOREACH(std::string result, results) { + config << result << std::endl; + } + config.close(); + } + else { + std::cout << "Warning: config not generated" << std::endl; } - config.close(); } diff --git a/volk/kernels/volk/volk_16i_32fc_dot_prod_32fc.h b/volk/kernels/volk/volk_16i_32fc_dot_prod_32fc.h index 8bc1569f61..8c66892fd9 100644 --- a/volk/kernels/volk/volk_16i_32fc_dot_prod_32fc.h +++ b/volk/kernels/volk/volk_16i_32fc_dot_prod_32fc.h @@ -1,5 +1,5 @@ -#ifndef INCLUDED_volk_16i_32fc_dot_prod_32fc_a_H -#define INCLUDED_volk_16i_32fc_dot_prod_32fc_a_H +#ifndef INCLUDED_volk_16i_32fc_dot_prod_32fc_H +#define INCLUDED_volk_16i_32fc_dot_prod_32fc_H #include <volk/volk_common.h> #include<stdio.h> @@ -37,6 +37,90 @@ static inline void volk_16i_32fc_dot_prod_32fc_generic(lv_32fc_t* result, const #endif /*LV_HAVE_GENERIC*/ + +#if LV_HAVE_SSE && LV_HAVE_MMX + +static inline void volk_16i_32fc_dot_prod_32fc_u_sse( lv_32fc_t* result, const short* input, const lv_32fc_t* taps, unsigned int num_points) { + + unsigned int number = 0; + const unsigned int sixteenthPoints = num_points / 8; + + float res[2]; + float *realpt = &res[0], *imagpt = &res[1]; + const short* aPtr = input; + const float* bPtr = (float*)taps; + + __m64 m0, m1; + __m128 f0, f1, f2, f3; + __m128 a0Val, a1Val, a2Val, a3Val; + __m128 b0Val, b1Val, b2Val, b3Val; + __m128 c0Val, c1Val, c2Val, c3Val; + + __m128 dotProdVal0 = _mm_setzero_ps(); + __m128 dotProdVal1 = _mm_setzero_ps(); + __m128 dotProdVal2 = _mm_setzero_ps(); + __m128 dotProdVal3 = _mm_setzero_ps(); + + for(;number < sixteenthPoints; number++){ + + m0 = _mm_set_pi16(*(aPtr+3), *(aPtr+2), *(aPtr+1), *(aPtr+0)); + m1 = _mm_set_pi16(*(aPtr+7), *(aPtr+6), *(aPtr+5), *(aPtr+4)); + f0 = _mm_cvtpi16_ps(m0); + f1 = _mm_cvtpi16_ps(m0); + f2 = _mm_cvtpi16_ps(m1); + f3 = _mm_cvtpi16_ps(m1); + + a0Val = _mm_unpacklo_ps(f0, f1); + a1Val = _mm_unpackhi_ps(f0, f1); + a2Val = _mm_unpacklo_ps(f2, f3); + a3Val = _mm_unpackhi_ps(f2, f3); + + b0Val = _mm_loadu_ps(bPtr); + b1Val = _mm_loadu_ps(bPtr+4); + b2Val = _mm_loadu_ps(bPtr+8); + b3Val = _mm_loadu_ps(bPtr+12); + + c0Val = _mm_mul_ps(a0Val, b0Val); + c1Val = _mm_mul_ps(a1Val, b1Val); + c2Val = _mm_mul_ps(a2Val, b2Val); + c3Val = _mm_mul_ps(a3Val, b3Val); + + dotProdVal0 = _mm_add_ps(c0Val, dotProdVal0); + dotProdVal1 = _mm_add_ps(c1Val, dotProdVal1); + dotProdVal2 = _mm_add_ps(c2Val, dotProdVal2); + dotProdVal3 = _mm_add_ps(c3Val, dotProdVal3); + + aPtr += 8; + bPtr += 16; + } + + dotProdVal0 = _mm_add_ps(dotProdVal0, dotProdVal1); + dotProdVal0 = _mm_add_ps(dotProdVal0, dotProdVal2); + dotProdVal0 = _mm_add_ps(dotProdVal0, dotProdVal3); + + __VOLK_ATTR_ALIGNED(16) float dotProductVector[4]; + + _mm_store_ps(dotProductVector,dotProdVal0); // Store the results back into the dot product vector + + *realpt = dotProductVector[0]; + *imagpt = dotProductVector[1]; + *realpt += dotProductVector[2]; + *imagpt += dotProductVector[3]; + + number = sixteenthPoints*8; + for(;number < num_points; number++){ + *realpt += ((*aPtr) * (*bPtr++)); + *imagpt += ((*aPtr++) * (*bPtr++)); + } + + *result = *(lv_32fc_t*)(&res[0]); +} + +#endif /*LV_HAVE_SSE && LV_HAVE_MMX*/ + + + + #if LV_HAVE_SSE && LV_HAVE_MMX @@ -119,4 +203,4 @@ static inline void volk_16i_32fc_dot_prod_32fc_a_sse( lv_32fc_t* result, const #endif /*LV_HAVE_SSE && LV_HAVE_MMX*/ -#endif /*INCLUDED_volk_16i_32fc_dot_prod_32fc_a_H*/ +#endif /*INCLUDED_volk_16i_32fc_dot_prod_32fc_H*/ diff --git a/volk/kernels/volk/volk_32f_x2_dot_prod_16i.h b/volk/kernels/volk/volk_32f_x2_dot_prod_16i.h index 8fcc7deaed..b637f17777 100644 --- a/volk/kernels/volk/volk_32f_x2_dot_prod_16i.h +++ b/volk/kernels/volk/volk_32f_x2_dot_prod_16i.h @@ -1,5 +1,5 @@ -#ifndef INCLUDED_volk_32f_x2_dot_prod_16i_a_H -#define INCLUDED_volk_32f_x2_dot_prod_16i_a_H +#ifndef INCLUDED_volk_32f_x2_dot_prod_16i_H +#define INCLUDED_volk_32f_x2_dot_prod_16i_H #include <volk/volk_common.h> #include<stdio.h> @@ -27,7 +27,6 @@ static inline void volk_32f_x2_dot_prod_16i_generic(int16_t* result, const float #ifdef LV_HAVE_SSE - static inline void volk_32f_x2_dot_prod_16i_a_sse(int16_t* result, const float* input, const float* taps, unsigned int num_points) { unsigned int number = 0; @@ -90,9 +89,77 @@ static inline void volk_32f_x2_dot_prod_16i_a_sse(int16_t* result, const float* } *result = (short)dotProduct; +} + +#endif /*LV_HAVE_SSE*/ + + +#ifdef LV_HAVE_SSE + +static inline void volk_32f_x2_dot_prod_16i_u_sse(int16_t* result, const float* input, const float* taps, unsigned int num_points) { + + unsigned int number = 0; + const unsigned int sixteenthPoints = num_points / 16; + + float dotProduct = 0; + const float* aPtr = input; + const float* bPtr = taps; + + __m128 a0Val, a1Val, a2Val, a3Val; + __m128 b0Val, b1Val, b2Val, b3Val; + __m128 c0Val, c1Val, c2Val, c3Val; + + __m128 dotProdVal0 = _mm_setzero_ps(); + __m128 dotProdVal1 = _mm_setzero_ps(); + __m128 dotProdVal2 = _mm_setzero_ps(); + __m128 dotProdVal3 = _mm_setzero_ps(); + + for(;number < sixteenthPoints; number++){ + + a0Val = _mm_loadu_ps(aPtr); + a1Val = _mm_loadu_ps(aPtr+4); + a2Val = _mm_loadu_ps(aPtr+8); + a3Val = _mm_loadu_ps(aPtr+12); + b0Val = _mm_loadu_ps(bPtr); + b1Val = _mm_loadu_ps(bPtr+4); + b2Val = _mm_loadu_ps(bPtr+8); + b3Val = _mm_loadu_ps(bPtr+12); + + c0Val = _mm_mul_ps(a0Val, b0Val); + c1Val = _mm_mul_ps(a1Val, b1Val); + c2Val = _mm_mul_ps(a2Val, b2Val); + c3Val = _mm_mul_ps(a3Val, b3Val); + + dotProdVal0 = _mm_add_ps(c0Val, dotProdVal0); + dotProdVal1 = _mm_add_ps(c1Val, dotProdVal1); + dotProdVal2 = _mm_add_ps(c2Val, dotProdVal2); + dotProdVal3 = _mm_add_ps(c3Val, dotProdVal3); + + aPtr += 16; + bPtr += 16; + } + + dotProdVal0 = _mm_add_ps(dotProdVal0, dotProdVal1); + dotProdVal0 = _mm_add_ps(dotProdVal0, dotProdVal2); + dotProdVal0 = _mm_add_ps(dotProdVal0, dotProdVal3); + __VOLK_ATTR_ALIGNED(16) float dotProductVector[4]; + + _mm_store_ps(dotProductVector,dotProdVal0); // Store the results back into the dot product vector + + dotProduct = dotProductVector[0]; + dotProduct += dotProductVector[1]; + dotProduct += dotProductVector[2]; + dotProduct += dotProductVector[3]; + + number = sixteenthPoints*16; + for(;number < num_points; number++){ + dotProduct += ((*aPtr++) * (*bPtr++)); + } + + *result = (short)dotProduct; } #endif /*LV_HAVE_SSE*/ -#endif /*INCLUDED_volk_32f_x2_dot_prod_16i_a_H*/ +#endif /*INCLUDED_volk_32f_x2_dot_prod_16i_H*/ diff --git a/volk/kernels/volk/volk_32fc_32f_dot_prod_32fc.h b/volk/kernels/volk/volk_32fc_32f_dot_prod_32fc.h index 8341129fef..f567ede516 100644 --- a/volk/kernels/volk/volk_32fc_32f_dot_prod_32fc.h +++ b/volk/kernels/volk/volk_32fc_32f_dot_prod_32fc.h @@ -284,5 +284,81 @@ static inline void volk_32fc_32f_dot_prod_32fc_u_avx( lv_32fc_t* result, const l #endif /*LV_HAVE_AVX*/ +#ifdef LV_HAVE_SSE + +static inline void volk_32fc_32f_dot_prod_32fc_u_sse( lv_32fc_t* result, const lv_32fc_t* input, const float* taps, unsigned int num_points) { + + unsigned int number = 0; + const unsigned int sixteenthPoints = num_points / 8; + + float res[2]; + float *realpt = &res[0], *imagpt = &res[1]; + const float* aPtr = (float*)input; + const float* bPtr = taps; + + __m128 a0Val, a1Val, a2Val, a3Val; + __m128 b0Val, b1Val, b2Val, b3Val; + __m128 x0Val, x1Val, x2Val, x3Val; + __m128 c0Val, c1Val, c2Val, c3Val; + + __m128 dotProdVal0 = _mm_setzero_ps(); + __m128 dotProdVal1 = _mm_setzero_ps(); + __m128 dotProdVal2 = _mm_setzero_ps(); + __m128 dotProdVal3 = _mm_setzero_ps(); + + for(;number < sixteenthPoints; number++){ + + a0Val = _mm_loadu_ps(aPtr); + a1Val = _mm_loadu_ps(aPtr+4); + a2Val = _mm_loadu_ps(aPtr+8); + a3Val = _mm_loadu_ps(aPtr+12); + + x0Val = _mm_loadu_ps(bPtr); + x1Val = _mm_loadu_ps(bPtr); + x2Val = _mm_loadu_ps(bPtr+4); + x3Val = _mm_loadu_ps(bPtr+4); + b0Val = _mm_unpacklo_ps(x0Val, x1Val); + b1Val = _mm_unpackhi_ps(x0Val, x1Val); + b2Val = _mm_unpacklo_ps(x2Val, x3Val); + b3Val = _mm_unpackhi_ps(x2Val, x3Val); + + c0Val = _mm_mul_ps(a0Val, b0Val); + c1Val = _mm_mul_ps(a1Val, b1Val); + c2Val = _mm_mul_ps(a2Val, b2Val); + c3Val = _mm_mul_ps(a3Val, b3Val); + + dotProdVal0 = _mm_add_ps(c0Val, dotProdVal0); + dotProdVal1 = _mm_add_ps(c1Val, dotProdVal1); + dotProdVal2 = _mm_add_ps(c2Val, dotProdVal2); + dotProdVal3 = _mm_add_ps(c3Val, dotProdVal3); + + aPtr += 16; + bPtr += 8; + } + + dotProdVal0 = _mm_add_ps(dotProdVal0, dotProdVal1); + dotProdVal0 = _mm_add_ps(dotProdVal0, dotProdVal2); + dotProdVal0 = _mm_add_ps(dotProdVal0, dotProdVal3); + + __VOLK_ATTR_ALIGNED(16) float dotProductVector[4]; + + _mm_store_ps(dotProductVector,dotProdVal0); // Store the results back into the dot product vector + + *realpt = dotProductVector[0]; + *imagpt = dotProductVector[1]; + *realpt += dotProductVector[2]; + *imagpt += dotProductVector[3]; + + number = sixteenthPoints*8; + for(;number < num_points; number++){ + *realpt += ((*aPtr++) * (*bPtr)); + *imagpt += ((*aPtr++) * (*bPtr++)); + } + + *result = *(lv_32fc_t*)(&res[0]); +} + +#endif /*LV_HAVE_SSE*/ + #endif /*INCLUDED_volk_32fc_32f_dot_prod_32fc_H*/ diff --git a/volk/lib/qa_utils.cc b/volk/lib/qa_utils.cc index 8b95d7d6b0..8007fe75a6 100644 --- a/volk/lib/qa_utils.cc +++ b/volk/lib/qa_utils.cc @@ -3,6 +3,7 @@ #include <boost/foreach.hpp> #include <boost/assign/list_of.hpp> #include <boost/tokenizer.hpp> +#include <boost/xpressive/xpressive.hpp> #include <iostream> #include <vector> #include <list> @@ -322,8 +323,14 @@ bool run_volk_tests(volk_func_desc_t desc, int iter, std::vector<std::string> *best_arch_vector = 0, std::string puppet_master_name = "NULL", - bool benchmark_mode + bool benchmark_mode, + std::string kernel_regex ) { + boost::xpressive::sregex kernel_expression = boost::xpressive::sregex::compile(kernel_regex); + if( !boost::xpressive::regex_search(name, kernel_expression) ) { + // in this case we have a regex and are only looking to test one kernel + return false; + } std::cout << "RUN_VOLK_TESTS: " << name << "(" << vlen << "," << iter << ")" << std::endl; // The multiply and lv_force_cast_hf are work arounds for GNU Radio bugs 582 and 583 diff --git a/volk/lib/qa_utils.h b/volk/lib/qa_utils.h index 0ede962e41..fc1a0239eb 100644 --- a/volk/lib/qa_utils.h +++ b/volk/lib/qa_utils.h @@ -21,12 +21,12 @@ volk_type_t volk_type_from_string(std::string); float uniform(void); void random_floats(float *buf, unsigned n); -bool run_volk_tests(volk_func_desc_t, void(*)(), std::string, float, lv_32fc_t, int, int, std::vector<std::string> *, std::string, bool benchmark_mode=false); +bool run_volk_tests(volk_func_desc_t, void(*)(), std::string, float, lv_32fc_t, int, int, std::vector<std::string> *, std::string, bool benchmark_mode=false, std::string kernel_regex=""); #define VOLK_RUN_TESTS(func, tol, scalar, len, iter) BOOST_AUTO_TEST_CASE(func##_test) { BOOST_CHECK_EQUAL(run_volk_tests(func##_get_func_desc(), (void (*)())func##_manual, std::string(#func), tol, scalar, len, iter, 0, "NULL"), 0); } -#define VOLK_PROFILE(func, tol, scalar, len, iter, results, bnmode) run_volk_tests(func##_get_func_desc(), (void (*)())func##_manual, std::string(#func), tol, scalar, len, iter, results, "NULL", bnmode) -#define VOLK_PUPPET_PROFILE(func, puppet_master_func, tol, scalar, len, iter, results, bnmode) run_volk_tests(func##_get_func_desc(), (void (*)())func##_manual, std::string(#func), tol, scalar, len, iter, results, std::string(#puppet_master_func), bnmode) +#define VOLK_PROFILE(func, tol, scalar, len, iter, results, bnmode, kernel_regex) run_volk_tests(func##_get_func_desc(), (void (*)())func##_manual, std::string(#func), tol, scalar, len, iter, results, "NULL", bnmode, kernel_regex) +#define VOLK_PUPPET_PROFILE(func, puppet_master_func, tol, scalar, len, iter, results, bnmode, kernel_regex) run_volk_tests(func##_get_func_desc(), (void (*)())func##_manual, std::string(#func), tol, scalar, len, iter, results, std::string(#puppet_master_func), bnmode, kernel_regex) typedef void (*volk_fn_1arg)(void *, unsigned int, const char*); //one input, operate in place typedef void (*volk_fn_2arg)(void *, void *, unsigned int, const char*); typedef void (*volk_fn_3arg)(void *, void *, void *, unsigned int, const char*); diff --git a/volk/python/volk_modtool/volk_modtool_generate.py b/volk/python/volk_modtool/volk_modtool_generate.py index 80c2aed598..637d4892ae 100644 --- a/volk/python/volk_modtool/volk_modtool_generate.py +++ b/volk/python/volk_modtool/volk_modtool_generate.py @@ -20,6 +20,7 @@ # import os +import glob import sys import re import glob @@ -150,12 +151,24 @@ class volk_modtool: newrelpath = re.sub(oldvolk, 'volk_' + self.my_dict['name'], relpath); dest = os.path.join(self.my_dict['destination'], 'volk_' + self.my_dict['name'], os.path.dirname(newrelpath), newname); - - if not os.path.exists(os.path.dirname(dest)): os.makedirs(os.path.dirname(dest)) open(dest, 'w+').write(outstring); + # copy orc proto-kernels if they exist + for orcfile in glob.glob(inpath + '/orc/' + top + name + '*.orc'): + if os.path.isfile(orcfile): + instring = open(orcfile, 'r').read(); + outstring = re.sub(oldvolk, 'volk_' + self.my_dict['name'], instring); + newname = 'volk_' + self.my_dict['name'] + '_' + name + '.orc'; + relpath = os.path.relpath(orcfile, base); + newrelpath = re.sub(oldvolk, 'volk_' + self.my_dict['name'], relpath); + dest = os.path.join(self.my_dict['destination'], 'volk_' + self.my_dict['name'], os.path.dirname(newrelpath), newname); + if not os.path.exists(os.path.dirname(dest)): + os.makedirs(os.path.dirname(dest)); + open(dest, 'w+').write(outstring) + + def remove_kernel(self, name): basename = self.my_dict['name']; if len(basename) > 0: @@ -215,6 +228,13 @@ class volk_modtool: print "Removing kernel %s"%(kernel.pattern) if os.path.exists(infile): os.remove(infile); + # remove the orc proto-kernels if they exist. There are no puppets here + # so just need to glob for files matching kernel name + print glob.glob(inpath + '/orc/' + top + name + '*.orc'); + for orcfile in glob.glob(inpath + '/orc/' + top + name + '*.orc'): + print orcfile + if(os.path.exists(orcfile)): + os.remove(orcfile); def import_kernel(self, name, base): if not (base): |