summaryrefslogtreecommitdiff
path: root/gnuradio-runtime/python
diff options
context:
space:
mode:
Diffstat (limited to 'gnuradio-runtime/python')
-rw-r--r--gnuradio-runtime/python/gnuradio/eng_notation.py23
-rw-r--r--gnuradio-runtime/python/gnuradio/gr/CMakeLists.txt5
-rw-r--r--gnuradio-runtime/python/gnuradio/gr/__init__.py4
-rw-r--r--gnuradio-runtime/python/gnuradio/gr/gateway.py161
-rw-r--r--gnuradio-runtime/python/gnuradio/gr_unittest.py12
-rw-r--r--gnuradio-runtime/python/pmt/CMakeLists.txt1
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})