ButterWorthFIlter(巴特沃斯濾波器)
一、背景
這種濾波器最先由英國工程師斯蒂芬·巴特沃斯(Stephen Butterworth)在1930年發表在英國《無線電工程》期刊的一篇論文中提出的。
二、特點
巴特沃斯濾波器的特點是通頻帶內的頻率響應曲線最大限度平坦,沒有起伏,而在阻頻帶則逐漸下降爲零。 在振幅的對數對角頻率的波特圖上,從某一邊界角頻率開始,振幅隨着角頻率的增加而逐步減少,趨向負無窮大。一階巴特沃斯濾波器的衰減率爲每倍頻6分貝,每十倍頻20分貝。二階巴特沃斯濾波器的衰減率爲每倍頻12分貝、三階巴特沃斯濾波器的衰減率爲每倍頻18分貝、如此類推。巴特沃斯濾波器的振幅對角頻率單調下降,並且也是唯一的無論階數,振幅對角頻率曲線都保持同樣的形狀的濾波器。只不過濾波器階數越高,在阻頻帶振幅衰減速度越快。其他濾波器高階的振幅對角頻率圖和低階數的振幅對角頻率有不同的形狀。
由圖可見,當N趨於無窮時,將會得到一個理想的低通濾波器
三、原理
1. 幅值平方
∣Ha(jw)∣2=1+(wcw)2N1
2. 拉普拉斯變換
令
s=σ+jω
其中,
σ=0
所以有,
w=js
重新帶入幅值平方
∣H(s)∣2=1+(wc2−s2)N1
H(s)=a0ωcN+a1ωcN−1s+...an−1ωc1sn−1+sNωcN
其中,
a0=1
設數組sb的各個元素爲上式分母中關於s的冪次方項的係數
sb={sb[0],sb[1],...sb[N]}={a0ωcN,a1ωcN−1,...an−1ωc1,1}
3. Z域變換(離散化)
由
z=esT=e2−sTe2sT
得到
s=T1lnz
3.1 線性化
試圖使用泰勒公式對s進行展開,但是lnz在z=0處沒有定義,於是令
z′=z+1z−1
於是
z=1−z′1+z′
對上式在z‘=0處泰勒展開,有
lnz=ln1−z′1+z′=2(z′+31z′3+51z′5...)
帶入s的定義,整理,得到
s=T1lnz=T2(z+1z−1+31z+1z−13+51z+1z−15...)
取一階近似,最終得到
s=T1lnz=T2(z+1z−1)=T2(1+z−11−z−1)
3.2 雙線性變換
雙線性變換就是使用這種一階估計法,將連續時間傳遞函數H(s)中的s替換成離散域中的z變量
即 Hd(z)=Ha(s)∣s=T2(z+1z−1)=Ha(T2z+1z−1)
進行整理後,最終得到
H(z)=a0ωcN(T2)N(1−z−1)N+a1ωcN−1(T2)N−1(1−z−1)N−1(1+z−1)1+...aN−mωcN−m(T2)N−m(1−z−1)N−m(1+z−1)m+...aN(1+z−1)NωcN(1+z−1)N
最終將上式寫成如下:
H(z)=den[0](z−1)0+den[1](z−1)1+...den[N](z−1)Nnum[0](z−1)0+num[1](z−1)1+...num[N](z−1)N
上式中的序列num、den就是離散化的目標。
四、巴特沃斯濾波器的設計(數字濾波器實現)
1.確定參數
需要確定參數如下:
- passF:通帶截止頻率
- stopF:阻帶截止頻率
- fs:採樣頻率
- rp:通帶最大衰減
- rs:阻帶最小衰減
2.計算預畸參數
雙線性變換具有從根本上避免脈衝響應不變法中的頻率混疊現象,缺點是引入了頻率失真,因此,引入預畸變的概念,即在雙線性變換之前,進行預畸來校正頻率失真的問題,使得進行雙線性變換之後的頻率與預期一致。
2.1 預畸原理
由
s=T2(z+1z−1)=σ+jΩ
使用下式進行變量代換
z=ejw
得到
s======T2(z+1z−1)T2(1+e−jw1−e−jw)T2(1+(cosω−jsinω)1−(cosω−jsinω))T2(2+2cosω2jsinω)T2(4cos22ωj2sin2ωcos2ω)T2jtan2ω=σ+jΩ
所以有
Ω==T2tan2ωT2tanπf
2.2 計算第一步確定的參數預畸值
passΩ=tanfsπ∗passF
stopΩ=tanfsπ∗stopF
3. 計算低通濾波器階數
由
rp=10lg(1+(ΩcpassΩ)2N)rs=10lg(1+(ΩcstopΩ)2N)
可得,階數N
N=21=lgpassΩstopΩlg100.1rp−1100.1rs−1
4. 計算截止頻率\Omega_c
由
rs=10lg(1+(ΩcstopΩ)2N)
有
Ωc=(100.1rs−1)2N1stopΩ
5. 確定分子分母系數數組長度length
length=N+1
6. 查表,獲取拉氏變換下傳遞函數的分母系數
H(s)=a0ΩcN+a1ΩcN−1s+...an−1Ωc1sn−1+sNΩcN
即計算:
a0ΩcN+a1ΩcN−1s+...an−1Ωc1sn−1+sN
其中,關於a可查表獲得
最終返回數組sb數組sb的各個元素爲上式分母中關於s的冪次方項的係數
sb={sb[0],sb[1],...sb[N]}={a0ωcN,a1ωcN−1,...an−1ωc1,1}
代碼如下:
for(int i = 0; i < length; i++)
{
//a0*Wc^N+a1*Wc^(N-1)*S+....+an-1*Wc^0*S^(n-1)
returnSb[i] = g_butterPb[length - 1][i] * pow(Wc, length-i); //計算係數
}
returnSb[length] = 1.0; //最高次冪的係數爲1
7. 雙線性變換
根據前面得出來的公式:
H(z)=a0ωcN(T2)N(1−z−1)N+a1ωcN−1(T2)N−1(1−z−1)N−1(1+z−1)1+...aN−mωcN−m(T2)N−m(1−z−1)N−m(1+z−1)m+...aN(1+z−1)NωcN(1+z−1)N
7.1 利用楊輝三角計算多項式(1-z^-1)、(1+z^-1)次方項內各自關於(z^-1)的係數
圖片來源:https://blog.csdn.net/NDHuaErFeiFei/article/details/88379163
- 對於 (1+z^-1),
(z−1+1)1=(z−1+1)2=(z−1+1)3=1∗z−1+1∗11∗(z−1)2+2∗z−1+1∗11∗(z−1)3+3∗(z−1)2+3∗z−1+1∗1
以3次方爲例,楊輝三角函數輸出[1,3,3,1]
- 對於 (1-z^-1)=(-z^-1+1)
(−z−1+1)1=(−z−1+1)2=(−z−1+1)3=−1∗z−1+1∗11∗(z−1)2−2∗z−1+1∗1−1∗(z−1)3+3∗(z−1)2−3∗z−1+1∗1
以3次方爲例,楊輝三角函數輸出[-1,3,-3,1]
楊輝三角函數代碼:
/*======================================================================
* 函數名: pascalTriangle
* 函數功能:計算楊輝三角的第N行的值(數組),該系列值爲(x+1)^N的係數,
* 加改進(x-1)^N的係數
*
* 變量名稱:
* N - 楊輝三角第N行,N=0,1,...,N
* symbol - 運算符號,0——(x+1)^N,1——(x-1)^N
* vector - 返回數組,楊輝三角的第N行的值
*
* 返回值: void
*=====================================================================*/
void pascalTriangle(
int N,
int symbol,
int *vector)
{
vector[0] = 1;
if(N == 0)
{
return;
}
else if (N == 1)
{
if(symbol == SYMBOL_ADD)
{
vector[1] = 1;
}
else
{
vector[0] = -1; //如果是減號,則第二項係數是-1
vector[1] = 1;
}
return;
}
int length = N + 1; //數組長度
int *temp = (int *)malloc(sizeof(int) * length);
/*
int temp[length]; //定義中間變量
*/
temp[0] = 1;
temp[1] = 1;
for(int i = 2; i <= N; i++)
{
vector[i] = 1;
for(int j = 1; j < i; j++)
{
vector[j] = temp[j - 1] + temp[j]; //x[m] = x[m-1][n-1] + x[m-1]
}
if(i == N) //最後一次不需要給中間變量賦值
{
if(symbol == SYMBOL_SUB) //運算符爲減號
{
for(int k = 0; k < length; k++)
{
vector[k] = vector[k] * pow((float)-1, length - 1 - k);
}
}
return;
}
for(int j = 1; j <= i; j++)
{
temp[j] = vector[j];
}
}
if (temp != NULL)
free(temp);
}
具體實現:
for(int i = 0; i <= length; i++)
{
for(int j = 0; j<= length; j++)
{
tempCoef1[j] = 0; //tempCoef1和tempCoef2進行初始化
tempCoef2[j] = 0;
}
//i :1 - z^(-1)冪次數
//otherN : 1 + z^(-1)冪次數
otherN = length - i;
pascalTriangle(3, SYMBOL_SUB, tempCoef1); //利用楊輝三角計算1 - z^(-1)冪的係數
pascalTriangle(otherN, SYMBOL_ADD, tempCoef2); //利用楊輝三角計算1 + z^(-1)冪的係數
coefficientEquation(tempCoef1, i+1, tempCoef2, otherN+1); //兩個多項式相乘,求其係數
}
7.2 利用楊輝三角計算(1-z^-1)^{N-m}*(1+z^-1)^{m}多項式關於z^-1項的係數
最終的係數數組長度:L=N-m+1+m+1-1=N+1
計算的目標多項式:
(1−z−1)N−m(1+z−1)m
最終得到如下形式:
C0(z−1)N+C1(z−1)N−1+...+CN(z−1)0
返回:
return [C0,C1,C2…CN]
核心函數coefficientEquation()實現兩個係數數組相乘後得到的多項式數組
/*======================================================================
* 函數名: coefficientEquation(整數)和coefficientEquation2(浮點數)
* 函數功能:計算多項式相乘的係數
*
* 變量名稱:
* originalCoef - 原來的係數數組,計算後的係數也存儲在該數組內
* N - 上面係數對應的多項式係數個數
* nextCoef - 與原數組相乘的數組的係數
* nextN - 上面係數對應的多項式係數個數
*
* 返回值: void
*=====================================================================*/
void coefficientEquation(
int *originalCoef,
int N,
int *nextCoef,
int nextN)
{
int *tempCoef = (int *)malloc(sizeof(int) * (N+nextN-1));
/*
double tempCoef[N + nextN - 1]; //中間變量
*/
for(int i = 0; i < N + nextN - 1; i++)
{
tempCoef[i] = originalCoef[i]; //中間變量初始化
originalCoef[i] = 0;
}
//乘完之後有多少項= N + nextN - 1
//設 (1,2,3) ===>N=3
// (3,4,5) ===>nextN=3
// original[0]=1*3 | (z^-1)^4
// original[1]=2*3+1*4 | (z^-1)^3
// original[2]=3*3+2*4+1*5 | (z^-1)^2
// original[3]=2*5+3*4 | (z^-1)^1
// original[4]=3*5 | (z^-1)^0
for(int j = 0; j < nextN; j++)
{
for(int i = j; i < N + nextN - 1; i++)
{
//printf("%d %d ",tempCoef[i-j],nextCoef[j]);
originalCoef[i] += tempCoef[i-j] * nextCoef[j];
//printf("%d %d \n",originalCoef[i],i);
}
}
if (tempCoef != NULL)
free(tempCoef);
}
7.3 計算H(z)分母系數
分母形式:
den[0](z−1)0+den[1](z−1)1+...den[N](z−1)N
其中,
den[0]=(T2)0∗CN∗sb[0]+(T2)1∗CN∗sb[1]+...+(T2)N∗CN∗sb[N]
den[1]=(T2)0∗CN−1∗sb[0]+(T2)1∗CN−1∗sb[1]+...+(T2)N∗CN−1∗sb[N]
......
den[N]=(T2)0∗C0∗sb[0]+(T2)1∗C0∗sb[1]+...+(T2)N∗C0∗sb[N]
最終計算結果:
return [den[0],den[1], … den[N]]
實現代碼:
//分母
length=N;
for(int i = 0; i <= length; i++)
{
for(int j = 0; j <= length; j++)
{
denominator[j] += pow(Fsx2, i) * (float)tempCoef1[length - j] * sb[i];
}
}
7.4 計算H(z)分子係數
(1)如7.2一樣,由楊輝三角計算得到(1+z^-1)^N的係數[tmpC0,tmpC1, … tmpCN]
(2)分子形式:
根據雙線性變換得到的分子:
ωcN(1+z−1)N
可以寫成如下形式:
num[0](z−1)0+num[1](z−1)1+...num[N](z−1)N
其中,
num[0]=tmpCN∗sb[0]
num[1]=tmpCN−1∗sb[0]
......
num[N]=tmpC0∗sb[0]
(3)實現代碼:
length=N;
for(int i = 0; i <= length; i++)
{
//分子係數
if(i == 0)
{
for(int j = 0; j <= length; j++)
{
numerator[j] = sb[0] * tempCoef2[length - j];
}
}
}
7.5 係數歸一化(分子分母同除)
目標:使分母常數項=1
即:
num=num/den[0]
den=den/den[0]
實現代碼:
length=N;
for(int i = 0; i <= length; i++)
{
//係數歸一化,分母的常數項爲1
for(int i = length; i >= 0; i--)
{
numerator[i] = numerator[i] / denominator[0];
denominator[i] = denominator[i] / denominator[0];
}
}
7.6 設計完成
得到了num和den數組,即得到如下形式的H(z)
H(z)=den[0](z−1)0+den[1](z−1)1+...den[N](z−1)Nnum[0](z−1)0+num[1](z−1)1+...num[N](z−1)N