# FSK Modulation in Python

I am currently writing a script that uses I/Q data to do FSK modulation.

My question is about the FFT plot. I expected to have a peak at 1Hz and 2Hz which are the frequencies that represent binary 0 and 1, however I have peaks at 2 and 4Hz.

I initially suspected that I was using the FFT freq function incorrectly however I set the bin size equal to the number of samples and also defined my sample freq.

Any idea why my FFT frequencies are off?

import numpy as np
import matplotlib.pyplot as plt
from scipy.fftpack import fft

binary_in = "10011010" # binary input string

sr = 200  # sampling rate
ts = 1.0/sr # sampling interval
time = (np.arange(0,len(binary_in)*1,ts))-ts  # time for plotting entire FSK signal
step = int((len(time))/(len(binary_in)))  # temporal step size for FSK signal relating to each bit
T = np.arange(0,step/100,0.01) # time for calculating sinusoids

f1 = 1  # frequency for binary 0
f2 = 2 # frequency for binary 1

coeff = 2*np.pi

## I and Q defined to have freq of (f1-f2)/2
I = np.cos(coeff*((f1-f2)/2)*T)
Q = np.sin(coeff*((f1-f2)/2)*T)

## In the case of a binary 1 I and Q would be mixed with the LO freq in the following way:
mixer1 = I*np.cos(coeff*((f1+f2)/2)*T)
mixer2 = Q*np.sin(coeff*((f1+f2)/2)*T)

out1 = mixer1+mixer2   #Output is added to give a FSK signal equal to approx. cos(-f2*t)

## In the case of a binary 1 I and Q would be mixed with the LO freq in the following way:
mixer3 = Q*np.cos(coeff*((f1+f2)/2)*T)
mixer4 = I*np.sin(coeff*((f1+f2)/2)*T)

out2 = mixer3+mixer4  #Approx. sin(f1*t)

final_fsk = []
bin_plot = []

ones = [1] * step
zeros = [0] * step

j=0 # index for assigning FSK signal to plotted output list

## Assign correct output freq. based on binary input
for i in range(0, len(binary_in)):
j=j+1
if binary_in[i] == "1":
final_fsk[(j*step-(step-1)):(j*step)] = out1
bin_plot[(j*step-(step-1)):(j*step)] = ones
else:
final_fsk[(j*step-(step-1)):(j*step)] = out2
bin_plot[(j*step-(step-1)):(j*step)] = zeros

fourier = np.fft.fft(final_fsk)
freq=np.fft.fftfreq(len(final_fsk),ts)
# n = np.arange(0, len(fourier))
# period = len(fourier)/sr
# freq = n/period

plt.subplot(311)
plt.title("Binary Input")
plt.plot(np.arange(0, (len(binary_in)),ts), bin_plot)
plt.ylabel('Amplitude')

plt.subplot(312)
plt.title(f"Output FSK for {binary_in}")
plt.plot(time, final_fsk, 'b')
plt.ylabel('Amplitude')
plt.xlabel('Time (s)')

plt.subplot(313)
plt.title("FSK FFT")
plt.plot(freq, np.abs(fourier), 'b')
plt.ylabel('Amplitude')
plt.xlabel('Freq (Hz)')

plt.show()



• It would be useful to zoom in the FSK FFT plot to the frequencies of interest. Commented Feb 16, 2023 at 18:19
• You're treating your I and Q oscillator separately when mixing them with your baseband signal. That doesn't work: That's supposed to be a complex multiplication! Commented Feb 16, 2023 at 19:50
• I think this code will get much easier if you just abandon trying to write I and Q separately, but treat them as Real and Imaginary part of a complex signal: then, your tone generation simply becomes $e^{j 2\pi \pm f\cdot t}$, and that's it. Commented Feb 16, 2023 at 19:52
• @MarcusMüller: I assume he's trying to simulate physical mixers in the time domain, so there's not going to be complex anything.
– Eeyn
Commented Feb 17, 2023 at 3:48
• Mixer1 is multiplying a 1 Hz sinusoid by a 3 Hz sinusoid, that will give you components at the sum and difference frequencies of 4 and 2 Hz, and nothing at 1 Hz. Likewise for Mixers 2, 3, and 4. You add them in various combinations, but adding will not change any frequencies. WIth the right choice of signs when you add them you might cancel the 4 Hz in your final result, but no summing or subtracting will bring back the 1 Hz that never left any of the mixers.
– Eeyn
Commented Feb 17, 2023 at 3:51