2.3循环卷积
一般教材给出如下定义:
设有两个长度分别为M,L的序列x(n),h(n),(一般情况M,L≤N)那么两序列的N点循环卷积定义为:
。
(3)
从定义中很难找到两个序列x(n)、h(n)作N点的循环卷积的方法,原因在于定义式中的h((n-m))NRN(n)的含义不明白。
因此在做循环卷积之前必须先说明循环移位(有的教材称为圆周移位)。
x(n)为有限长序列,长度为M,序列以N为周期的循环移位定义为:
y(n)=x((n+m))NRN(n)(4)
循环移位可以用下列三个操作来完成。
首先将x(n)以N为周期作周期延拓得到周期序列了X(n)-x((n))N;然后将周期序列X(n)左移m位,得
X(n)=x((n+m))N;最后取主值x((n+m))NRN(n)。
经过上述三个操作就完成公式(4)循环移位。
当表达式为y(n)=x((n+m))NRN(n),在第二步将周期序列X(n)右移m位,同理经过三个步骤可以求得循环右移序列。
[1]
2.4离散卷积
给定两个离散时间信号x(n)和v(n),x(n)和v(n)的卷积定义为:
(5)
式(5)右边的和是叫做卷积和。
对于所有整数n<0,如果x(n)和v(n)均为0,则当所有整数i<0时,x(i)=0,n-i<0(或n
0n=-1,-2,…
n=0,1,2,…
如果信号x(n)和v(n)以简单数学表达式给定,则只要将x(n)和v(n)带入式(6),就可由解析式计算卷积x(n)*v(n).[2]
3设计路线与方案
3.1设计路线
3.1.1循环卷积的设计路线
路线一利用序列延拓的方法计算循环卷积
利用延拓计算循环卷积的方法中最重要的是理解计算式中循环的特点,用序列循环截取的例子说明设计思路:
例:
设有如图1(a)序列x(n),序列长为4,求y(n)=x((n+3))6R6(n)
根基定义第一步,将序列x(n)以6为周期作周期延拓,得到图1(b)。
接着按照第二步将图1(b)的周期序列左移三位,得到图1(c)。
最后取一个周期的主值,也就是用序列R6(n)与图1(c)的序列相乘,图1(d)虚框所包围的便是要求的序列。
[3]
在例子中序列先延拓后平移再截取的计算方法,是在信号与系统学习时理解卷积计算的最佳方法,在用计算机语言去实现的时候需要用到动态数组,具体的步骤如下:
1输入xv两序列;
2将v序列以原点对称翻折到负半轴;
3将v序列以N为周期延拓;
4将x序列与v序列对应项相乘后求和
5得到的结果为y序列的第一项
6将v序列接着向正半轴平移一个单位,重复步骤④,得到的是y序列之前得到结果的后一项;
7重复步骤⑥,直到平移的次数为选定的循环周期N;
8得到y序列中的N个项即为循环卷积所得结果;
路线优点:
设计思路易于理解,能够体现出循环卷积计算的本质。
路线缺点:
由于序列须要延拓,一是占用了更多的计算机存储空间,二是要计算出最短延拓长度以减少计算机计算量和存储空间,这样使算法复杂。
路线二利用序列非延拓的方法计算循环卷积
通过例子分析出,卷积的计算规律。
例:
两个长度为三的序列x,v按照周期N=3做卷积,运算参与的项为x[0],x[1],x[2],v[0],v[1],v[2],将其代入到循环卷积计算式
(7)
中得,
y[0]=x[0]v[0]+x[1]v[2]+x[2]v[1]
y[1]=x[0]v[1]+x[1]v[0]+x[2]v[2]
y[2]=x[0]v[2]+x[1]v[1]+x[2]v[0]
通过例子,可以发现规律:
以x序列作为排列顺序,和式由两部分组成,第一部分是x的编号小于等于y的编号时,v的编号是y的编号减去x的编号;另外一部分是x的编号大于y的编号时,v的编号是在y的编号减去x的编号基础上,再加上周期N
这个规律适用于所有以0为赋值起点序列的循环卷积计算。
路线优点:
这种方案在计算机算法中应该是最好的一种,因为这种方案不需要序列延拓,而是通过计算的项之间的规律完成算法。
这样就节省了很大的存储空间,同时也确定了序列的长度,这样可以在计算机中精确的把握计算的规格。
路线缺点:
规律递推的方法不便于去便理解循环卷积的计算,同时算法不易理解。
路线对比:
路线一从原理方面入手,路线二从算法方面入手,由于在计算机的编写中,一个函数是可以封装的,人们只能看见其功能和外部接口,而不需要考虑其内部结构,所以原理不用体现在算法的设计中,只需要计算次数最少,所占存储空间最少,算法规格可以明确的路线即可。
所以最终选择路线二。
3
3.1
3.1.1
3.1.2离散卷积的设计方案
路线一通过离散卷积内部规律计算
首先举出一个长度为三和一个长度为四的序列作离散卷积的例子:
设x序列的内容为x[0],x[1],x[2],v序列的内容为v[0],v[1],v[2],v[3],则通过离散卷积公式
(8)
所得结果为,
y[0]=x[0]v[0]
y[1]=x[0]v[1]+x[1]v[0]
y[2]=x[0]v[2]+x[1]v[1]+x[2]v[0]
y[3]=x[0]v[3]+x[1]v[2]+x[2]v[1]
y[4]=x[1]v[3]+x[2]v[2]
y[5]=x[2]v[3]
通过这个例子可以看出对于y序列中的一项编号是和式中每一项两个序列编号之和。
路线优点:
这个规律非常明显,而且非常易于实施,只要限定好x和v两个序列的编号上下限,然后利用计算机语言中for循环就可以解决。
路线缺点:
规律性的结论需要原始公式去证明,不但增加了工程难度,同时若规律出现了错误,亦加大了实际设计的工程量。
方案二利用FFT计算离散卷积
利用FFT技术,也就是用循环卷积来替代离散卷积。
由于循环卷积是对有限项求和,特别适合于计算机运算,在速度上比直接计算离散卷积要快得多,所以通过循环卷积来计算离散卷积具有较大的优越性。
由定义可知,循环卷积是离散卷积的周期延拓序列的主值序列。
因此,为了避免混叠,其必要条件是将序列都补零点,补到序列长度至少为离散卷积结果的长度,然后计算循环卷积,这时循环卷积就能代表离散卷积的结果。
由上分析可知,要求两个长度为的序列的离散卷积时,可先将这2个序列补N-1个零点使其变为两个长度均为2N-1的序列,然后分别求出它们的离散付里叶变换(简称DFT),再相乘起来,最后再求其离散付里叶反变换,从而获得长度为2N-1的两个序列的循环卷积,它就是原来两个长度为的序列的离散卷积。
由于循环卷积可使用DFT的快速算法FFT来计算,则当序列x(n)和h(n)的长度分别为N和M时,离散卷积y(n)的计算可按以下
步骤进行:
(1)将x(n)和h(n)都补零至L点,L=N+M-1;
(2)计算x(n)的L点FFT,即x(k)=FFT[x(n)];
(3)计算h(n)的L点FFT,即H(k)=FFT[h(n)];
(4)计算Y(k)=X(k)H(k);
(5)计算Y(k)的反变换,即y(n)=IFFT[X(k)
(1)将x(n)和h(n)都补零至L点,L=N+M-1;
(2)计算x(n)的L点FFT,即x(k)=FFT[x(n)];
(3)计算h(n)的L点FFT,即H(k)=FFT[h(n)];
(4)计算Y(k)=X(k)H(k);
(5)计算Y(k)的反变换,即y(n)=IFFT[X(k)H(k)]。
路线优点:
图1为实现快速卷积的运算过程,可见实现这一快速卷积的过程共需两次FFT和一次IFFT,相当于三次FFT运算;而对于一般的离散系统,由于其特征已知,则由h(n)计算H(k)这一步骤可提前进行,并将数据存储起来,而不必每次实时计算,故该FFT算法过程仅需一次FFT和一次IFFT,相当于两次FFT运算,从根本上减少了复数乘法运算次数,提高了信号处理的实时性。
依据FFT的运算公式,则需2(
log2L)次复数乘法运算。
另外第(4)步还需要次复数乘法运算,故整个运算过程中复数乘法运算次数为:
4L(1+log2L).显然,随着N值得增大,其运算量与直接进行时域运算相比,要快很多。
而且当N越大,L值就越大,用循环卷积计算的优越性就越明显。
[4]
路线缺点:
显然通过C++语言去实现FFT算法的难度已经远远超过了离散卷积本身的难度。
如果采用MATLAB语言编写则可直接运用其FFT算法来计算,那么这条路线便是最优方案。
路线对比:
路线一是从简单算法方面入手,路线二则是从高级算法入手,可以说在高级计算机语言的设计中,若存在两者的函数,两者都是最佳方案。
但是由于采用C++语言进行编写,其不包含FFT算法,若再采用路线二则加大了工程量,增加了计算难度,所以最终采用易于实现的路线一。
3.2设计方案
3.2.1设计步骤
1分析设计任务及要求;
2安装设计所需的语言软件;
3分析离散卷积和循环卷积的计算方法及二者的区别;
4将离散卷积和循环卷积转变为满足计算机逻辑的计算方法;
5用流程图的方式体现该逻辑方法;
6按照流程图的顺序编写计算机语言;
7编写结束进行调试;
8如调试失败则通过断点分析存储数据的情况,修改原语句;如调试成功,则
路线⑨;
9将初步完成的程序交付用户使用,并继续修改至便于用户实用的界面;
10完成设计报告,并写出自己的收获与心得
3.2
3.2.1
3.2.2设计流程图
(附录一)
1
2
3
3.1
3.2
3.2.1
3.2.2
计算机语言代码
#include//引用IO定义文件
#include//引用必要的头文件t
usingnamespacestd;//声明域名空间
intmain()//主程序定义
{
intCircleConvolution();//声明套用的循环卷积y函数y
intDiscreteConvolution();//声明套用的离散卷积y函数y
charselect;//定义用于不同功|能选择的字符
cout<<"WelcometotheCaclulatorofConvolution!
\n"<cout<<"EntertheCircleConvolutionmode,pleasepress'C'or'c'.\nEntertheDiscreteCovolutionmode,pleasepress'D'or'd'.\nIfyouwanttoexit,youcanpressanykey."<cin>>select;//输入选择模式的字符
if(select=='C'||select=='c')CircleConvolution();
if(select=='D'||select=='d')DiscreteConvolution();
elseexit(0);//直接退出程序
return0;
}
intDiscreteConvolution()//定义离散卷积函数y
{
floatx[100]={0},v[100]={0},y[200]={0};//定义参与运算的序列并对其初始化
inth,i,j,k;//定义运算所用到的参数y,其中Dh&k用于循-环,i&j定义序列长度
charselect;//定义用于不同功|能选择的字符
intmain();//声明用到的函数名
cout<<"\n\nWelcometotheCalculatorofDiscreteConvolution!
\n";
cout<<"Pleaseinputthelenghthofsequencex:
i.\n";//对x序列长度进行定义
cin>>i;
cout<<"\nPleaseinputthecontentofsequencex.\n";//开始对x序列进行赋值¦
for(h=0;h<=i-1;h++)
{
cout<<"x["<cin>>x[h];
};
cout<<"\nPleaseinputthelenghthofsequencev:
j.\n";//对v序列长度进行定义
cin>>j;
cout<<"\nPleaseinputthecontentofsequencev.\n";//开始对v序列进行赋值¦
for(h=0;h<=j-1;h++)
{
cout<<"v["<cin>>v[h];
};
for(k=0;k<=j+i-2;k++)//对序列x和序列v进行离散卷积运算,得到序列y
for(h=0;h<=i-1;h++)
if(k>=h)
y[k]+=x[h]*v[k-h];
cout<<"\nTheresultofthecalculationis:
\n";//显示结果
for(h=0;h<=i+j-2;h++)
cout<<"y["<cout<<"\n\nIfyouneedanotherservice,pleasepress'S'or's'.\nIfyouwanttoexit,youcanpressanykey."<cin>>select;
if(select=='S'||select=='s')
main();//回到主程序
else
exit(0);//直接退出程序
return0;
};
intCircleConvolution()
{
floatx[100]={0},v[100]={0},y[100]={0};//定义参与运算的序列并对其初始化
inth,i,j,k,n;
//定义运算所用到的参数y,其中h&k用于循-环,i&j定义序列长度,n定义循-环周期
charselect;//定义用于不同功能选择的字符
intmain();//声明用到的函数
cout<<"\n\nWelcometotheCalculatorofCircleConvolution!
\n";
cout<<"PleaseinputthePeriodofCircleCalculationn.\n";//输入循-环卷积y的循-环周期
cin>>n;
cout<<"\nPleaseinputthelenghthofsequencex:
i.\n";//定义序列x的长度
cin>>i;
for(;i>n;)//限制序列x的长度小于周期n
{
cout<<"\nPleaseinputanothernumbernotmorethanthePeriodn.\n";
cin>>i;
}
cout<<"\nPleaseinputthecontentofsequencex.\n";
for(h=0;h<=i-1;h++)//对序列x进行D赋3值¦
{
cout<<"x["<cin>>x[h];
};
cout<<"\nPleaseinputthelenghthofsequencev:
j.\n";//定义序列v的长度
cin>>j;
for(;j>n;)//限制序列v的长度小于周期n
{
cout<<"\nPleaseinputanothernumbertoadapttothecalculation.\n";
cin>>j;
}
cout<<"\nPleaseinputthecontentofsequencev.\n";//对序列v进行赋值¦
for(h=0;h<=j-1;h++)
{
cout<<"v["<cin>>v[h];
};
for(h=0;h<=n-1;h++)//对序列x和序列v进行循环卷积运算,得到序列y
for(k=0;k<=n-1;k++)
if(h<=k)
y[k]+=x[h]*v[k-h];
else
y[k]+=x[h]*v[k-h+n];
cout<<"\nTheresultofthecalculationis:
\n";//显示结果
for(h=0;h<=n-1;h++)
cout<<"y["<cout<<"\nIfyouneedanotherservice,pleasepress'S'or's'.\nIfyouwanttoexit,youcanpressanykey."<