众所知周,法国的大神傅里叶可能通信和电子信息相关行业最头疼的人物,这个闹过革命,随拿破仑远征埃及的埃及总督,在回到法国的后,向法国皇家科学院递交过一篇关于三角级数论文,但是非常可惜,这篇论文并未引起大数学家高等数学中绕不开的大神——拉格朗日的注意。然而数十年过去了,随着无线电报机的出现,傅里叶变换焕发了新的生机。现在,本科大学生的两门课《信号与系统》和《数字信号处理》就是以傅里叶变换为基本研究对象。作为通信和电子信息专业的专业基础课,其上手难度可想而知,尤其是在刚学《数字信号处理》时,甚至连DFT都算不清楚。今天,笔者就送给大家一个用来专门计算《数字信号处理》中卷积和DFT的工具。
这里需要声明一下,这个计算器使用python写成,目前只能在python的环境下运行,所以没有装python的同学们要把python装上,另外,还需要tkinter和numpy、matplotlib三个库,利用pip可以轻松安装。(不会安装的同学们也不用担心,我会在下一篇文章中讲述windows如何安装pip和下载相应库)
当然,有兴趣的同学可以自己写一个卷积和dft的函数,这算是信号专业的入门了。
首先,我们展示一下计算器的外观,并简要介绍一下布局:
这个计算器最左边有两个CheckButton,用来选择计算器的主要功能:卷积还是dft,再到右边,从上往下看,先是两个Entry,分别作为待处理数据的输入口,初始化时,两个输入口都存在,当我们选中dft时,则只有输入口1,而选择卷积时,则输入口1和输入口2都存在。
往下看是两个RadioButton,用来选择是否做出计算结果的图像,以方便使用者观看。下面是四个Button,作用等到使用方法时一一解释。
最下面是一个text,用来展示输入数据和计算结果,当选择卷积功能时,则会出现 两个text,用来分别显示两组输入数据。
下面,我们来介绍一下如何使用这个计算器。
dft功能:
首先,我们要在功能区选择dft功能,这时上方会显示“已选择dft”,如果不小心将dft和卷积同时选上,则会出现“不能同时选择”。
第二步,就可以输入数据了,在输入口1输入数据,每输入完一个数据就敲击一下“Enter”键,该输入就会在下方的text中显示,并且自动空格。因为dft是数据运算,所以如果你输入的不是数字,上方就会出现红色提示“输入只能是数字”。
当数据输入完后,可以选择是否做图,这一步并不是很关键,因为不论是否做图,都会呈现数值结果。待数据输入完后,请点击输入结束,这时会出现一个“-->”符号,然后点击计算,这时如果你选择了做图,就会出现图像,如果选择不做图,则什么都没有出现,那么计算数值呢?别忘了有一个显示结果,轻点“显示结果”,就会出现一个text,而计算结果就在上面。
下面,我们来操作一遍:
计算的图像如下:
大家可能会奇怪,为什么计算出的图像有三个,其实只要仔细看一下纵坐标就可以知道,第一幅图是原序列的图像,第二幅是卷积后序列的实部,第三幅是卷积后序列的虚部,由于二维图像无法同时绘制出复数(a+bi),所以将实部和虚部分开展示。
现在,我们来介绍卷积功能。
卷积功能:
卷积和dft在操作方法上的的不同之处在于,卷积有两个原始序列,所以有两个Entry,分别作为原始序列x1和x2的输入口。
输入数据的方法同dft,这里就不再重复了,不同的是,卷积的两个原始序列分别显示在了两个不同text中,这样方便观看。数据输入完后,就点击输入结束——计算——展示结果,然后就会出现计算结果。
如图,上述数据计算结果:
现在,我们再来动手操作一遍:
我们已经介绍完了计算器的使用方法,现在我将代码开源,欢迎大家来完善功能。笔者编程能力有限,这个程序算是笔者学习gui的小试牛刀了。
import numpy as np
from tkinter import *
import matplotlib.pyplot as plt
import seaborn
root=Tk()
draw=IntVar()
draw.set(1)
f4=Frame()
lf1=LabelFrame(relief=GROOVE,text='是否做图:')
ra1=Radiobutton(lf1,text='是',variable=draw,value=1)
ra2=Radiobutton(lf1,text='否',variable=draw,value=0)
ra1.grid(row=1,column=1)
ra2.grid(row=1,column=2)
la2=Label(f4,text="请选择")
la3=Label(f4,text="功能")
la2.pack()
la3.pack()
ck1=IntVar()
ck2=IntVar()
ck1.set(0)
ck2.set(0)
cb1=Checkbutton(f4,text="dft",variable=ck1,width=4,indicatoron=1,justify='left')
cb2=Checkbutton(f4,text="卷积",variable=ck2,width=4,indicatoron=1,justify='left')
cb1.pack()
w1=Toplevel()
w1.title('计算结果')
w1.withdraw()
cb2.pack()
f4.pack(side='left')
t1=Text(width=30,height=10)
t2=Text(width=15,height=10)
t3=Text(w1,width=30,height=20)
t3.pack()
a=StringVar()
a.set('输入口1')
b=StringVar()
b.set('输入口2')
f1=Frame()
f1.pack()
la1=Label(f1,text='计算器',width=24)
la1.pack()
f2=Frame(relief=SUNKEN,bd=2)
f2.pack()
en1=Entry(f2,width=24,textvariable=a)
en1.pack()
en2=Entry(f2,width=24,textvariable=b)
en2.pack()
f3=Frame()
lf1.pack()
def c1(a):
v=a
t1.insert('insert',v)
t1.insert('insert',' ')
en1.delete(0,END)
def c1b(a):
v=a
t2.insert('insert',v)
t2.insert('insert',' ')
en2.delete(0,END)
def c2(event):
if ck1.get()==1 and ck2.get()==0:
c=t1.delete("0.0","end")
else:
c=t1.delete("0.0","end")
c=t2.delete("0.0","end")
def c3(event):
if ck1.get()==1 and ck2.get()==0:
t1.insert('insert','-->')
elif ck1.get()==0 and ck2.get()==1:
t1.insert('insert','-->')
t2.insert('insert','-->')
def c4(a):
if ck1.get()==1 and ck2.get()==0:
y=t1.get("0.0","end")
u=0
k=0
m=0
n=0
p=[]
j=[]
h=[]
x=y.split(' ')
x.remove('-->n')
m=len(x)
while n<m:
x[n]=int(x[n])
n=n+1
w=np.fft.fft(x)
print(w)
t3.insert('insert',w)
while k<m:
j.append(w[k].imag)
k=k+1
while u<m:
h.append(w[u].real)
u=u+1
draw2=a
if draw2==1:
fig,ax=plt.subplots(3,1)
ax[0].stem(range(m),x)
ax[0].set_xlabel('k')
ax[0].set_ylabel('h(k)')
ax[1].stem(range(m),h)
ax[1].set_xlabel('k')
ax[1].set_ylabel('H(k)real')
ax[2].stem(range(m),j)
ax[2].set_xlabel('k')
ax[2].set_ylabel('H(k)imag')
plt.show()
elif ck1.get()==0 and ck2.get()==1:
y1=t1.get("0.0","end")
u=0
k=0
m=0
n=0
p=[]
j=[]
h=[]
x1=y1.split(' ')
x1.remove('-->n')
m=len(x1)
while n<m:
x1[n]=int(x1[n])
n=n+1
y2=t2.get("0.0","end")
u=0
k=0
m=0
n=0
p=[]
j=[]
h=[]
x2=y2.split(' ')
x2.remove('-->n')
m=len(x2)
while n<m:
x2[n]=int(x2[n])
n=n+1
y3=np.convolve(x1,x2)
print(y3)
t3.insert('insert',y3)
draw1=a
if draw1==1:
x3=len(x1)+len(x2)-1
plt.stem(range(x3),y3)
plt.show()
elif ck1.get()==1 and ck2.get()==1:
t1.delete("0.0","end")
t1.insert('insert',"不能同时选择多种功能")
elif ck1.get()==0 and ck2.get()==0:
t1.delete("0.0","end")
t1.insert('insert',"请选择一种功能")
def c5(o,i):
if o=='1':
if i not in '0123456789 -->输入口':
la1.config(text='输入只能是数字',fg='red')
return False
else:
la1.config(text='计算器',fg='black')
return True
def c7():
if ck1.get()==1 and ck2.get()==0:
la2.config(text="已选择")
la3.config(text="dft")
t2.pack_forget()
t1.config(width=30,height=10)
en2.pack_forget()
elif ck1.get()==0 and ck2.get()==1:
la2.config(text="已选择")
la3.config(text="卷积")
t1.config(width=15,height=10)
t2.pack(side='right')
en2.pack()
elif ck1.get()==1 and ck2.get()==1:
la2.config(text="不能同")
la3.config(text="时选择")
t2.pack_forget()
t1.config(width=30,height=10)
elif ck1.get()==0 and ck2.get()==0:
la2.config(text="请选择")
la3.config(text="功能")
t2.pack_forget()
t1.config(width=30,height=10)
c6=en1.register(c5)
en1.config(validate='all',validatecommand=(c6,'%d','%S'))
c8=en2.register(c5)
en2.config(validate='all',validatecommand=(c8,'%d','%S'))
b1=Button(f3,width=5,text='清空')
b2=Button(f3,width=5,text='计算')
b3=Button(f3,width=6,text='输入结束')
b4=Button(f3,width=6,text='显示结果',command=w1.deiconify)
b1.grid(row=1,column=3)
b2.grid(row=1,column=2)
b3.grid(row=1,column=1)
b4.grid(row=1,column=4)
en1.bind('<Return>',lambda event: c1(en1.get()))
en2.bind('<Return>',lambda event: c1b(en2.get()))
b1.bind('<Button-1>',c2)
b3.bind('<Button-1>',c3)
b2.bind('<Button-1>',lambda event: c4(draw.get()))
cb1.config(command=c7)
cb2.config(command=c7)
f3.pack()
t1.pack(side='left')
root.mainloop()