I have a quadrature (IQ - 0..255) data byte stream that I receive from an rtl-sdr dongle as IQIQIQIQ.....

That means that negative values are < 128 and positive values >= 128

I am attempting to decode a 2FSK signal:

To normalize the data I do the following and get a resulting complex number with values ranging between -1 and 1 ... (more or less)

 Real = (I - 127.4) * 1/128 
 Imag = (Q - 127.4) * 1/128

I demodulated with GNU Radio: enter image description here

enter image description here

The scope plot correspond to the data that I get from a CC1101 tranceiver with an Arduino: 00000000011101110100010001110111010001000111011101000100010001110100010001000111011101000

This is the parameters for the CC1101 (SmartRF Studio) enter image description here

The project is for a USB receiver to receive and identify signals from pager buttons. Unfortunately the CC1101 only works well for about 60% of the pagers. That is why I want to use a RTL-SDR dongle and do my own demodulation.

GNURadio is great but it is a few hundered MB there is no minimal install ;-(

I would like to use the Goertzel algorithm to demodulate but it requires Real(float) input (not complex). If this is possible I could translate and compile the source to C or Delphi and create an executable that is maybe 1MB is size and a lot easier to deploy.

How do I prepare/use this data for the Goertzel algorithm? Here is the prototype of the Goertzel C# code posted in another thread, that I want to use.

float goertzel(int numSamples,int TARGET_FREQUENCY,int SAMPLING_RATE, float* data)

It requires the data as an array of float. float* data https://dsp.stackexchange.com/questions/23749/normalizing-the-magnitude-of-goertzel-filter-c

From reading the code I can see that it is not an array containing complex numbers (just an array of float) and I don't know how to get my IQ data into the format required by the Goertzel algorithm.

Finally my appologies and thanks to Marcus Müller (answered below) who spent a lot of time and effort to answer my original post in great detail, which had some incorrect, incomplete and confusing information, which I have now updated to be more complete.

  • 1
    $\begingroup$ The Goertzel algorithm doesn't require real input. It works just fine with complex inputs. $\endgroup$
    – Jason R
    Feb 14, 2018 at 12:50
  • $\begingroup$ If you're going to convert to C (certainly not Delphi, totally unsuited for signal processing), then it's absolutely no use that you start with a C# goertzel implementation that uses real instead of complex numbers. $\endgroup$ Feb 14, 2018 at 19:54
  • $\begingroup$ And: if you're going to compile software yourself to get a smaller file: GNU Radio without anything but the runtime, gr-fft, gr-filter and maybe gr-digital really isn't very large, and relatively easy to compile yourself :D $\endgroup$ Feb 14, 2018 at 19:55

1 Answer 1


Goertzel is defined on complex numbers $I + jQ$, not real numbers. So, directly apply Goertzel to your baseband.

Your 1/128 scaling is unnecessary, since that's only a factor and doesn't contribute to the decision.

Also, your -127.4 (which almost certainly is a misunderstanding of some sorts) just kills the bias of unsigned integers coming out the device. Since that all ends up in the (not evaluated) DC bin of your DFT, you don't have to do that, either, and don't gain anything by doing so, either (numerical accuracy of floating point operations aside, but with any reasonable observation length, i.e. any reasonable bit rate, your accuracy cannot be degraded - after all, you start with just 8 bit of precision).

So, take these numbers, and feed them into your Goertzel, just converted to the data type your Goertzel implementation needs.

Another note: don't let yourself be fooled, computers are fast. 1 MS/s is not very much, and you need at most a full symbol-length FFT, i.e. a $\frac{10^6}{2400}=416\frac23\approx 417$-point FFT (yes, there's FFTs for non-power of two) will also take nearly no time. I've just benchmarked this, on my really elderly AMD CPU:

FFT benchmark N=417

I only got 60,000 transforms per second, corresponding to 60,000 baud – that's only 25 as fast as you would actually need, so that's OK, but a bit disappointing.

Now, note that FFTs work better, the smaller the prime factors of their length is. Under that point of view, 1 MS/s is a terrible choice – it's not a multiple of 2400 /s. So, instead, use a much smaller sampling rate that is a multiple of 2.4 kHz. What about 240 kHz? That would give you 100-FFTs, and of these, I get 1.3 million per second (you'd only need 2400 per second to follow your bits!).

So, the question really is why you came to the conclusion you needed 1MS/s for your 2400 bd signal in the first place. Unless it's absolutely unreasonably wide-spaced in spectrum, that's a waste of sampling rate. Your Dongle supports a wide range of sampling rates, among these multiples of your nominal baud rate – use such to keep your effort in range, or resample.

So, generally, regarding the question how to preprocess the data:

Use something that gives you samples that you can work with. I do like GNU Radio (that's what I used to build above flow graph), and it comes with all the tools you need. Even a weak personal computer-style CPU (like the ARM in a Raspberry Pi) will have plenty enough horsepower so that you can do a full FFT. Don't reinvent the wheel, though, but use existing implementations.

Of course, for detection of one of two potential frequencies, a Goertzel is actually more appropriate then an FFT. But if you're clever, you realize that actually you can get away with two Goertzels, one of which covers every frequency above 0, one every frequency below 0. That's not really any more efficient than a 3-point DFT, of which you just ignore the DC bin. So, I'd argue:

  • take your samples; don't scale or shift them, because that's just cosmetics (DC offset doesn't matter to a "is there more energy left or right in the spectrum, neither does scaling).
  • Like anyone who strives for acceptable SNR, you'd filter to only consider the part of the spectrum where there might be your signal. Use a real-tapped (ie. spectrum-symmetrical) band pass filter to
    • suppress the DC component we didn't suppress, alongside with LO leakage
    • select your bands of interest from noise
  • Notice that above filter might optimally be (depends on whether you only consider an AWGN channel) the conjugate pulse shaping filter, shifted up with a cosine
  • Decimate the output of that filter. You can decimate very drastically here, just make sure if aliasing occurs, it always happens within the same band where the original energy came from. If you're not sure you can achieve that, only decimate to Nyquist rate.
  • Circularly shift in frequency (by multiplying with a $e^{j\frac{2\pi}{x}t} $) so that one of your tones now is at DC, and the other is not.
  • Your 2 Goertzels now collapse to the full 2-DFT, which really collapses to
    • First bin: Moving average of samples
    • Second bin: Difference of successive samples
  • $\begingroup$ Thank you very much for this useful information. It did not solve the question that I had, but most likely solved issues that I was still going to run into. $\endgroup$ Feb 14, 2018 at 19:47
  • $\begingroup$ Then I don't understand the question that you have! Can you elaborate? $\endgroup$ Feb 14, 2018 at 19:51
  • $\begingroup$ Ah I see, you've edited your question; I'll need time to read that. $\endgroup$ Feb 14, 2018 at 19:52

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.