Since adding message based lambda blocks the other day, it seemed an obvious pairing to include a stream equivalent block as well for really rapid prototyping needs. The interface is a bit less simple because we can have N input ports and M output ports, I settled on defining a lambda function which takes a list of input vectors (the same input_items list of numpy arrays normal python blocks get, and an integer output port index). To implement a simplish stream block now that consumes N input streams and produces M output streams, this function prototype simply needs to be implemented. This won’t let you do everything, for instance output streams still need to produce the same number of items, and they are assumed to consume at this same rate. Extending this to a sync decimate or interpolater should be straight forward, but the function prototype would need to become a bit more complex for a “general work” kind of interface which allows producing/consuming each port at different rates, so I chose not to address that for now.
Example Stream Lambda Block Flowgraph
A simple example graph which demonstrates the use of these blocks is shown below, this graph takes a simple Gaussian noise source in, throttles it to a fixed throughput rate, and then feeds it through a 2-in 2-out stream lambda block. In this case, we provide the following lambda function for the block,
lambda input_items, output_index: (input_items[output_index][::2]**2)*[0.1,0.2][output_index] + [-1,1][output_index]
That is to say:
output_items = input_items[::2]**2*0.1 – 1
output_items = input_items[::2]**2*0.2 + 1
Here, each stream is independent of the other output, and they could have been implemented as two seperate blocks, but this is just provided as an illustration.
The second block then is a 2-in 1-out stream lambda block which merges the two streams into one output stream, this mapping is given by:
lambda input_items, output_index: (input_items[:] * numpy.conj(input_items[:]))
This one is a bit simpler, as we will simply have a single evaluation:
output_items = input_items*conj(input_items)
The resulting output signal is then shown in the output plot.
Dynamic Runtime Updates
What’s really fun is these lambda functions can be updated at runtime to change the block’s functionality. I’ve added a variable_text_input block to gr-pyqt which allows for runtime input of new lambda functions to each block which are passed to the GRC setter callback for each stream lambda block. By running this graph, you can now fiddle with the block’s algorithms at run time and immediately see the output effect on the stream signal. Just don’t type any invalid python at run-time or you’ll crash the block’s thread context.
The dynamic update version of the stream lambda block demo is shown below.
These dynamic function updates should work with the message lambda block as well, but that is not shown here.
The GRC graph for this example can be found @ https://github.com/osh/gr-pyqt/blob/master/apps/test_stream_lambda.grc