What kind of FSK signal is this and what demodulation technique can I use to demodulate it. Why is the phase of the 1's changing like that and how does that effect how it needs to be demodulated. I will be programming the demodulation in python.

I am trying to low pass at 900Hz and then generate the envelope but I am not getting the desired result.

import numpy as np
import matplotlib.pyplot as plt
import scipy.signal as signal
from scipy.fftpack import fft, rfft, rfftfreq, irfft
import scipy.signal.signaltools as sigtool

from scipy.io import wavfile
fs, data = wavfile.read('jx3p.wav')

h = signal.firwin(numtaps = 300, cutoff = 900, fs=fs)
data = signal.lfilter(h, 1, data)

data = np.abs(sigtool.hilbert(data))

import matplotlib.pyplot as plt

enter image description here


Here is the code to multiple the signal by the same signal with a 12 sample delay and then add the resulting signal with a 3 sample delay of itself.

import matplotlib.pyplot as plt
from scipy.fftpack import rfft, rfftfreq
from scipy.io import wavfile
from scipy.signal import blackman

fs, data = wavfile.read('jx3p.wav')

x = 286600
y = x+22050
data = data[x:y]

m = max(abs(data))
data = [d/m for d in data]

delay = 12
data1 = tuple(a*b for a,b in zip(data[delay:],data))

filter_delay = int(delay/4)
data2 = tuple(a+b for a,b in zip(data1[filter_delay:],data1))



enter image description here

  • $\begingroup$ that doesn't look like FSK at all, at first sight. Also, symbols of different length are very atypical. $\endgroup$ Nov 21, 2019 at 12:26
  • $\begingroup$ You will need to tell us where you got that signal from, how you conclude it's FSK, and how you came to think that what you see is 1 or 0. $\endgroup$ Nov 21, 2019 at 12:31
  • $\begingroup$ @Marcus Müller Its the patch data outputted from the Roland JX3P synth from 1983. $\endgroup$
    – Baz
    Nov 21, 2019 at 12:33
  • $\begingroup$ that sounds like there's a technical spec for that? $\endgroup$ Nov 21, 2019 at 12:35
  • $\begingroup$ I can see how it is FSK- the 0 is clearly the higher frequency FSK symbol and the 1, although not as clean of a frequency symbol is a lower frequency none-the less. $\endgroup$ Nov 21, 2019 at 12:37

1 Answer 1


For this specific waveform as described, the following would demodulate the signal from the Frequency Shift Modulated Input into a square wave output:

FSK demod

This works given the duration of the "0" symbol is 12 samples by using a "delay and multiply" frequency discriminator. The multiply will have a strong double frequency component that needs to be filtered out (and can be used for timing recovery if the timing was not synchronous to the symbol rate as further detailed below), and this is done with a simple delay of 3 samples and add, which would provide a null at twice the frequency of the zero symbol. The "1" symbol as shown by the OP is a low frequency symbol in the FSK modulation, and observe how in this case it is approximately formed using the same half cycle of the higher frequency "0" symbol followed by a zero magnitude for a full one and a half cycle duration before continuing with the negative half cycle followed by a zero magnitude for a duration for one and a half cycles (thus it is 4 times the duration of the "1" symbol.) This is convenient as the output for the case of demodulating the "0" symbol would be naturally nulled given the block diagram above without the need for subsequent filtering.

Notice with this approach that at the start of each symbol, if the symbol is a 1 the initial first half cycle result will be a sin^2 always regardless of the next symbol, but if the symbol is a zero the result will be zero throughout the duration of the zero. So in this case with this particular waveform, the way the lower frequency is constructed actually helps for a cleaner demodulation, since it provides the nulled response if the delay is the duration of the zero symbol (Maybe the waveform designer had this in mind which is why it has the peculiar form for the lower frequency).

The "Square Wave" output would be further filtered through an "integrate and dump" process prior to making a final symbol decision: sum over 12 samples in the symbol duration and decide if the result if greater or less than a decision threshold. If greater a "0" was demodulated, and if less a "1" was demodulated. Given the atypical longer duration between the symbols, four "1"'s in a row is the presence of an actual "1" symbol.

This structure can then be readily used with standard timing recovery for recovering the symbol clock boundaries. If the symbol clock is synchronized (coherent) to the waveform sample clock, the timing recovery is quite trivial. Two options show below:

TIming Recovery

The strong double frequency component can be used for symbol timing recovery for the case of the symbols no being coherent to the sample clock. If they are coherent then symbol timing recovery is simply aligning the sample edge to a start of a symbol which can easily done with a simple threshold detection at the Demodulated FSK output as shown in the block diagram (for lower SNR conditions it would be more robust to average the estimated timing position even such a threshold approach was used, such that the timing position does not change abruptly based on the result of any one threshold detection). Once detected, a modulo 12 counter starts, and every time the counter rolls over the symbol is selected at the output. The more robust timing approach would compare the roll-over timing clock and the FSK output threshold detector impulses to establish a timing error (another multiplier would do this), which would then be accumulated in a timing loop to adjust the actual start time of the cyclical timing recovery counter (again only need to do that for a very robust solution in the presence of low SNR, for the waveform as shown all that would not be necessary).

See this link where I detail non-coherent FSK demodulation approaches that further how a delay and multiply forms a frequency discriminator:


With this graphic in mind; a delay multiply is a frequency discriminator:

enter image description here

Optional coherent techniques would have a 3 dB SNR advantage but require more processing. This could be done by matched filter correlation to the two symbols (have replicas of the two symbols and multiply and accumulate to each one to decide which symbol is observed). The delay and multiply followed by delay and add is notably simpler.

  • 1
    $\begingroup$ By delay I mean the time duration for the zero symbol-- yes I didn't compute your math but if you say the 0 symbol is 12 samples long then yes that would be perfect. Multiply your signal with the 12 sampled delayed version of itself. If you do this, can you post that result? There will be post filtering needed as well. $\endgroup$ Nov 21, 2019 at 16:23
  • 1
    $\begingroup$ I updated my post with the implementation, would be good to show/confirm the final result. $\endgroup$ Nov 21, 2019 at 20:07
  • 1
    $\begingroup$ Yes it does- notice now when a 1 is sent it always stays high. There is additional high frequency noise components so could benefit from a better digital low pass filter but this would be simplest; You would average all 12 samples in a symbol before deciding if it was a "1" or a "0" (36 sample average in that case), which is itself a low pass filter process. As long as the residual noise does not cross a decision threshold half way between the average of your "0" values and the average of your "1" values (which would mean an error) this would be fine. $\endgroup$ Nov 21, 2019 at 22:05
  • 1
    $\begingroup$ In either case that pilot should be used to establish the symbol clock. I am guessing your symbol clock is completely coherent to your sampling clock since it is locally generated which simplifies things. The complexity comes down to how robust your timing recovery needs to be (how good is the SNR of the signal for all use cases?). If low SNR you want a timing recovery that continues to monitor (and correct its time) throughout the whole packet. If high SNR it is quite trival. I would think that the pilot sequence would also be used to frame the whole data packet as well. Good luck! $\endgroup$ Nov 22, 2019 at 16:36
  • 1
    $\begingroup$ @Baz Just to be clear on the formuila for the null location consistent with what you did but not what we wrote: it would be T/2 where T is the period of the frequency of the null. In samples it is $f_s/(2f)$ where $f_s$ is the sampling rate and $f$ is the frequency to null.---- so as you computed 44100(7350*2) = 3 samples. $\endgroup$ Nov 23, 2019 at 15:41

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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