summaryrefslogtreecommitdiff
path: root/gnuradio-runtime/python/gnuradio/gr/gateway.py
diff options
context:
space:
mode:
Diffstat (limited to 'gnuradio-runtime/python/gnuradio/gr/gateway.py')
-rw-r--r--gnuradio-runtime/python/gnuradio/gr/gateway.py349
1 files changed, 183 insertions, 166 deletions
diff --git a/gnuradio-runtime/python/gnuradio/gr/gateway.py b/gnuradio-runtime/python/gnuradio/gr/gateway.py
index 71d15781d2..615027b833 100644
--- a/gnuradio-runtime/python/gnuradio/gr/gateway.py
+++ b/gnuradio-runtime/python/gnuradio/gr/gateway.py
@@ -1,5 +1,5 @@
#
-# Copyright 2011-2012, 2018 Free Software Foundation, Inc.
+# Copyright 2011-2012, 2018, 2020 Free Software Foundation, Inc.
#
# This file is part of GNU Radio
#
@@ -12,11 +12,12 @@ from __future__ import unicode_literals
import numpy
+import ctypes
+
+from . import gr_python as gr
+from .gr_python import io_signature # , io_signaturev
+from .gr_python import block_gateway
-from . import runtime_swig as gr
-from .runtime_swig import io_signature, io_signaturev
-from .runtime_swig import block_gw_message_type
-from .runtime_swig import block_gateway
########################################################################
# Magic to turn pointers into numpy arrays
@@ -25,54 +26,20 @@ from .runtime_swig import block_gateway
def pointer_to_ndarray(addr, dtype, nitems):
class array_like(object):
__array_interface__ = {
- 'data' : (int(addr), False),
- 'typestr' : dtype.base.str,
- 'descr' : dtype.base.descr,
- 'shape' : (nitems,) + dtype.shape,
- 'strides' : None,
- 'version' : 3
+ 'data': (int(addr), False),
+ 'typestr': dtype.base.str,
+ 'descr': dtype.base.descr,
+ 'shape': (nitems,) + dtype.shape,
+ 'strides': None,
+ 'version': 3
}
return numpy.asarray(array_like()).view(dtype.base)
########################################################################
-# Handler that does callbacks from C++
-########################################################################
-class gateway_handler(gr.feval_ll):
-
- #don't put a constructor, it won't work
-
- def init(self, callback):
- self._callback = callback
-
- def eval(self, arg):
- try: self._callback()
- except Exception as ex:
- print("handler caught exception: %s"%ex)
- import traceback; traceback.print_exc()
- raise ex
- return 0
-
-########################################################################
-# Handler that does callbacks from C++
+# io_signature for Python
########################################################################
-class msg_handler(gr.feval_p):
-
- #don't put a constructor, it won't work
-
- def init(self, callback):
- self._callback = callback
- def eval(self, arg):
- try: self._callback(arg)
- except Exception as ex:
- print("handler caught exception: %s"%ex)
- import traceback; traceback.print_exc()
- raise ex
- return 0
-########################################################################
-# io_signature for Python
-########################################################################
class py_io_signature(object):
"""
Describes the type/number of ports for block input or output.
@@ -94,15 +61,15 @@ class py_io_signature(object):
"""
self.__min_ports = min_ports
self.__max_ports = max_ports
- self.__types = tuple( numpy.dtype(t) for t in type_list )
+ 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])
+ return io_signature.makev(self.__min_ports, self.__max_ports,
+ [t.itemsize for t in self.__types] or [0])
def port_types(self, nports):
"""
@@ -129,14 +96,19 @@ class py_io_signature(object):
########################################################################
# The guts that make this into a gr block
########################################################################
+
+
class gateway_block(object):
- def __init__(self, name, in_sig, out_sig, work_type, factor):
+ def __init__(self, name, in_sig, out_sig, block_type):
+ self._decim = 1
+ self._interp = 1
+ self._block_type = block_type
# Normalize the many Python ways of saying 'nothing' to '()'
in_sig = in_sig or ()
out_sig = out_sig or ()
-
+
# Backward compatibility: array of type strings -> py_io_signature
if type(in_sig) is py_io_signature:
self.__in_sig = in_sig
@@ -145,98 +117,93 @@ class gateway_block(object):
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,
- 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
- self.__msg_handlers = {}
-
- #register block functions
- prefix = 'block__'
- for attr in [x for x in dir(self.__gateway) if x.startswith(prefix)]:
- setattr(self, attr.replace(prefix, ''), getattr(self.__gateway, attr))
- self.pop_msg_queue = lambda: gr.block_gw_pop_msg_queue_safe(self.__gateway)
+ self.__out_sig = py_io_signature(
+ len(out_sig), len(out_sig), out_sig)
+
+ self.gateway = block_gateway(
+ self, name, self.__in_sig.gr_io_signature(), self.__out_sig.gr_io_signature())
+
+ self.msg_handlers = {}
+
+ def __getattr__(self, name):
+ """
+ Pass-through member requests to the C++ object.
+ """
+ if not hasattr(self, "gateway"):
+ raise RuntimeError(
+ "{0}: invalid state -- did you forget to call {0}.__init__ in "
+ "a derived class?".format(self.__class__.__name__))
+ return getattr(self.gateway, name)
def to_basic_block(self):
"""
Makes this block connectable by hier/top block python
"""
- return self.__gateway.to_basic_block()
+ return self.gateway.to_basic_block()
+
+ def fixed_rate_noutput_to_ninput(self, noutput_items):
+ return int((noutput_items * self._decim / self._interp) + self.gateway.history() - 1)
- def __gr_block_handle(self):
+ def handle_forecast(self, noutput_items, ninputs):
"""
- Dispatch tasks according to the action type specified in the message.
+ This is the handler function for forecast calls from
+ block_gateway in c++ across pybind11 wrappers
"""
+ return self.forecast(noutput_items, ninputs)
+
+ # return ninput_items_required
- 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],
- in_types[i],
- self.__message.general_work_args_ninput_items[i]
- ) for i in range(ninputs)],
-
- output_items=[pointer_to_ndarray(
- self.__message.general_work_args_output_items[i],
- out_types[i],
- self.__message.general_work_args_noutput_items
- ) 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],
- in_types[i],
- self.__message.work_args_ninput_items
- ) for i in range(ninputs)],
-
- output_items=[pointer_to_ndarray(
- self.__message.work_args_output_items[i],
- out_types[i],
- self.__message.work_args_noutput_items
- ) for i in range(noutputs)],
- )
-
- elif self.__message.action == gr.block_gw_message_type.ACTION_FORECAST:
- self.forecast(
- noutput_items=self.__message.forecast_args_noutput_items,
- ninput_items_required=self.__message.forecast_args_ninput_items_required,
- )
-
- elif self.__message.action == gr.block_gw_message_type.ACTION_START:
- self.__message.start_args_return_value = self.start()
-
- elif self.__message.action == gr.block_gw_message_type.ACTION_STOP:
- self.__message.stop_args_return_value = self.stop()
-
- def forecast(self, noutput_items, ninput_items_required):
+ def forecast(self, noutput_items, ninputs):
"""
forecast is only called from a general block
this is the default implementation
"""
- for i in range(len(ninput_items_required)):
- ninput_items_required[i] = noutput_items + self.history() - 1
+ ninput_items_required = [0]*ninputs
+ for i in range(ninputs):
+ ninput_items_required[i] = noutput_items + self.gateway.history() - 1
+
+ return ninput_items_required
+
+ def handle_general_work(self, noutput_items,
+ ninput_items,
+ input_items,
+ output_items):
+
+ ninputs = len(input_items)
+ noutputs = len(output_items)
+ in_types = self.in_sig().port_types(ninputs)
+ out_types = self.out_sig().port_types(noutputs)
+
+
+ ctypes.pythonapi.PyCapsule_GetPointer.restype = ctypes.c_void_p
+ ctypes.pythonapi.PyCapsule_GetPointer.argtypes = [ctypes.py_object, ctypes.c_char_p]
+
+ if self._block_type != gr.GW_BLOCK_GENERAL:
+ ii=[pointer_to_ndarray(
+ ctypes.pythonapi.PyCapsule_GetPointer(input_items[i],None),
+ in_types[i],
+ self.fixed_rate_noutput_to_ninput(noutput_items)
+ ) for i in range(ninputs)]
+ else:
+ ii=[pointer_to_ndarray(
+ ctypes.pythonapi.PyCapsule_GetPointer(input_items[i],None),
+ in_types[i],
+ ninput_items[i]
+ ) for i in range(ninputs)]
+
+ oo=[pointer_to_ndarray(
+ ctypes.pythonapi.PyCapsule_GetPointer(output_items[i],None),
+ out_types[i],
+ noutput_items
+ ) for i in range(noutputs)]
+
+ if self._block_type != gr.GW_BLOCK_GENERAL:
+ r = self.work(ii,oo)
+ self.consume_items(r)
+ else:
+ r = self.general_work(ii,oo)
+
+ return r
def general_work(self, *args, **kwargs):
"""general work to be overloaded in a derived class"""
@@ -252,25 +219,43 @@ class gateway_block(object):
def stop(self):
return True
- def set_msg_handler(self, which_port, handler_func):
- handler = msg_handler()
- handler.init(handler_func)
- self.__gateway.set_msg_handler_feval(which_port, handler)
- # Save handler object in class so it's not garbage collected
- self.__msg_handlers[which_port] = handler
-
def in_sig(self):
return self.__in_sig
def out_sig(self):
return self.__out_sig
+ def set_msg_handler(self, which_port, handler_function):
+ self.gateway.set_msg_handler_pybind(which_port, handler_function.__name__)
+ # Save handler object in class so it's not garbage collected
+ self.msg_handlers[which_port] = handler_function
+
+ # TODO: Make gateway_block have an is-a instead of has-a relationship
+ # That way we don't have to wrap all these functions
+
+ # def message_port_register_in(self, port_id):
+ # return self.gateway.message_port_register_in(port_id)
+
+ # def message_port_register_out(self, port_id):
+ # return self.gateway.message_port_register_out(port_id)
+
+ # def consume_each(self, how_many_items):
+ # return self.gateway.consume_each( how_many_items)
+
+ # def consume(self, which_input, how_many_items):
+ # return self.gateway.consume(which_input, how_many_items)
+
+ # def produce(self, which_output, how_many_items):
+ # return self.gateway.produce(which_output, how_many_items)
+
+
########################################################################
# Wrappers for the user to inherit from
########################################################################
-class basic_block(gateway_block):
- """Args:
+class basic_block(gateway_block):
+ """
+ Args:
name (str): block name
in_sig (gr.py_io_signature): input port signature
@@ -279,18 +264,19 @@ class basic_block(gateway_block):
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,
- in_sig=in_sig,
- out_sig=out_sig,
- work_type=gr.GR_BLOCK_GW_WORK_GENERAL,
- factor=1, #not relevant factor
- )
-
-class sync_block(gateway_block):
+ name=name,
+ in_sig=in_sig,
+ out_sig=out_sig,
+ block_type=gr.GW_BLOCK_GENERAL
+ )
+
+ def consume_items(self, nitems ):
+ pass
+
+class sync_block(gateway_block):
"""
Args:
name (str): block name
@@ -304,12 +290,18 @@ class sync_block(gateway_block):
"""
def __init__(self, name, in_sig, out_sig):
gateway_block.__init__(self,
- name=name,
- in_sig=in_sig,
- out_sig=out_sig,
- work_type=gr.GR_BLOCK_GW_WORK_SYNC,
- factor=1,
- )
+ name=name,
+ in_sig=in_sig,
+ out_sig=out_sig,
+ block_type=gr.GW_BLOCK_SYNC
+ )
+ self._decim = 1
+ self._interp = 1
+
+ def consume_items(self, nitems ):
+ if (nitems > 0):
+ self.gateway.consume_each(nitems)
+
class decim_block(gateway_block):
"""
@@ -325,12 +317,24 @@ class decim_block(gateway_block):
"""
def __init__(self, name, in_sig, out_sig, decim):
gateway_block.__init__(self,
- name=name,
- in_sig=in_sig,
- out_sig=out_sig,
- work_type=gr.GR_BLOCK_GW_WORK_DECIM,
- factor=decim,
- )
+ name=name,
+ in_sig=in_sig,
+ out_sig=out_sig,
+ block_type=gr.GW_BLOCK_DECIM
+ )
+ self._decim = decim
+ self._interp = 1
+ self.gateway.set_relative_rate(self._interp, self._decim)
+ self.gateway.set_output_multiple(self._interp)
+
+ def consume_items(self, nitems ):
+ if (nitems > 0):
+ self.gateway.consume_each(int(nitems * self._decim))
+
+ def forecast(self, noutput_items, ninputs):
+ return [self.fixed_rate_noutput_to_ninput(noutput_items) for ii in range(ninputs)]
+
+
class interp_block(gateway_block):
"""
@@ -346,9 +350,22 @@ class interp_block(gateway_block):
"""
def __init__(self, name, in_sig, out_sig, interp):
gateway_block.__init__(self,
- name=name,
- in_sig=in_sig,
- out_sig=out_sig,
- work_type=gr.GR_BLOCK_GW_WORK_INTERP,
- factor=interp,
- )
+ name=name,
+ in_sig=in_sig,
+ out_sig=out_sig,
+ block_type=gr.GW_BLOCK_DECIM
+ )
+ self._decim = 1
+ self._interp = interp
+ self.gateway.set_relative_rate(self._interp, self._decim)
+ self.gateway.set_output_multiple(self._interp)
+
+
+ def consume_items(self, nitems ):
+ if (nitems > 0):
+ self.gateway.consume_each(int(nitems / self._interp))
+
+ def forecast(self, noutput_items, ninputs):
+ # print("{},{},{}".format(noutput_items, ninputs, [self.fixed_rate_noutput_to_ninput(noutput_items) for ii in range(ninputs)]))
+
+ return [self.fixed_rate_noutput_to_ninput(noutput_items) for ii in range(ninputs)]