FSK analysis: only ever find one frequency I've trying to write this software as part of my amateur radio and SDR hobby for analyzing various two-level FSK signals.  I'm very new to DSP but have been having a lot of fun so far learning more about this subject.  My long-term idea is to try and automatically detect the mark and space frequencies, as well as the baud rate, in arbitrary signals (maybe this is incredibly naive -- I don't know).

Right now I'm trying to get this software working using a known signal, known mark and space frequencies, and known baud rate.  I've tested it with samples from sources like the Emergency Alert System, Bell 103, and even a pure $2600\textrm{ Hz}$ sine wave just to make sure that my frequency detection algorithm (very basic zero-crossing detection) works correctly.

I get back a $2600\textrm{ Hz}$ frequency when I test against the pure $2600\textrm{ Hz}$ sine wave sample so I'm at least somewhat confident about my ability to detect one particular frequency.  However, I can never extract the mark and space frequencies when I use two-level FSK samples.

What I'm using right now is a sliding window that starts at the beginning of a sample and moves along the sample, changing both the starting position of the window and the overall length of the window, in an effort to try and find a good fit.  But no matter what, I never see the mark or space frequency (actually, sometimes I will get a somewhat near read on one of the two frequencies but never both).

The project is pretty small if you want to have a look for yourself and give me some pointers (no pun intended).  Apologies for the amount of code in the Cli project right now.  I've put the code that I'm pretty confident about into their own classes and behind interfaces but my "work-in-progress" code is currently just living in the Main() method of the command-line interface.

Here is my current "window algorithm" in a nutshell:


Start with window positioned at $0\textrm{ ms}$ into the sample
Start with window length of a fairly minimal value
Count zero crossings across the current window to determine the frequency of the signal in that window
Output various information about the window and the frequency it contains
Increase the window length and repeat until the window length hits a maximum (maybe $1-10\textrm{ ms}$ in $0.25-1.0\textrm{ ms}$ increments -- all easily configured)
Once the maximum window length is reached, reset to the minimum window length, increment the window position a little, and repeat the varying window length frequency samples again
Repeat this until the maximum window position is reached


And here is a screenshot of the software running, where it was able to accurately detect one of the two FSK frequencies but not the other:



EDIT 1: Turns out I had a bad sample.  When I looked at a frequency spectrum plot I only saw one frequency peak, whereas a 2-level FSK spectrum should have two peaks. I found another sample and now my window algorithm is working much better.



EDIT 2: Still using the same algorithm but now with the new sample and a few very minor tweaks to the software am successfully decoding at least the first non-trivial amount of this NWR/NOAA SAME signal.  You can very clearly see the 1010 1011 preamble repeating at the beginning.



EDIT 3: Refactored a lot of the code and cleaned-up the Cli project so that it is actually just a Command-Line Interface.
You can make a very simple frequency discriminator by multiplying your received signal with a delayed version of itself, followed by a low pass filter. Adjust the delay to set the frequency range of your discriminator. This works because a multiplier followed by a low pass filter is a phase detector (multiply two sinusoids with same frequency and different phase and you will get the sum and the difference of the frequencies: the sum is the doubled frequency to filter out with the low pass filter and the difference is the phase). A change in phase versus a change in time is frequency, therefore the low pass filtered multiplication of the signal with a delayed version of itself will be proportional to the frequency over that particular interval. Choose your low pass filter and delay based on your symbol rate.

Optionally you can make a tuned discriminator with two pass band filters with your signal fed into the input of each one. The BW of the filter depends on your symbol rate. Follow each filter with a magnitude detector, and subtract one detector output from the other. The output of the subtractor will be your demodulated FSK.