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:

  1. Start with window positioned at $0\textrm{ ms}$ into the sample
  2. Start with window length of a fairly minimal value
  3. Count zero crossings across the current window to determine the frequency of the signal in that window
  4. Output various information about the window and the frequency it contains
  5. 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)
  6. 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
  7. 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.

  • $\begingroup$ How do you handle transitions of symbols (from one frequency to the next) using this approach? $\endgroup$ Jun 13, 2016 at 17:34
  • $\begingroup$ I was thinking that the frequency during a transition would be clearly outside of the tolerable deviation from the two FSK frequencies and so I would just reject it and advance the window again. $\endgroup$
    – Dan
    Jun 13, 2016 at 17:35
  • $\begingroup$ A zero crossing detector is very sensitive to noise (you may have multiple zeros crossings and not just one at your true crossings). In addition the analog channel that the signal passes through could also cause artifacts at the transition between symbols (change from one frequency to the next) that would affect the robustness of your approach. That said, if you want to make this work in minimum noise conditions with ideal (Wideband, no multipath and minimum distortion channels) then you can add hysteresis to your crossing detectors and validate your algorithm with both tones alone. $\endgroup$ Jun 13, 2016 at 17:41
  • $\begingroup$ My lunch break is over but hope this was helpful! $\endgroup$ Jun 13, 2016 at 17:44
  • $\begingroup$ Okay, that makes sense. I think I'd still like to get this approach working (with ideal signals) and then upgrade to more real world signals and be motivated to change to a more robust approach. I'm 99% sure I can pick up either tone alone (I've analyzed several pure sinusoidal signals (1,000 Hz, 2,000 Hz, 2,600 Hz, etc) and was able to recover the frequency perfectly. Everything seems to fall apart when I use a real FSK signal though... $\endgroup$
    – Dan
    Jun 13, 2016 at 17:45

1 Answer 1


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.

  • $\begingroup$ I imagine that my idea of sliding a variable-sized window across the sample (audio -- amplitude over time) and determining the frequency of each instance of the window is too naive. Can you help me understand why? A lot of the concepts you mentioned in your answer are a bit over my head at the moment. $\endgroup$
    – Dan
    Jun 13, 2016 at 15:28
  • $\begingroup$ It's not naive, just more processing intensive to be optimum. What is your other frequency you are trying to detect, you only mentioned 2600? And what is your baud rate? The delay and multiply approach is so simple, I will suggest a starting delay based on your parameters and you can try it out to understand it better. I think once you see it first hand you will see why I am suggesting that approach versus zero crossing detection. $\endgroup$ Jun 13, 2016 at 15:45
  • $\begingroup$ Thanks, Dan. I am currently focusing on a signal from the Emergency Alert System which is 520.83 bits per second with FSK frequencies 1,562.5 Hz and 2,083.3 Hz (nws.noaa.gov/directives/sym/pd01017012curr.pdf). I'm not currently concerned with optimum processing although I will almost certainly revisit that once I get my current approach working (and especially if I can't ever get it working haha). $\endgroup$
    – Dan
    Jun 13, 2016 at 16:31
  • $\begingroup$ Wow, those frequencies work out so nicely for such a discriminator- I am sure it is not a coincidence. I don't know what sampling rate you are using, but however many samples it works out to, use a delay of 960 us. So delay the signal 960 us and multiply it by the signal prior to that delay. Low pass filter the output so that you pass the 520.83 bits per second data but reject the 2x frequency components above 3KHz. This will work quite well for you: The result will be a frequency discriminator that has a cosine shape with the neg peak at 1562.5Hz and pos peak at 2083.3Hz, and 0 at 1822.9Hz. $\endgroup$ Jun 13, 2016 at 16:47
  • $\begingroup$ I'm using a 44,100 Hz sampling rate. When you say multiply the signal, do you mean to multiply the amplitude value that I'm getting from the quantized audio sample? $\endgroup$
    – Dan
    Jun 13, 2016 at 16:57

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.