I've been working on coding some FSK demodulation and clock recovery in C, since
GNUradio's documentation is terrible. My target bitstream is the control channel on the local Motorola Smartzone IIi system, which is 3600 baud. Ideally the code, when finished, will log calls and group associations to make pretty graphs (the system I have doing this currently is a terribly ghetto mess of Windows programs from 1998 and perl.)
The first thing I did was record samples of the stream in two ways: from the soundcard connected to the discriminator tap of my scanner, and from my
RTL-SDR dongle. I normalized both outputs into unsigned chars (uint8_t) for simplicity, then coded a quick histogram to make sure my two FSK levels were easily visible. It turns out they are!
8: ######
16: ########################
24: #######################################################
32: ###########################################################################################
40: ###################################################################################################
48: ########################################
56: #############
64: ###########
72: ##########
80: #########
88: #########
96: ########
104: ########
112: ########
120: ########
128: #######
136: ########
144: ########
152: ########
160: ########
168: ########
176: #########
184: ##########
192: ###########
200: ###############
208: #############################
216: ###########################################################
224: #################################################################################
232: ###############################################################
240: #################################
248: ############
The labels on the left are the base value of the bin being graphed, ie "192" is the sum of all samples from 192 to 199, inclusive. As you can see, there are lots of samples at both ends of the plot and very few in the center, which shows that our signal is clean.
After I had confirmed that my samples were in fact sensible, I gave some thought to clock recovery. Since both my streams were recorded at 44.1khz in order to be soundcard-compatible, there are just over 12 samples per symbol. However, since the number isn't exactly 12 as well as local oscillator differences, one can't simply determine the center of one symbol then extrapolate ad infinium from there.
There are entire books written on clock recovery schemes, but suffice to say this problem is generally solved by a PLL that gets 'nudged' forward or backwards in time based on the rate of change of the signal when the symbol is sampled. However, it's generally advantageous to try the simplest solution first.
I hacked up a quick sliding window sampling function that simply tries all 12 available offsets on the given "chunk" of samples (about 1/10th of a second worth), and determines which offset gives the lowest mean signal error. When the chunksize is small enough that the clock error during that period is less than one symbol wide, all the bits are successfully recovered. The process repeats at the next chunk, and no one is the wiser. Clearly this method isn't fast, but I was able to write and test it in an hour.
I was able to dump the binary stream from the samples recorded, however it turns out the data is interleaved and I can't find a spec document (Smartzone is a closed standard.) Unfortunately, until I figure that out, I'm stuck.