discuss-gnuradio
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: GNU Radio 3.9.3.0 Python basic block - issues with forecast and prod


From: Johannes Demel
Subject: Re: GNU Radio 3.9.3.0 Python basic block - issues with forecast and produce
Date: Mon, 24 Jan 2022 09:43:25 +0100
User-agent: Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Thunderbird/78.14.0

Hi Patric,

first off, the `consume_each` call needs to go behind any read on the input buffer. You really tell the system at this point: I'm finally done with these items, do whatever. Since GR is a multi-threaded system, this may cause trouble because the samples you want to read are already overwritten.

The best place to check for specifics about Python blocks is here:
https://github.com/gnuradio/gnuradio/blob/main/gnuradio-runtime/python/gnuradio/gr/gateway.py

This is the interface that is implemented. The `forecast` method is implemented in L153ff. It is different from the C++ version.

I guess Python blocks (and embedded Python blocks even more) are considered simple blocks for beginners. Unfortunately, this is not how they're treated by developers. Also, they are leaky abstractions over their C++ originals.

To rephrase the new issue:
- You insert a known pattern and expect corresponding output.
- Actual output is all zeros. (The screenshot is missing).

So let's comment on your `general_work`.

> def general_work(self, input_items, output_items):
>      # Firstly check, if input_items has a sufficient amount of items
>      if len(input_items[0]) >= self.buffer_len:
>          # Then consume exactly self.buffer_len items
>          self.consume_each(self.buffer_len)
`consume_each` should go after the last use of `input_items`.

>          # Now only output a fraction of the input items, say the first
> self.out_items, on output port[0]
>          output_items[0]  = input_items[0][:self.out_items]
This is probably the line causing issues. Try
`output_items[0][0:self.out_items] = input_items[0][0:self.out_items]`
The difference is the left hand indexing. Now, you write items to positions in an array. Previously, you overwrote the array and replaced it with a new one. Thus, the output buffer was still full of zeros.

This behavior is very unpythonic. It makes perfect sense though from a C++ block perspective. You pass pointers or references to a data structure and write data to it in C++. You wouldn't normally do that in Python.

Cheers
Johannes

On 23.01.22 19:27, Patric Müller wrote:
Hello everyone and hello Johannes,

first of all, a huge thanks for the quick and detailed response, it is really helpful. After implementing your suggested changes and additional tinkering, I am still facing two issues:

The `forecast` method expects estimates. The scheduler will call
`general_work` anyways at some point, if the system is unable to fulfill
your forecast requirement. `set_output_multiple` is much more strict.
In your case I'd start with:
`ninput_items_required[0] = noutput_items`

I've changed the `forecast`method accordingly, however I still get the same errors. If I use
`ninput_items_required[0] = noutput_items`
then I receive:
`TypeError: 'int' object does not support item assignment`
I've checked the type and value of `ninput_items_required` and sure enough, it is of `<class 'int'>` and has a value of `1`.

My next thought was that it may not be a <class 'list'>, since I only have one input port, so I also tried:
`ninput_items_required = noutput_items`
This yields me with:
`Unable to cast Python instance to C++ type (compile in debug mode for details)`

For now and since it isn't strictly necessary, I've commented the `forecast` method.


At the end of `general_work` report these values to the system:
- consumed items via `consume_each`
- produced items via `return integer_with_number_of_consumed_items`.

I have changed my code accordingly. My `general_work` function now looks like this:

def general_work(self, input_items, output_items):
     # Firstly check, if input_items has a sufficient amount of items
     if len(input_items[0]) >= self.buffer_len:
         # Then consume exactly self.buffer_len items
         self.consume_each(self.buffer_len)
        # Now only output a fraction of the input items, say the first self.out_items, on output port[0]
         output_items[0]  = input_items[0][:self.out_items]

     else:
         # if we do not have enough input_items, set empty output
         output_items[0] = []

    # finally, return len(output_items[0]), which is either equal to `out_items` or `0`
     return len(output_items[0])


It runs and I did some tests. I have created a signal of length 1024, which is composed of a random sequence with length 312 and a cosine signal with a length of 712 both muxed together. Next follows the python block and the signal is compared before and after by using time sinks.
Screenshot of the flowgraph:



I would expect that I should only observe the random sequence (first 312 samples) in the `processed signal` time sink. However the time sink only shows zeros, which is probably due to a coding error. In order to investigate, I took a look at the content of `output_items[0]` and also at the length. The length is 312, which seems fine, since I specified it with `self.out_items`. The contents also seem fine, it is definitely not zero and matches the content of `input_items[0]`.

Since everything "seems" fine, I am not sure on how I could debug this further. My complete code, including the Debug statements, can be found here:
https://pastebin.com/HRMzp0zA

Thanks for taking your time and looking into this!
Kind regards,
Patric





reply via email to

[Prev in Thread] Current Thread [Next in Thread]