diff options
Diffstat (limited to 'gnuradio-runtime/python')
-rw-r--r-- | gnuradio-runtime/python/gnuradio/eng_notation.py | 23 | ||||
-rw-r--r-- | gnuradio-runtime/python/gnuradio/gr/CMakeLists.txt | 5 | ||||
-rw-r--r-- | gnuradio-runtime/python/gnuradio/gr/__init__.py | 4 | ||||
-rw-r--r-- | gnuradio-runtime/python/gnuradio/gr/gateway.py | 161 | ||||
-rw-r--r-- | gnuradio-runtime/python/gnuradio/gr_unittest.py | 12 | ||||
-rw-r--r-- | gnuradio-runtime/python/pmt/CMakeLists.txt | 1 |
6 files changed, 162 insertions, 44 deletions
diff --git a/gnuradio-runtime/python/gnuradio/eng_notation.py b/gnuradio-runtime/python/gnuradio/eng_notation.py index 5a3a216ee4..391dc5838f 100644 --- a/gnuradio-runtime/python/gnuradio/eng_notation.py +++ b/gnuradio-runtime/python/gnuradio/eng_notation.py @@ -37,29 +37,30 @@ scale_factor['p'] = 1e-12 scale_factor['f'] = 1e-15 scale_factor['a'] = 1e-18 -def num_to_str (n): +def num_to_str (n, precision=6): '''Convert a number to a string in engineering notation. E.g., 5e-9 -> 5n''' m = abs(n) + format_spec = '%.' + repr(int(precision)) + 'g' if m >= 1e9: - return "%gG" % (n * 1e-9) + return '%sG' % float(format_spec % (n * 1e-9)) elif m >= 1e6: - return "%gM" % (n * 1e-6) + return '%sM' % float(format_spec % (n * 1e-6)) elif m >= 1e3: - return "%gk" % (n * 1e-3) + return '%sk' % float(format_spec % (n * 1e-3)) elif m >= 1: - return "%g" % (n) + return '%s' % float(format_spec % (n)) elif m >= 1e-3: - return "%gm" % (n * 1e3) + return '%sm' % float(format_spec % (n * 1e3)) elif m >= 1e-6: - return "%gu" % (n * 1e6) # where's that mu when you need it (unicode?) + return '%su' % float(format_spec % (n * 1e6)) elif m >= 1e-9: - return "%gn" % (n * 1e9) + return '%sn' % float(format_spec % (n * 1e9)) elif m >= 1e-12: - return "%gp" % (n * 1e12) + return '%sp' % float(format_spec % (n * 1e12)) elif m >= 1e-15: - return "%gf" % (n * 1e15) + return '%sf' % float(format_spec % (n * 1e15)) else: - return "%g" % (n) + return '%s' % float(format_spec % (n)) def str_to_num (value): diff --git a/gnuradio-runtime/python/gnuradio/gr/CMakeLists.txt b/gnuradio-runtime/python/gnuradio/gr/CMakeLists.txt index 7c82b1df35..1b535ef997 100644 --- a/gnuradio-runtime/python/gnuradio/gr/CMakeLists.txt +++ b/gnuradio-runtime/python/gnuradio/gr/CMakeLists.txt @@ -44,6 +44,11 @@ if(ENABLE_TESTING) set(GR_TEST_PYTHON_DIRS ${CMAKE_BINARY_DIR}/gruel/src/python ${CMAKE_BINARY_DIR}/gnuradio-runtime/python + ${CMAKE_BINARY_DIR}/gnuradio-runtime/swig + ${CMAKE_BINARY_DIR}/gr-blocks/swig + ${CMAKE_BINARY_DIR}/gr-fft/swig + ${CMAKE_BINARY_DIR}/gr-filter/swig + ${CMAKE_BINARY_DIR}/gr-analog/swig ) include(GrTest) file(GLOB py_qa_test_files "qa_*.py") diff --git a/gnuradio-runtime/python/gnuradio/gr/__init__.py b/gnuradio-runtime/python/gnuradio/gr/__init__.py index 72f54e9f42..6ec5074fff 100644 --- a/gnuradio-runtime/python/gnuradio/gr/__init__.py +++ b/gnuradio-runtime/python/gnuradio/gr/__init__.py @@ -1,5 +1,5 @@ # -# Copyright 2003-2012 Free Software Foundation, Inc. +# Copyright 2003-2012, 2018 Free Software Foundation, Inc. # # This file is part of GNU Radio # @@ -46,7 +46,7 @@ from .exceptions import * from .top_block import * from .hier_block2 import * from .tag_utils import * -from .gateway import basic_block, sync_block, decim_block, interp_block +from .gateway import basic_block, sync_block, decim_block, interp_block, py_io_signature # Force the preference database to be initialized prefs = prefs.singleton diff --git a/gnuradio-runtime/python/gnuradio/gr/gateway.py b/gnuradio-runtime/python/gnuradio/gr/gateway.py index 1acf900f84..4eaa8745ac 100644 --- a/gnuradio-runtime/python/gnuradio/gr/gateway.py +++ b/gnuradio-runtime/python/gnuradio/gr/gateway.py @@ -1,5 +1,5 @@ # -# Copyright 2011-2012 Free Software Foundation, Inc. +# Copyright 2011-2012, 2018 Free Software Foundation, Inc. # # This file is part of GNU Radio # @@ -83,35 +83,89 @@ class msg_handler(gr.feval_p): return 0 ######################################################################## +# io_signature for Python +######################################################################## +class py_io_signature(object): + """ + Describes the type/number of ports for block input or output. + """ + + # Minimum and maximum number of ports, and a list of numpy types. + def __init__(self, min_ports, max_ports, type_list): + """ + Args: + + min_ports (int): mininum number of connected ports. + + max_ports (int): maximum number of connected ports. -1 indicates + no limit. + + type_list (list[str]): numpy type names for each port. If the + number of connected ports is greater than the number of types + provided, the last type in the list is repeated. + """ + self.__min_ports = min_ports + self.__max_ports = max_ports + self.__types = tuple( numpy.dtype(t) for t in type_list ) + + def gr_io_signature(self): + """ + Make/return a gr.io_signature. A non-empty list of sizes is + required, even if there are no ports. + """ + return io_signaturev(self.__min_ports, self.__max_ports, + [t.itemsize for t in self.__types] or [0]) + + def port_types(self, nports): + """ + Return data types for the first nports ports. If nports is + smaller than the provided type list, return a truncated list. If + larger, fill with the last type. + """ + ntypes = len(self.__types) + if ntypes == 0: + return () + if nports <= ntypes: + return self.__types[:nports] + return self.__types + [self.__types[-1]]*(nports-ntypes) + + def __iter__(self): + """ + Return the iterator over the maximum ports type list. + """ + return iter(self.port_types(self.__max_ports)) + + def __hash__(self): + return hash((self.__min_ports, self.__max_ports, self.__types)) + +######################################################################## # The guts that make this into a gr block ######################################################################## class gateway_block(object): def __init__(self, name, in_sig, out_sig, work_type, factor): - #ensure that the sigs are iterable dtypes - def sig_to_dtype_sig(sig): - if sig is None: sig = () - return list(map(numpy.dtype, sig)) - self.__in_sig = sig_to_dtype_sig(in_sig) - self.__out_sig = sig_to_dtype_sig(out_sig) - - #cache the ranges to iterate when dispatching work - self.__in_indexes = list(range(len(self.__in_sig))) - self.__out_indexes = list(range(len(self.__out_sig))) + # Normalize the many Python ways of saying 'nothing' to '()' + in_sig = in_sig or () + out_sig = out_sig or () - #convert the signatures into gr.io_signatures - def sig_to_gr_io_sigv(sig): - if not len(sig): return io_signature(0, 0, 0) - return io_signaturev(len(sig), len(sig), [s.itemsize for s in sig]) - gr_in_sig = sig_to_gr_io_sigv(self.__in_sig) - gr_out_sig = sig_to_gr_io_sigv(self.__out_sig) + # Backward compatibility: array of type strings -> py_io_signature + if type(in_sig) is py_io_signature: + self.__in_sig = in_sig + else: + self.__in_sig = py_io_signature(len(in_sig), len(in_sig), in_sig) + if type(out_sig) is py_io_signature: + self.__out_sig = out_sig + else: + self.__out_sig = py_io_signature(len(out_sig), len(out_sig), out_sig) #create internal gateway block self.__handler = gateway_handler() self.__handler.init(self.__gr_block_handle) self.__gateway = block_gateway( - self.__handler, name, gr_in_sig, gr_out_sig, work_type, factor) + self.__handler, name, + self.__in_sig.gr_io_signature(), self.__out_sig.gr_io_signature(), + work_type, factor) self.__message = self.__gateway.block_message() #dict to keep references to all message handlers @@ -133,36 +187,47 @@ class gateway_block(object): """ Dispatch tasks according to the action type specified in the message. """ + if self.__message.action == gr.block_gw_message_type.ACTION_GENERAL_WORK: + # Actual number of inputs/output from scheduler + ninputs = len(self.__message.general_work_args_input_items) + noutputs = len(self.__message.general_work_args_output_items) + in_types = self.__in_sig.port_types(ninputs) + out_types = self.__out_sig.port_types(noutputs) self.__message.general_work_args_return_value = self.general_work( input_items=[pointer_to_ndarray( self.__message.general_work_args_input_items[i], - self.__in_sig[i], + in_types[i], self.__message.general_work_args_ninput_items[i] - ) for i in self.__in_indexes], + ) for i in range(ninputs)], output_items=[pointer_to_ndarray( self.__message.general_work_args_output_items[i], - self.__out_sig[i], + out_types[i], self.__message.general_work_args_noutput_items - ) for i in self.__out_indexes], + ) for i in range(noutputs)], ) elif self.__message.action == gr.block_gw_message_type.ACTION_WORK: + # Actual number of inputs/output from scheduler + ninputs = len(self.__message.work_args_input_items) + noutputs = len(self.__message.work_args_output_items) + in_types = self.__in_sig.port_types(ninputs) + out_types = self.__out_sig.port_types(noutputs) self.__message.work_args_return_value = self.work( input_items=[pointer_to_ndarray( self.__message.work_args_input_items[i], - self.__in_sig[i], + in_types[i], self.__message.work_args_ninput_items - ) for i in self.__in_indexes], + ) for i in range(ninputs)], output_items=[pointer_to_ndarray( self.__message.work_args_output_items[i], - self.__out_sig[i], + out_types[i], self.__message.work_args_noutput_items - ) for i in self.__out_indexes], + ) for i in range(noutputs)], ) elif self.__message.action == gr.block_gw_message_type.ACTION_FORECAST: @@ -217,6 +282,17 @@ class gateway_block(object): # Wrappers for the user to inherit from ######################################################################## class basic_block(gateway_block): + """Args: + name (str): block name + + in_sig (gr.py_io_signature): input port signature + + out_sig (gr.py_io_signature): output port signature + + For backward compatibility, a sequence of numpy type names is also + accepted as an io signature. + + """ def __init__(self, name, in_sig, out_sig): gateway_block.__init__(self, name=name, @@ -227,6 +303,17 @@ class basic_block(gateway_block): ) class sync_block(gateway_block): + """ + Args: + name (str): block name + + in_sig (gr.py_io_signature): input port signature + + out_sig (gr.py_io_signature): output port signature + + For backward compatibility, a sequence of numpy type names is also + accepted as an io signature. + """ def __init__(self, name, in_sig, out_sig): gateway_block.__init__(self, name=name, @@ -237,6 +324,17 @@ class sync_block(gateway_block): ) class decim_block(gateway_block): + """ + Args: + name (str): block name + + in_sig (gr.py_io_signature): input port signature + + out_sig (gr.py_io_signature): output port signature + + For backward compatibility, a sequence of numpy type names is also + accepted as an io signature. + """ def __init__(self, name, in_sig, out_sig, decim): gateway_block.__init__(self, name=name, @@ -247,6 +345,17 @@ class decim_block(gateway_block): ) class interp_block(gateway_block): + """ + Args: + name (str): block name + + in_sig (gr.py_io_signature): input port signature + + out_sig (gr.py_io_signature): output port signature + + For backward compatibility, a sequence of numpy type names is also + accepted as an io signature. + """ def __init__(self, name, in_sig, out_sig, interp): gateway_block.__init__(self, name=name, diff --git a/gnuradio-runtime/python/gnuradio/gr_unittest.py b/gnuradio-runtime/python/gnuradio/gr_unittest.py index 1b8323ad7a..190ea63354 100644 --- a/gnuradio-runtime/python/gnuradio/gr_unittest.py +++ b/gnuradio-runtime/python/gnuradio/gr_unittest.py @@ -1,6 +1,6 @@ #!/usr/bin/env python # -# Copyright 2004,2010 Free Software Foundation, Inc. +# Copyright 2004,2010,2018 Free Software Foundation, Inc. # # This file is part of GNU Radio # @@ -33,6 +33,7 @@ import unittest from . import gr_xmlrunner + class TestCase(unittest.TestCase): """A subclass of unittest.TestCase that adds additional assertions @@ -121,7 +122,8 @@ TextTestRunner = unittest.TextTestRunner TestProgram = unittest.TestProgram main = TestProgram -def run(PUT, filename=None): + +def run(PUT, filename=None, verbosity=1): ''' Runs the unittest on a TestCase and produces an optional XML report PUT: the program under test and should be a gr_unittest.TestCase @@ -152,7 +154,7 @@ def run(PUT, filename=None): fout = open(path+"/"+filename, "w") xmlrunner = gr_xmlrunner.XMLTestRunner(fout) - txtrunner = TextTestRunner(verbosity=1) + txtrunner = TextTestRunner(verbosity=verbosity) # Run the test; runner also creates XML output file # FIXME: make xmlrunner output to screen so we don't have to do run and main @@ -162,7 +164,7 @@ def run(PUT, filename=None): if xmlrunner is not None: xmlrunner.run(suite) - main() + main(verbosity=verbosity) # This will run and fail make check if problem # but does not output to screen. @@ -170,7 +172,7 @@ def run(PUT, filename=None): else: # If no filename is given, just run the test - main() + main(verbosity=verbosity) ############################################################################## diff --git a/gnuradio-runtime/python/pmt/CMakeLists.txt b/gnuradio-runtime/python/pmt/CMakeLists.txt index 7afac956cb..e635663924 100644 --- a/gnuradio-runtime/python/pmt/CMakeLists.txt +++ b/gnuradio-runtime/python/pmt/CMakeLists.txt @@ -39,6 +39,7 @@ foreach(py_qa_test_file ${py_qa_test_files}) set(GR_TEST_PYTHON_DIRS ${CMAKE_BINARY_DIR}/gnuradio-runtime/python ${CMAKE_BINARY_DIR}/gnuradio-runtime/swig + ${CMAKE_BINARY_DIR}/gnuradio-blocks/swig ) set(GR_TEST_TARGET_DEPS gnuradio-runtime) GR_ADD_TEST(${py_qa_test_name} ${QA_PYTHON_EXECUTABLE} -B ${py_qa_test_file}) |