其實所有的數(shù)字濾波器都逃不過延時加權求和的六字真諦,但我們之前討論的FIR濾波器,其截止頻率等參數(shù)都是事先已經定好;如果事先已知有用信號與噪聲信號的頻率相差較遠,用這種濾波器是可以完成濾波的。
但如果噪聲與有用信號頻率相差不大或者噪聲近似高斯白噪聲呢,這個情況該如何面對?這意味著濾波器參數(shù)不能再保持固定不變,也就意味著加權系數(shù)不能是定值,而是應該根據信號的某些統(tǒng)計特性改變。
維納濾波器就是這樣一種自適應濾波器,根據維納霍夫方程可以求得維納濾波器的加權系數(shù)。但是,維納濾波器有其本身的局限性,在求解加權系數(shù)時,需要知道期望信號與輸入信號的互相關函數(shù),輸入信號的自相關函數(shù)。這導致計算過程設計到矩陣運算,并且濾波器不適合非平穩(wěn)信號,非平穩(wěn)信號的相關性不是僅僅跟時間有關。
為了簡化計算量,LMS不通過求均方誤差的數(shù)學期望最小去獲得最佳權值,而是直接對均方誤差求最小值獲得權值,然后不斷地重復這個過程,直到接近最優(yōu)維納解。
維納的權值更新方程:
所以,LMS算法相比于前面講過的FIR濾波器,不僅要對輸入信號進行加權求和,還要求濾波器輸出信號與期望信號的差值,然后根據差值更新權值信息。
LMS算法的FPGA實現(xiàn)結構里,我們依然使用FIR的基礎模塊,基礎模塊實現(xiàn)加權和權值更新,然后通過generate for調用基礎模塊,對所有結果求和;頂層求誤差;
示例中使用的輸入信號為帶噪信號,期望信號是帶噪的語音信號。
base_generator.v
基礎模塊實現(xiàn)過程,當時使用的是Altera的芯片,所以設置了如果FPGA_FAMILY為“ALTERA”,會調用Altera的乘法器IP核進行權值增量的計算和加權計算過程。
wire signed [X_W+E_W-1:0] coef_tmp ; // xin * err
reg signed [X_W+E_W:0] coef_nxt ; // wout(k+1)
reg signed [X_W+E_W-1:0] coef ; // wout(k)
reg signed [X_W-1:0] x_r ; // Save xin value for later calculation
reg signed [E_W-1:0] e_r ; // Save err value for later calculation
reg [2:0] state ;
//因為調用了Altera的IP核,不使用Altera就用*代替
generate if (FPGA_FAMILY == "ALTERA")
begin
mult16 m1(
.dataa (x_r),
.datab (e_r),
.result (coef_tmp)
);
mult16 m2(
.dataa (x_r),
.datab (wout),
.result (yout)
);
end
else
begin
assign coef_tmp = x_r * e_r;
assign yout = x_r * wout;
end
endgenerate
always @ (posedge clk or negedge rst_n)
begin
if (!rst_n)
begin
state <= 'd0;
coef_nxt <= 'd0;
coef <= 'd0;
wout <= 'd0;
update <= 'd0;
x_r <= 'd0;
e_r <= 'd0;
end
else
case (state)
3'd0:
begin
if (en)
begin
state <= 3'd1;
x_r <= xin ;
e_r <= err ;
end
update <= 1'b0;
end
3'd1:
begin
coef_nxt <= coef_tmp + coef;
coef <= coef_nxt[X_W-1+E_W:0];
wout <= coef_nxt[X_W-1+E_W-:W_W];
state <= 3'd0;
update <= 1'b1;
end
default: state <= 3'd0;
endcase
end
always塊里面,首先保存輸入信號和誤差信號,然后進行加權結果輸出,權值更新。
lms16_order.v中間模塊部分代碼,首先使用generate for調用了基礎模塊,然后對計算輸出結果進行了擴位;這里沒有使用Shift-RAM代替輸入信號的延遲數(shù)組,也沒有在求和過程使用流水線;這兩個地方其實是需要改進的。
genvar i;
generate for (i = 1; i < 16; i = i + 1) begin:order
base_generator
#(
.FPGA_FAMILY(FPGA_FAMILY),
.X_W(X_W ) ,
.E_W(E_W ) ,
.W_W(W_W )
)
bg_inst
(
.clk (clk),
.rst_n (rst_n),
.en (en),
.xin (xin_arr[i-1]),
.err (err),
.yout (yout_arr[i]),
.wout (wout[(((i+1)*W_W)-1)-:W_W]),
.update (b_update[i])
);
assign yout_exp[i] = {{4{yout_arr[i][X_W+W_W-1]}}, yout_arr[i]};
end
endgenerate
always @ (posedge clk or negedge rst_n)
begin
if (!rst_n)
begin
en <= 1'b0;
state <= 3'd0;
yout_ori<= 'd0;
update <= 1'b0;
end
else
case (state)
3'd0:
begin
if (en_i)
begin
en <= 1'b1;
state <= 3'd1;
end
update<= 1'b0;
end
3'd1:
begin
en <= 1'b0;
if (b_update == 16'hffff)
begin
yout_ori <= yout_exp[0] + yout_exp[1] + yout_exp[2] + yout_exp[3] + yout_exp[4] + yout_exp[5] + yout_exp[6] + yout_exp[7] +
yout_exp[8] + yout_exp[9] + yout_exp[10] + yout_exp[11] + yout_exp[12] + yout_exp[13] + yout_exp[14] + yout_exp[15];
state <= 3'd0;
update<= 1'b1;
end
end
default: state <= 3'd0;
endcase
end
lms.v頂層實現(xiàn)部分代碼,對濾波器求和結果與期望信號求誤差信號,這里,u值采取了截位填充符號位的做法,信號右移N位,同時最高位填入N位符號位,誤差數(shù)據相當于除以2的N次方
lms16_order
#(
.FPGA_FAMILY(FPGA_FAMILY),
.X_W(X_W ) ,
.E_W(E_W ) ,
.W_W(W_W ) ,
.Y_W(D_W )
)
l_inst
(
.clk (clk) ,
.rst_n (rst_n) ,
.en_i (en_i) ,
.xin (xin) ,
.err (err_tmp),
.update (lms_up),
.xnxt () ,
.wout (wout) ,
.yout (lms_yout)
);
always @ (posedge clk or negedge rst_n)
begin
if (!rst_n)
begin
update <= 1'b0;
err <= 'd0;
d_r <= 'd0;
end
else if (lms_up)
begin
update <= 1'b1;
err <= d_r - lms_yout;
d_r <= din;
end
else
begin
update <= 1'b0;
end
end
always @ (*)
case (u)
8'd0: err_tmp <= err;
8'd1: err_tmp <= {err[E_W-1],err[E_W-1:1]};
8'd2: err_tmp <= {{2{err[E_W-1]}},err[E_W-1:2]};
8'd3: err_tmp <= {{3{err[E_W-1]}},err[E_W-1:3]};
8'd4: err_tmp <= {{4{err[E_W-1]}},err[E_W-1:4]};
8'd5: err_tmp <= {{5{err[E_W-1]}},err[E_W-1:5]};
8'd6: err_tmp <= {{6{err[E_W-1]}},err[E_W-1:6]};
8'd7: err_tmp <= {{7{err[E_W-1]}},err[E_W-1:7]};
8'd8: err_tmp <= {{8{err[E_W-1]}},err[E_W-1:8]};
8'd9: err_tmp <= {{9{err[E_W-1]}},err[E_W-1:9]};
8'd10: err_tmp <= {{10{err[E_W-1]}},err[E_W-1:10]};
8'd11: err_tmp <= {{11{err[E_W-1]}},err[E_W-1:11]};
8'd12: err_tmp <= {{12{err[E_W-1]}},err[E_W-1:12]};
8'd13: err_tmp <= {{13{err[E_W-1]}},err[E_W-1:13]};
8'd14: err_tmp <= {{14{err[E_W-1]}},err[E_W-1:14]};
8'd15: err_tmp <= {{15{err[E_W-1]}},err[E_W-1:15]};
default: err_tmp<= err ;
endcase