In
the follow, we will try to follow as closely to the Mathematica
code as possible (see
this and this),
in other words, "literal translation," even if it
might not be optimal with Python, because we want to
help those getting start with Python and Jupyter
notebook to have the same sense of familiarity as with
Mathematica notebook, and vice versa.
import numpy as np
import scipy as sp
import matplotlib.pyplot as plt
import sounddevice as sd
msg = [1, 1, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0
, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1, 1]
# while a simpler function can be written, this is trying to do literal translation
# from Mathematica for illustration
def unitbox(t,m):
if np.abs(t-m)< 0.25 :
r=1
else:
r=0
return r
def digitsig(t,messg):
s=sum([messg[m-1]*unitbox(t,m) for m in np.arange(len(messg))+1])
return s
srate=8 ; deltt=1./srate
tarr=np.arange(0,len(msg)+1,deltt)
sig=[digitsig(u,msg) for u in tarr]
sigplot=plt.plot(sig)
plt.show(sigplot)
deltF=1./(len(tarr)*deltt); npt=len(sig)
farr=(np.arange(npt)-1-np.floor(npt/2))*deltF
plt.plot(farr)
We will stick to Mathematica convention of Fourier (physics based), hence we use ifft (inverse fft) of numpy so that it is consistent with our Mathematica Tutorial results. In addition, we include the constant factor.
sigFT=np.fft.ifft(sig)*(deltt/np.sqrt(2*np.pi))
sigFTsh=np.fft.fftshift(sigFT)
sigFTAbs=np.absolute(sigFTsh)
sigFTdB=20*np.log10(sigFTAbs)
plt.plot(farr,sigFTdB)
def carrier(fc,t):
c=np.cos(2*np.pi*fc*t)
return c
srate2 = 1100; deltt2 = 1./srate2;
tarr2 = np.arange(0,len(msg)+1,deltt2)
sig2 = np.array([digitsig(u,msg) for u in tarr2])
fc = 125.
carr2 =np.array(carrier(fc, tarr2))
sigcarr = sig2*carr2
imgsz= plt.figure(figsize=(8,8*0.1));sigcarrplt=plt.plot(sigcarr)
plt.show(sigcarrplt)
sndsr=22050;
sd.play(sigcarr,sndsr)
deltF=1./(len(tarr2)*deltt2); npt2=len(sigcarr)
farr2=(np.arange(npt2)-1-np.floor(npt2/2))*deltF
plt.plot(farr2)
sigcarrFT=np.fft.ifft(sigcarr)*(deltt2/np.sqrt(2*np.pi))
sigcarrFTsh=np.fft.fftshift(sigcarrFT)
sigcarrFTAbs=np.absolute(sigcarrFTsh)
sigcarrFTdB=20*np.log10(sigcarrFTAbs)
specSC=plt.plot(farr2,sigcarrFTdB)
plt.show(specSC)
sigcarrdBmax=np.ma.max(sigcarrFTdB)
plt.xlim(fc-5,fc+5);plt.ylim(sigcarrdBmax-60,sigcarrdBmax+5)
plt.plot(farr2,sigcarrFTdB)
noise = np.random.normal(0.,0.5,len(sigcarr));
# this is very bad noise by normal comm stardard
sigcarrnoise = sigcarr + noise;
imgsz= plt.figure(figsize=(8,8*0.1));plt.plot(sigcarrnoise)
sndsr=22050;
sd.play(sigcarrnoise,sndsr)
sigcarrNFT=np.fft.ifft(sigcarrnoise)*(deltt2/np.sqrt(2*np.pi))
sigcarrNFTsh=np.fft.fftshift(sigcarrNFT)
sigcarrNFTAbs=np.absolute(sigcarrNFTsh)
sigcarrNFTdB=20*np.log10(sigcarrNFTAbs)
sigcarrNdBmax=np.ma.max(sigcarrNFTdB)
plt.xlim(fc-20,fc+20);plt.ylim(sigcarrNdBmax-60,sigcarrNdBmax+5)
specSCN=plt.plot(farr2,sigcarrNFTdB)
plt.show(specSCN)
sigoutArr=[]; sigFTfiltArr=[]
for bw in [5,10,20,30]:
filtBP=[1 if np.absolute(np.absolute(f)-fc) <= 0.5*bw else 0 for f in farr2]
sigFTfiltered0 = sigcarrNFTsh*np.array(filtBP);
# plt.xlim(fc-20,fc+20)
sigFTfiltArr.append(sigFTfiltered0)
sigFTfilt1 = np.fft.ifftshift(sigFTfiltered0);
sigout=np.real(np.fft.fft(sigFTfilt1))*deltF*np.sqrt(2*np.pi)*len(sigFTfilt1)
plt.figure(figsize=(4,3)); plt.xlim(0,3)
plt.plot(tarr2,sigout)
sigoutArr.append(sigout)
Observe that we include those constant factors for fft and ifft only to show that the signal amplitude is back where it should be. In practice, where amplitude doesn't matter, the factors are often neglected.
for s in sigFTfiltArr:
plt.figure(figsize=(6,3)); plt.xlim(fc-20,fc+20);
plt.plot(farr2,10*np.log10(np.absolute(s)))
although we already had the filtered result with bw=30 in sigFTfiltArr list, we can do it again for reinforced learning
bwbp = 30;
filtBP=[1 if np.absolute(np.absolute(f)-fc) <= 0.5*bw else 0 for f in farr2]
sigFTfiltered0 = sigcarrNFTsh*np.array(filtBP);
sigFTfilt1 = np.fft.ifftshift(sigFTfiltered0);
sigout=np.real(np.fft.fft(sigFTfilt1))*deltF*np.sqrt(2*np.pi)*len(sigFTfilt1)
plt.figure(figsize=(8,0.15*8))
plt.plot(sigout)
the above should be compared with the noisy signal before filtering
sigReceived=[];
sigP=sigout**2; # power
sigoutPFT=np.fft.fft(sigP)
sigoutPFTsh=np.fft.fftshift(sigoutPFT)*deltt2/np.sqrt(2*np.pi)
for bwlp in [2,5,10,20] :
filtLP=[1 if np.absolute(f) <= bwlp else 0 for f in farr2]
sigFTfiltLP= np.fft.ifftshift(sigoutPFTsh*np.array(filtLP));
sigoutBB=np.real(np.fft.ifft(sigFTfiltLP))*deltF*np.sqrt(2*np.pi)*len(sigFTfiltLP)
plt.figure(figsize=(8,0.15*8))
sigReceived.append(plt.plot(sigoutBB))
How to use Fourier array shift functions:
fftshift -> RotateRight
ifftshift -> RotateLeft
examples below
a =np.fft.fftshift(range(11))
print(a)
np.fft.ifftshift(a)