# Processing expectations

**URL:** <https://www.ruby-forum.com/t/processing-expectations/242927>\
**Category:** GNU Radio\
**Created:** [August 7, 2015, 8:48pm UTC](https://www.ruby-forum.com/t/processing-expectations/242927 "2015-08-07T20:48:12Z")\
**Posts on this page:** 13\
**Page:** 1

<div class="post-metadata">

**Author:** ![Sadaf\_N](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/s/858c86/32.png) [@Sadaf\_N](https://www.ruby-forum.com/u/Sadaf_N)\
**Post date:** [August 7, 2015, 8:48pm UTC](https://www.ruby-forum.com/t/processing-expectations/242927/1 "2015-08-07T20:48:12Z")

</div>

I’m brand new to SDR and I’m just trying to calibrate my expectations of  
what will be possible in real time. I’m hoping someone here can give me  
a  
pointer or two.

I’m running GNU Radio 3.7.2 on Ubuntu 14.04, having installed from the  
Ubuntu repositories. The machine I’m using has an Intel Core i5-2520M  
running at 2500MHz.

I’m constructing flowgraphs in GRC and using an RTL-2832U-based USB  
dongle  
as a receiver.

I’ve built a fairly simple broadcast FM stereo receiver as an example.  
It  
has the following components:

- RTL-SDR source (768k sample rate).
- Low pass filter (768k sample rate, 96k cutoff, 4k transition)
- Rational resampler (4x decimation)
- WBFM receiver (quadrature rate 192k)
- Low pass filter (1x decimation, 192k sample rate, 15k cutoff, 2k  
transition)
- 2x Band pass filter (1x decimation, 192k sample rate, differing cutoff  
and transition)
- Multiply
- Low pass filter (192k sample rate, 15k cutoff, 2k transition)
- Add, subtract
- 2 x rational resampler (4x decimation)
- Audio sink (sample rate 48k)

My system is not able to run this in realtime, producing regular  
underruns  
(about two per second). Decreasing the receiver sample rate to 384k and  
decreasing the decimation factor in the first resampler to 2 allows it  
to  
run in without underruns, from which I deduce that the CPU is right on  
the  
edge of being able to process the flowchart in realtime.

Are my expectations of what I’ll be able to do in realtime unreasonably  
high? Is there something I’m missing, that I can do to make this run  
much  
faster?

Thanks,  
Tom

---

<div class="post-metadata">

**Author:** ![tbcox23](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/t/b9bd4f/32.png) [@tbcox23](https://www.ruby-forum.com/u/tbcox23)\
**Post date:** [August 7, 2015, 9:05pm UTC](https://www.ruby-forum.com/t/processing-expectations/242927/2 "2015-08-07T21:05:43Z")

</div>

With a processor like that, you should be able to clock your dongle at 2  
Msps all the time, and then resample. I remember faintly that there was  
some issue with rtlsdr dongles not working will with lower sampling  
rates, maybe that’s what it is.

On a side note, this allows you to offset-tune and avoid DC spurs, which  
can be pretty ugly on the RTLSDR.

M

---

<div class="post-metadata">

**Author:** ![tbcox23](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/t/b9bd4f/32.png) [@tbcox23](https://www.ruby-forum.com/u/tbcox23)\
**Post date:** [August 7, 2015, 9:09pm UTC](https://www.ruby-forum.com/t/processing-expectations/242927/3 "2015-08-07T21:09:14Z")

</div>

You may also want to have a look at  
[http://stats.gnuradio.org/](http://stats.gnuradio.org/)  
To get a feel for how fast different sdr kernels run on different  
processing platforms

Tim

---

<div class="post-metadata">

**Author:** ![tbcox23](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/t/b9bd4f/32.png) [@tbcox23](https://www.ruby-forum.com/u/tbcox23)\
**Post date:** [August 7, 2015, 9:30pm UTC](https://www.ruby-forum.com/t/processing-expectations/242927/4 "2015-08-07T21:30:51Z")

</div>

On Fri, Aug 7, 2015 at 2:47 PM, Tom C. [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid) wrote:

> 

You’ve just strung together 5 filters in a row here. You can do your  
decimation step in the low pass filter, and the WBFM stage has a filter  
in  
it, as well. That 4k transition in the initial LPF at 768 kHz will make  
for  
a long filter; if that’s also doing 4x down sampling, you’ll remove a  
lot  
of processing requirements there.

Think about all of these filter stages and how you might be able to  
combine  
them and reduce their overall power requirement.s

> - Multiply
> - Low pass filter (192k sample rate, 15k cutoff, 2k transition)
> - Add, subtract
> - 2 x rational resampler (4x decimation)
> - Audio sink (sample rate 48k)

Again, you can do the resampling and LPF together; that resampler  
implements its own filter. You can also integrate the multiply stage  
into  
the LPF by setting the gain of the taps to your multiply value.

> Thanks,  
> Tom

It sounds like you’re close to what you need to get this working. Just  
think more about the filter stages and values. Use gr\_filter\_design to  
plot  
them and see how many taps you’re using to help you reduce the  
computational requirements.

Tom

---

<div class="post-metadata">

**Author:** ![tbcox23](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/t/b9bd4f/32.png) [@tbcox23](https://www.ruby-forum.com/u/tbcox23)\
**Post date:** [August 10, 2015, 11:07am UTC](https://www.ruby-forum.com/t/processing-expectations/242927/5 "2015-08-10T11:07:35Z")

</div>

Many thanks to all who responded here. As Martin remembered faintly,  
the  
RTL-SDR dongle does not work well at low sample rates, and it gives  
considerably better reception to run it at (eg) 1.536 Msps and then  
resample it to 384 ksps than to use 384 ksps from the start.

Thanks to Tom for pointing out the ways of combining resampling and  
filtering. Was I supposed to know somehow that the rational resampler  
includes the necessary aliasing filters? That’s not meant to be a  
sarcastic question - I’m trying to figure out where to look in the  
documentation for these sorts of things. I don’t have GRC to hand, but  
I  
don’t remember it being in the documentation for the block. The  
documentation for the rational\_resampler\_base\_fff class sort of hints at  
it  
without saying as much, and it took me quite a while to figure out where  
to  
find even that.

At any rate, I’ve got the thing running in realtime, and performing  
quite  
well after replacing the crummy antenna provided with the dongle with a  
VHF  
TV antenna pointed in the right direction.

Thanks again,  
Tom

---

<div class="post-metadata">

**Author:** ![tbcox23](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/t/b9bd4f/32.png) [@tbcox23](https://www.ruby-forum.com/u/tbcox23)\
**Post date:** [August 10, 2015, 1:57pm UTC](https://www.ruby-forum.com/t/processing-expectations/242927/6 "2015-08-10T13:57:00Z")

</div>

* * *

Discuss-gnuradio mailing list  
removed\_email\_address@domain.invalid  
[https://lists.gnu.org/mailman/listinfo/discuss-gnuradio](https://lists.gnu.org/mailman/listinfo/discuss-gnuradio)

---

<div class="post-metadata">

**Author:** ![tbcox23](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/t/b9bd4f/32.png) [@tbcox23](https://www.ruby-forum.com/u/tbcox23)\
**Post date:** [August 10, 2015, 2:15pm UTC](https://www.ruby-forum.com/t/processing-expectations/242927/7 "2015-08-10T14:15:07Z")

</div>

Hi Tom,

I just had to look this up. If you’re in GRC, you have “rationale  
resampler” and “rational resampler base”; they do basically the same,  
but if you use the one without “base”, and don’t specify the taps, GNU  
Radio just automatically designs a filter that avoids all aliasing and  
imaging, which is done with a python wrapper around the C++  
rational\_resampler\_base\_xxx’s make function[1]. That’s pretty handy in  
most use cases, but not too much if you want your own filter for some  
reason.

If you’re using the “base” variant, you /must/ specify the taps  
yourself, because you directly invoke the C++ block’s maker. If you go  
ahead and just use “[1.0]” as taps, you get the aliased results from my  
pictures.

So if you happen to /want /to specify the taps, because you can  
integrate the functionality of a downstream filter into the resampler to  
save CPU cycles, it doesn’t make a difference which block you use.

Best regards,  
Marcus

[1]  
[https://github.com/gnuradio/gnuradio/blob/master/gr-filter/python/filter/rational\_resampler.py](https://github.com/gnuradio/gnuradio/blob/master/gr-filter/python/filter/rational_resampler.py)

---

<div class="post-metadata">

**Author:** ![tbcox23](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/t/b9bd4f/32.png) [@tbcox23](https://www.ruby-forum.com/u/tbcox23)\
**Post date:** [August 10, 2015, 1:30pm UTC](https://www.ruby-forum.com/t/processing-expectations/242927/8 "2015-08-10T13:30:42Z")

</div>

Hi Tom,

> Was I supposed to know somehow that the rational resampler includes  
> the necessary aliasing filters? That’s not meant to be a sarcastic  
> question - I’m trying to figure out where to look in the documentation  
> for these sorts of things.  
> I think it’s actually a good question! It’s always better to ask how you  
> can figure out something rather then just relying on pre-made solutions.  
> I don’t have GRC to hand, but I don’t remember it being in the  
> documentation for the block.  
> GRC docs typically only contain a small piece of the information in the  
> full doxygen.

So the point here is that the doxygen [1] does mention it builds a  
rational resampling polyphase FIR out of the taps that you need to  
supply.  
Also, it quite explicitely says that you need to use a filter that will  
suppress the aliases or images (depending on what’s higher, decimation  
or interpolation).

So the thing that’s happening here is that you want to achieve a M/N  
resampling; you actually[2] do the following:

—\>[??? M] → [??? N] →

So you pad 1 input sample to M “intermediate” samples by simply adding  
M-1 zeros after each sample, and then you throw away N-1 out N samples.  
Simple as that? Not really. First of all, as you’ve noticed, the M  
interpolation introduces aliases.  
So assume the input spectrum might look like this:  
input spectrum

Obviously, we’re wasting sample space here. So having a sharp look, we  
determine that this only needs 3/5 of the original bandwidth; hence M=3,  
N=5.

What we want to have looks like:  
Wanted output spectrum  
I.e. we want spectrum that as closely fits the full Nyquist bandwidth,  
but doesn’t “overlap” the edges, ie. there’s no aliasing at the edges.  
Notice that at the band edges, the spectral power goes down by \> 20dB.

What we actually get if we just interpolate and decimate (GRC file here  
[3]) however looks like  
actual output spectrum

The output spectrum is a obviously continuous piece of overlaid signal;  
if you look closely, you’ll notice that these two center peaks seem to  
re-appear at ~±7kHz. Something is wrong here.

But it’s not really surprising: If you take a look at the intermediate  
spectrum [4], there’s the 3 repetitions we’d expect from interpolation.  
Now, mentally cut the intermediate spectrum in five equally wide parts,  
and mentally shift them the four outer ones over the center one. That  
aliasing is what ruins our signal!

That demonstrates what’s necessary to get rid of this: in the  
intermediate spectrum, we’ll need to suppress everything that isn’t in  
the “middle” zone. Doing that yields [5], which is what we wanted.

Here, M \< N, and that’s why you need an anti-alias filter; but the same  
M repetitions appear for any M, and the same “cut up and overlay” logic  
applies to any N; this explains why for M \> N, you need an anti-imaging  
filter.

The point here is, though, that you just really need one filter here,  
and it needs to be the stricter one of both (cutoff at  
decimation/interpolation or at interpolation/decimation). Of course, if  
you follow up with another filter that already fulfills that, you might  
as well just interpolate that filter to match the M_input sample rate,  
and use that. Due to [2], rational resamplers don’t actually run at  
M_input sample rate (that would be computationally terrible!), so the  
resulting filter might actually be not much worse than the minimal  
filter that you’d use just to guarantee proper resampling, but you might  
just save a whole separate filter.

Best regards,  
Marcus

[1]  
[https://gnuradio.org/doc/doxygen/classgr\_1\_1filter\_1\_1rational\_\_resampler\_\_base\_\_fff.html#details](https://gnuradio.org/doc/doxygen/classgr_1_1filter_1_1rational __resampler__ base__fff.html#details)  
[2] Note that there’s polyphase trickery that /mathematicall//y/ does  
the same as just actually adding the zeros, and throwing away samples,  
but without doing the unnecessary operations (who needs samples that get  
thrown away? Who needs to multiply something with 0 to then add it up?)  
[3]

> <https://gist.github.com/marcusmueller/24d64e67709fafbf036f#file-spec-grc>
>
> There are more than three files. show original

[4]

> <https://gist.github.com/marcusmueller/24d64e67709fafbf036f#file-nofilter-png>
>
> There are more than three files. show original

[5]

> <https://gist.github.com/marcusmueller/24d64e67709fafbf036f#file-proper-png>
>
> There are more than three files. show original

---

<div class="post-metadata">

**Author:** ![tbcox23](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/t/b9bd4f/32.png) [@tbcox23](https://www.ruby-forum.com/u/tbcox23)\
**Post date:** [August 10, 2015, 2:37pm UTC](https://www.ruby-forum.com/t/processing-expectations/242927/9 "2015-08-10T14:37:54Z")

</div>

Hi Tom,

added the block, opened the block properties, had a look at the id; I  
knew that these kind of blocks live within gr-filter, so

cd gr-filter  
vim grc/_rational_.grc ##that’s where the block definitions for GRC  
reside

found out that the non-base variant used rational\_resampler\_$(type), but  
I had a look into filter, and only found rational\_resampler\_base\_\*; so I  
guessed it was a python file.  
Went into python/filter, and opened rational\_resampler.py, based on it  
being the only python file that was possibly relevant here.

You know, that’s really unintuitive, and I think we’ll need some helpers  
or better documentation that makes finding such things easier.

Best regards,  
Marcus

---

<div class="post-metadata">

**Author:** ![tbcox23](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/t/b9bd4f/32.png) [@tbcox23](https://www.ruby-forum.com/u/tbcox23)\
**Post date:** [August 10, 2015, 2:32pm UTC](https://www.ruby-forum.com/t/processing-expectations/242927/10 "2015-08-10T14:32:38Z")

</div>

On Mon, 10 Aug 2015 at 13:14 Marcus Müller [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid)  
wrote:

> If you’re using the “base” variant, you _must_ specify the taps yourself,  
> because you directly invoke the C++ block’s maker. If you go ahead and just  
> use “[1.0]” as taps, you get the aliased results from my pictures.
> 
> So if you happen to \*want \*to specify the taps, because you can integrate  
> the functionality of a downstream filter into the resampler to save CPU  
> cycles, it doesn’t make a difference which block you use.

Ah, I see. Many thanks for taking the time to explain this. Where did  
you  
look to find out that ‘rational resampler’ block does an automatic  
filter  
design for you?

Regards,  
Tom

---

<div class="post-metadata">

**Author:** ![tbcox23](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/t/b9bd4f/32.png) [@tbcox23](https://www.ruby-forum.com/u/tbcox23)\
**Post date:** [August 10, 2015, 3:03pm UTC](https://www.ruby-forum.com/t/processing-expectations/242927/11 "2015-08-10T15:03:28Z")

</div>

With GNU Radio, never feel stupid 😉  
Documentation did turn out quite great in most places, but as soon as  
you want to figure out what happens inside, you’re pretty much on your  
own, often. grep/ack/git grep is a constant friend, but it’s not always  
easy to figure out how stuff works internally. There’s a delicate  
balance between writing code and documenting that outside of the code  
itself, especially since GNU Radio has quite a lot of contributors.

Cheers,  
Marcus

---

<div class="post-metadata">

**Author:** ![tbcox23](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/t/b9bd4f/32.png) [@tbcox23](https://www.ruby-forum.com/u/tbcox23)\
**Post date:** [August 10, 2015, 2:45pm UTC](https://www.ruby-forum.com/t/processing-expectations/242927/12 "2015-08-10T14:45:30Z")

</div>

I see. I don’t feel quite so stupid for having not found it myself,  
now!

---

<div class="post-metadata">

**Author:** ![tbcox23](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/t/b9bd4f/32.png) [@tbcox23](https://www.ruby-forum.com/u/tbcox23)\
**Post date:** [August 10, 2015, 3:37pm UTC](https://www.ruby-forum.com/t/processing-expectations/242927/13 "2015-08-10T15:37:57Z")

</div>

Okay. Time to fork on github, methinks, and start contributing  
documentation patches.
