First off: No, I can't explain the strange behaviour when sliding. However, changing the frequency offset will definitely lead to big frequency jumps in the signal to be decoded (that's why you're doing that, after all), and the structures in charge of recovering timing will not like that; that's kind of expected.
Unfortunately, I haven't found even a single decent book on GNU Radio
There is none. GNU Radio's been evolving rapidly, and so none of the people close to the development "core" circles had the time to write something like that.
and hence most of my references are based on some random websites including the official GNU Radio page.
Well, the official GNU Radio website certainly isn't random, but the right place to go!
I'll refer to GNU Radio terms such as hierarchical block, connection etc from here; if you don't understand all of those, I'd recommend going through the official Guided Tutorials!
the GFSK demod block, which apparently doesnt have any documentation except its source code
Well, there's a documentation comment in the source code, which does get extracted by Sphynx; however, that documentation isn't visible in GRC. We need to work on that!
However, the documentation comment is pretty verbose:
Hierarchical block for Gaussian Minimum Shift Key (GFSK)
demodulation.
The input is the complex modulated signal at baseband.
The output is a stream of bits packed 1 bit per byte (the LSB)
Args:
samples_per_symbol: samples per baud (integer)
verbose: Print information about modulator? (bool)
log: Print modualtion data to files? (bool)
Clock recovery parameters. These all have reasonble defaults.
Args:
gain_mu: controls rate of mu adjustment (float)
mu: fractional delay [0.0, 1.0] (float)
omega_relative_limit: sets max variation in omega (float, typically 0.000200 (200 ppm))
freq_error: bit rate error as a fraction
float:
Basically, you can fully adjust the parameters of the timing recovery and FM demodulator contained within the hierarchical block.
Now, obviously, that did not work out for you this far! I think you should go ahead and recreate the hierarchical flow in a GRC graph:
Reading the same source, you'll find
self.connect(self, self.fmdemod, self.clock_recovery, self.slicer, self)
which means:
As the hierarchical block that gfsk_demod is,
- connect it's input port to the input of
fmdemod
,
- connect
fmdemod
's output to the input of the clock_recovery
, and then
- take the output of the clock recovery block and convert it to discrete numbers (using the
slicer
).
A few lines up in the source code you'll find how these are parameterized.
I suggest you first feed in your RX signal to a "stock" Quadrature Demod block. Mathematically, that will take the input signal, and convert it to its phase velocity (i.e. the "momentaneous frequency" of each sample, expressed as multiples of the sampling rate). Visualize the output with a scope/time sink! Does it look right, i.e. do you see alternating "high" and "low" values, in a roughly Gaussian pulse shape?
If that's the case, replicate the Muller&Mueller Clock Recovery block in GRC (or replace it with the new, shiny PFB clock recovery, which deals a lot better with real-world signals); does the output look right?
Use "decimating FIR" to reduce the output rate by samples_per_symbol
; does that still look like your original bit stream as "soft" values? If it does, binary slice the result.