diff options
-rw-r--r-- | gnuradio-runtime/lib/hier_block2_detail.cc | 40 | ||||
-rw-r--r-- | gnuradio-runtime/lib/hier_block2_detail.h | 1 | ||||
-rw-r--r-- | gr-digital/include/gnuradio/digital/pfb_clock_sync_ccf.h | 11 | ||||
-rw-r--r-- | gr-digital/include/gnuradio/digital/pfb_clock_sync_fff.h | 11 | ||||
-rw-r--r-- | gr-digital/lib/pfb_clock_sync_ccf_impl.cc | 24 | ||||
-rw-r--r-- | gr-digital/lib/pfb_clock_sync_ccf_impl.h | 5 | ||||
-rw-r--r-- | gr-digital/lib/pfb_clock_sync_fff_impl.cc | 23 | ||||
-rw-r--r-- | gr-digital/lib/pfb_clock_sync_fff_impl.h | 3 | ||||
-rwxr-xr-x | gr-digital/python/digital/qa_pfb_clock_sync.py | 104 | ||||
-rw-r--r-- | gr-qtgui/grc/qtgui_freq_sink_x.xml | 2 | ||||
-rw-r--r-- | grc/gui/Messages.py | 2 | ||||
-rw-r--r-- | grc/python/Block.py | 7 | ||||
-rw-r--r-- | grc/python/Generator.py | 49 | ||||
-rw-r--r-- | grc/python/epy_block_io.py | 12 |
14 files changed, 241 insertions, 53 deletions
diff --git a/gnuradio-runtime/lib/hier_block2_detail.cc b/gnuradio-runtime/lib/hier_block2_detail.cc index 60910d0dd9..0d0ddf55ba 100644 --- a/gnuradio-runtime/lib/hier_block2_detail.cc +++ b/gnuradio-runtime/lib/hier_block2_detail.cc @@ -328,11 +328,45 @@ namespace gr { } void + hier_block2_detail::refresh_io_signature() + { + int min_inputs = d_owner->input_signature()->min_streams(); + int max_inputs = d_owner->input_signature()->max_streams(); + int min_outputs = d_owner->output_signature()->min_streams(); + int max_outputs = d_owner->output_signature()->max_streams(); + + if(max_inputs == io_signature::IO_INFINITE || + max_outputs == io_signature::IO_INFINITE || + (min_inputs != max_inputs) ||(min_outputs != max_outputs) ) { + std::stringstream msg; + msg << "Hierarchical blocks do not yet support arbitrary or" + << " variable numbers of inputs or outputs (" << d_owner->name() << ")"; + throw std::runtime_error(msg.str()); + } + + // Check for # input change + if ((signed)d_inputs.size() != max_inputs) + { + d_inputs.resize(max_inputs); + } + + // Check for # output change + if ((signed)d_outputs.size() != max_outputs) + { + d_outputs.resize(max_outputs); + d_min_output_buffer.resize(max_outputs, 0); + d_max_output_buffer.resize(max_outputs, 0); + } + } + + void hier_block2_detail::connect_input(int my_port, int port, basic_block_sptr block) { std::stringstream msg; + refresh_io_signature(); + if(my_port < 0 || my_port >= (signed)d_inputs.size()) { msg << "input port " << my_port << " out of range for " << block; throw std::invalid_argument(msg.str()); @@ -356,6 +390,8 @@ namespace gr { { std::stringstream msg; + refresh_io_signature(); + if(my_port < 0 || my_port >= (signed)d_outputs.size()) { msg << "output port " << my_port << " out of range for " << block; throw std::invalid_argument(msg.str()); @@ -376,6 +412,8 @@ namespace gr { { std::stringstream msg; + refresh_io_signature(); + if(my_port < 0 || my_port >= (signed)d_inputs.size()) { msg << "input port number " << my_port << " out of range for " << block; throw std::invalid_argument(msg.str()); @@ -399,6 +437,8 @@ namespace gr { { std::stringstream msg; + refresh_io_signature(); + if(my_port < 0 || my_port >= (signed)d_outputs.size()) { msg << "output port number " << my_port << " out of range for " << block; throw std::invalid_argument(msg.str()); diff --git a/gnuradio-runtime/lib/hier_block2_detail.h b/gnuradio-runtime/lib/hier_block2_detail.h index 8c38c3754a..a5584fe92a 100644 --- a/gnuradio-runtime/lib/hier_block2_detail.h +++ b/gnuradio-runtime/lib/hier_block2_detail.h @@ -71,6 +71,7 @@ namespace gr { endpoint_vector_t d_outputs; // Single internal endpoint per external output basic_block_vector_t d_blocks; + void refresh_io_signature(); void connect_input(int my_port, int port, basic_block_sptr block); void connect_output(int my_port, int port, basic_block_sptr block); void disconnect_input(int my_port, int port, basic_block_sptr block); diff --git a/gr-digital/include/gnuradio/digital/pfb_clock_sync_ccf.h b/gr-digital/include/gnuradio/digital/pfb_clock_sync_ccf.h index 4d489d1534..118435289c 100644 --- a/gr-digital/include/gnuradio/digital/pfb_clock_sync_ccf.h +++ b/gr-digital/include/gnuradio/digital/pfb_clock_sync_ccf.h @@ -187,7 +187,16 @@ namespace gr { virtual void update_gains() = 0; /*! - * Resets the filterbank's filter taps with the new prototype filter + * Resets the filterbank's filter taps with the new prototype filter. + */ + virtual void update_taps(const std::vector<float> &taps) = 0; + + /*! + * Used to set the taps of the filters in the filterbank and + * differential filterbank. + * + * WARNING: this should not be used externally and will be moved + * to a private funtion in the next API. */ virtual void set_taps(const std::vector<float> &taps, std::vector< std::vector<float> > &ourtaps, diff --git a/gr-digital/include/gnuradio/digital/pfb_clock_sync_fff.h b/gr-digital/include/gnuradio/digital/pfb_clock_sync_fff.h index c3034579a5..b774ee49aa 100644 --- a/gr-digital/include/gnuradio/digital/pfb_clock_sync_fff.h +++ b/gr-digital/include/gnuradio/digital/pfb_clock_sync_fff.h @@ -187,7 +187,16 @@ namespace gr { virtual void update_gains() = 0; /*! - * Resets the filterbank's filter taps with the new prototype filter + * Resets the filterbank's filter taps with the new prototype filter. + */ + virtual void update_taps(const std::vector<float> &taps) = 0; + + /*! + * Used to set the taps of the filters in the filterbank and + * differential filterbank. + * + * WARNING: this should not be used externally and will be moved + * to a private funtion in the next API. */ virtual void set_taps(const std::vector<float> &taps, std::vector< std::vector<float> > &ourtaps, diff --git a/gr-digital/lib/pfb_clock_sync_ccf_impl.cc b/gr-digital/lib/pfb_clock_sync_ccf_impl.cc index c8e1221c90..314ff94b0b 100644 --- a/gr-digital/lib/pfb_clock_sync_ccf_impl.cc +++ b/gr-digital/lib/pfb_clock_sync_ccf_impl.cc @@ -134,6 +134,14 @@ namespace gr { ninput_items_required[i] = (noutput_items + history()) * (d_sps/d_osps); } + void + pfb_clock_sync_ccf_impl::update_taps(const std::vector<float> &taps) + { + d_updated_taps = taps; + d_updated = true; + } + + /******************************************************************* SET FUNCTIONS *******************************************************************/ @@ -279,8 +287,6 @@ namespace gr { // Make sure there is enough output space for d_osps outputs/input. set_output_multiple(d_osps); - - d_updated = true; } void @@ -397,6 +403,15 @@ namespace gr { gr_complex *in = (gr_complex *) input_items[0]; gr_complex *out = (gr_complex *) output_items[0]; + if(d_updated) { + std::vector<float> dtaps; + create_diff_taps(d_updated_taps, dtaps); + set_taps(d_updated_taps, d_taps, d_filters); + set_taps(dtaps, d_dtaps, d_diff_filters); + d_updated = false; + return 0; // history requirements may have changed. + } + float *err = NULL, *outrate = NULL, *outk = NULL; if(output_items.size() == 4) { err = (float *) output_items[1]; @@ -404,11 +419,6 @@ namespace gr { outk = (float*)output_items[3]; } - if(d_updated) { - d_updated = false; - return 0; // history requirements may have changed. - } - std::vector<tag_t> tags; get_tags_in_range(tags, 0, nitems_read(0), nitems_read(0)+d_sps*noutput_items, diff --git a/gr-digital/lib/pfb_clock_sync_ccf_impl.h b/gr-digital/lib/pfb_clock_sync_ccf_impl.h index 015e037275..535628189f 100644 --- a/gr-digital/lib/pfb_clock_sync_ccf_impl.h +++ b/gr-digital/lib/pfb_clock_sync_ccf_impl.h @@ -47,6 +47,7 @@ namespace gr { std::vector<kernel::fir_filter_ccf*> d_diff_filters; std::vector< std::vector<float> > d_taps; std::vector< std::vector<float> > d_dtaps; + std::vector<float> d_updated_taps; float d_k; float d_rate; @@ -73,9 +74,11 @@ namespace gr { void setup_rpc(); void update_gains(); - + void forecast(int noutput_items, gr_vector_int &ninput_items_required); + void update_taps(const std::vector<float> &taps); + void set_taps(const std::vector<float> &taps, std::vector< std::vector<float> > &ourtaps, std::vector<kernel::fir_filter_ccf*> &ourfilter); diff --git a/gr-digital/lib/pfb_clock_sync_fff_impl.cc b/gr-digital/lib/pfb_clock_sync_fff_impl.cc index beb6bf5579..09c72495a0 100644 --- a/gr-digital/lib/pfb_clock_sync_fff_impl.cc +++ b/gr-digital/lib/pfb_clock_sync_fff_impl.cc @@ -131,6 +131,13 @@ namespace gr { ninput_items_required[i] = (noutput_items + history()) * (d_sps/d_osps); } + void + pfb_clock_sync_fff_impl::update_taps(const std::vector<float> &taps) + { + d_updated_taps = taps; + d_updated = true; + } + /******************************************************************* SET FUNCTIONS *******************************************************************/ @@ -258,8 +265,6 @@ namespace gr { // Make sure there is enough output space for d_osps outputs/input. set_output_multiple(d_osps); - - d_updated = true; } void @@ -376,6 +381,15 @@ namespace gr { float *in = (float *) input_items[0]; float *out = (float *) output_items[0]; + if(d_updated) { + std::vector<float> dtaps; + create_diff_taps(d_updated_taps, dtaps); + set_taps(d_updated_taps, d_taps, d_filters); + set_taps(dtaps, d_dtaps, d_diff_filters); + d_updated = false; + return 0; // history requirements may have changed. + } + float *err = NULL, *outrate = NULL, *outk = NULL; if(output_items.size() == 4) { err = (float *) output_items[1]; @@ -383,11 +397,6 @@ namespace gr { outk = (float*)output_items[3]; } - if(d_updated) { - d_updated = false; - return 0; // history requirements may have changed. - } - int i = 0, count = 0; // produce output as long as we can and there are enough input samples diff --git a/gr-digital/lib/pfb_clock_sync_fff_impl.h b/gr-digital/lib/pfb_clock_sync_fff_impl.h index 2c88341c55..7bbbd995d1 100644 --- a/gr-digital/lib/pfb_clock_sync_fff_impl.h +++ b/gr-digital/lib/pfb_clock_sync_fff_impl.h @@ -47,6 +47,7 @@ namespace gr { std::vector<kernel::fir_filter_fff*> d_diff_filters; std::vector< std::vector<float> > d_taps; std::vector< std::vector<float> > d_dtaps; + std::vector<float> d_updated_taps; float d_k; float d_rate; @@ -74,6 +75,8 @@ namespace gr { void forecast(int noutput_items, gr_vector_int &ninput_items_required); + void update_taps(const std::vector<float> &taps); + void set_taps(const std::vector<float> &taps, std::vector< std::vector<float> > &ourtaps, std::vector<kernel::fir_filter_fff*> &ourfilter); diff --git a/gr-digital/python/digital/qa_pfb_clock_sync.py b/gr-digital/python/digital/qa_pfb_clock_sync.py index 3c8074b154..e16a99338e 100755 --- a/gr-digital/python/digital/qa_pfb_clock_sync.py +++ b/gr-digital/python/digital/qa_pfb_clock_sync.py @@ -1,27 +1,28 @@ #!/usr/bin/env python # # Copyright 2011,2013 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. -# +# import random import cmath +import time from gnuradio import gr, gr_unittest, filter, digital, blocks @@ -43,7 +44,7 @@ class test_pfb_clock_sync(gr_unittest.TestCase): init_phase = nfilts/2 max_rate_deviation = 0.5 osps = 1 - + ntaps = 11 * int(sps*nfilts) taps = filter.firdes.root_raised_cosine(nfilts, nfilts*sps, 1.0, excess_bw, ntaps) @@ -52,7 +53,7 @@ class test_pfb_clock_sync(gr_unittest.TestCase): nfilts, init_phase, max_rate_deviation, osps) - + data = 10000*[complex(1,0), complex(-1,0)] self.src = blocks.vector_source_c(data, False) @@ -69,7 +70,7 @@ class test_pfb_clock_sync(gr_unittest.TestCase): self.tb.connect(self.src, self.rrc_filter, self.test, self.snk) self.tb.run() - + expected_result = 10000*[complex(1,0), complex(-1,0)] dst_data = self.snk.data() @@ -82,7 +83,7 @@ class test_pfb_clock_sync(gr_unittest.TestCase): #for e,d in zip(expected_result, dst_data): # print e, d - + self.assertComplexTuplesAlmostEqual(expected_result, dst_data, 1) @@ -96,7 +97,7 @@ class test_pfb_clock_sync(gr_unittest.TestCase): init_phase = nfilts/2 max_rate_deviation = 0.5 osps = 1 - + ntaps = 11 * int(sps*nfilts) taps = filter.firdes.root_raised_cosine(nfilts, nfilts*sps, 1.0, excess_bw, ntaps) @@ -105,7 +106,7 @@ class test_pfb_clock_sync(gr_unittest.TestCase): nfilts, init_phase, max_rate_deviation, osps) - + data = 10000*[1, -1] self.src = blocks.vector_source_f(data, False) @@ -122,7 +123,7 @@ class test_pfb_clock_sync(gr_unittest.TestCase): self.tb.connect(self.src, self.rrc_filter, self.test, self.snk) self.tb.run() - + expected_result = 10000*[1, -1] dst_data = self.snk.data() @@ -135,9 +136,86 @@ class test_pfb_clock_sync(gr_unittest.TestCase): #for e,d in zip(expected_result, dst_data): # print e, d - + self.assertFloatTuplesAlmostEqual(expected_result, dst_data, 1) + def test03(self): + # Test resting of taps + excess_bw0 = 0.35 + excess_bw1 = 0.22 + + sps = 4 + loop_bw = cmath.pi/100.0 + nfilts = 32 + init_phase = nfilts/2 + max_rate_deviation = 0.5 + osps = 1 + + ntaps = 11 * int(sps*nfilts) + taps = filter.firdes.root_raised_cosine(nfilts, nfilts*sps, + 1.0, excess_bw0, ntaps) + + self.test = digital.pfb_clock_sync_ccf(sps, loop_bw, taps, + nfilts, init_phase, + max_rate_deviation, + osps) + + self.src = blocks.null_source(gr.sizeof_gr_complex) + self.snk = blocks.null_sink(gr.sizeof_gr_complex) + + self.tb.connect(self.src, self.test, self.snk) + self.tb.start() + time.sleep(0.1) + + taps = filter.firdes.root_raised_cosine(nfilts, nfilts*sps, + 1.0, excess_bw1, ntaps) + + self.test.update_taps(taps) + + self.tb.stop() + self.tb.wait() + + self.assertTrue(True) + + def test03_f(self): + # Test resting of taps + excess_bw0 = 0.35 + excess_bw1 = 0.22 + + sps = 4 + loop_bw = cmath.pi/100.0 + nfilts = 32 + init_phase = nfilts/2 + max_rate_deviation = 0.5 + osps = 1 + + ntaps = 11 * int(sps*nfilts) + taps = filter.firdes.root_raised_cosine(nfilts, nfilts*sps, + 1.0, excess_bw0, ntaps) + + self.test = digital.pfb_clock_sync_fff(sps, loop_bw, taps, + nfilts, init_phase, + max_rate_deviation, + osps) + + self.src = blocks.null_source(gr.sizeof_float) + self.snk = blocks.null_sink(gr.sizeof_float) + + self.tb.connect(self.src, self.test, self.snk) + self.tb.start() + time.sleep(0.1) + + taps = filter.firdes.root_raised_cosine(nfilts, nfilts*sps, + 1.0, excess_bw1, ntaps) + + self.test.update_taps(taps) + + self.tb.stop() + self.tb.wait() + + self.assertTrue(True) + + if __name__ == '__main__': gr_unittest.run(test_pfb_clock_sync, "test_pfb_clock_sync.xml") diff --git a/gr-qtgui/grc/qtgui_freq_sink_x.xml b/gr-qtgui/grc/qtgui_freq_sink_x.xml index 538ff3f3c8..d7c3139849 100644 --- a/gr-qtgui/grc/qtgui_freq_sink_x.xml +++ b/gr-qtgui/grc/qtgui_freq_sink_x.xml @@ -110,7 +110,7 @@ $(gui_hint()($win))</make> <key>fftsize</key> <value>1024</value> <type>int</type> - <hide>#if $type.t == 'message' then 'all' else 'all'#</hide> + <hide>#if $type.t == 'message' then 'all' else 'none'#</hide> </param> <param> diff --git a/grc/gui/Messages.py b/grc/gui/Messages.py index 32c6cf108e..551a8ce753 100644 --- a/grc/gui/Messages.py +++ b/grc/gui/Messages.py @@ -122,7 +122,7 @@ def send_fail_gen(error): def send_start_exec(file_path): - send('\nExecuting: %r\n' % file_path) + send('\nExecuting: %s\n' % file_path) def send_verbose_exec(verbose): diff --git a/grc/python/Block.py b/grc/python/Block.py index 239352de1f..f5c994dc05 100644 --- a/grc/python/Block.py +++ b/grc/python/Block.py @@ -272,7 +272,7 @@ class Block(_Block, _GUIBlock): self._make = '{}({})'.format(blk_io.cls, ', '.join( '{0}=${0}'.format(key) for key, _ in blk_io.params)) - params = dict() + params = {} for param in list(self._params): if hasattr(param, '__epy_param__'): params[param.get_key()] = param @@ -293,7 +293,7 @@ class Block(_Block, _GUIBlock): def update_ports(label, ports, port_specs, direction): ports_to_remove = list(ports) iter_ports = iter(ports) - ports_new = list() + ports_new = [] port_current = next(iter_ports, None) for key, port_type in port_specs: reuse_port = ( @@ -306,6 +306,9 @@ class Block(_Block, _GUIBlock): port, port_current = port_current, next(iter_ports, None) else: n = odict(dict(name=label + str(key), type=port_type, key=key)) + if port_type == 'message': + n['name'] = key + n['optional'] = '1' port = platform.Port(block=self, n=n, dir=direction) ports_new.append(port) # replace old port list with new one diff --git a/grc/python/Generator.py b/grc/python/Generator.py index d688beba15..56e3a6e78f 100644 --- a/grc/python/Generator.py +++ b/grc/python/Generator.py @@ -23,6 +23,7 @@ import subprocess import tempfile import shlex import codecs +import re # for shlex_quote from distutils.spawn import find_executable from Cheetah.Template import Template @@ -125,24 +126,30 @@ class TopBlockGenerator(object): Returns: a popen object """ - def args_to_string(args): - """Accounts for spaces in args""" - return ' '.join(repr(arg) if ' ' in arg else arg for arg in args) - run_command = self._flow_graph.get_option('run_command') - cmds = shlex.split(run_command.format(python=sys.executable, - filename=self.get_file_path())) + try: + run_command = run_command.format( + python=shlex_quote(sys.executable), + filename=shlex_quote(self.get_file_path())) + run_command_args = shlex.split(run_command) + except Exception as e: + raise ValueError("Can't parse run command {!r}: {}".format(run_command, e)) # when in no gui mode on linux, use a graphical terminal (looks nice) xterm_executable = find_executable(XTERM_EXECUTABLE) if self._generate_options == 'no_gui' and xterm_executable: - cmds = [xterm_executable, '-e', args_to_string(cmds)] + run_command_args = [xterm_executable, '-e', run_command] - Messages.send_start_exec(args_to_string(cmds)) - p = subprocess.Popen( - args=cmds, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, - shell=False, universal_newlines=True) - return p + # this does not reproduce a shell executable command string, if a graphical + # terminal is used. Passing run_command though shlex_quote would do it but + # it looks really ugly and confusing in the console panel. + Messages.send_start_exec(' '.join(run_command_args)) + + return subprocess.Popen( + args=run_command_args, + stdout=subprocess.PIPE, stderr=subprocess.STDOUT, + shell=False, universal_newlines=True + ) def _build_python_code_from_template(self): """ @@ -420,3 +427,21 @@ class QtHierBlockGenerator(HierBlockGenerator): "\n${gui_hint()($win)}" ) return n + + +########################################################### +# back-port from python3 +########################################################### +_find_unsafe = re.compile(r'[^\w@%+=:,./-]').search + + +def shlex_quote(s): + """Return a shell-escaped version of the string *s*.""" + if not s: + return "''" + if _find_unsafe(s) is None: + return s + + # use single quotes, and put single quotes into double quotes + # the string $'b is then quoted as '$'"'"'b' + return "'" + s.replace("'", "'\"'\"'") + "'" diff --git a/grc/python/epy_block_io.py b/grc/python/epy_block_io.py index 8d3ce1caa1..e089908a01 100644 --- a/grc/python/epy_block_io.py +++ b/grc/python/epy_block_io.py @@ -39,10 +39,8 @@ def _blk_class(source_code): raise ValueError("Can't interpret source code: " + str(e)) for var in ns.itervalues(): if inspect.isclass(var)and issubclass(var, gr.gateway.gateway_block): - break - else: - raise ValueError('No python block class found in code') - return var + return var + raise ValueError('No python block class found in code') def extract(cls): @@ -55,7 +53,7 @@ def extract(cls): cls_name = cls.__name__ if len(defaults) + 1 != len(spec.args): - raise ValueError("Need all default values") + raise ValueError("Need all __init__ arguments to have default values") try: instance = cls() @@ -66,9 +64,9 @@ def extract(cls): params = list(zip(spec.args[1:], defaults)) sinks = _ports(instance.in_sig(), - pmt.to_python(instance.message_ports_in())) + pmt.to_python(instance.message_ports_in())) sources = _ports(instance.out_sig(), - pmt.to_python(instance.message_ports_out())) + pmt.to_python(instance.message_ports_out())) return BlockIO(name, cls_name, params, sinks, sources, doc) |