赞
踩
前言:在数字通信系统中插入一种参数可调的滤波器,以校正和补偿系统特性,减少码间干扰的影响,这种起补偿作用的滤波器称为均衡器。
目录
在无线通信系统中,由于通信的信道存在多径效应、信道带宽有限及信道特性本身的不完善等因素,导致数据通过信道时将不可避免地产生码间干扰,从而降低系统的性能,影响通信的质量。自适应均衡器是基于自适应均衡技术的装置,能够基于对信道特性的测量随时调整自身的系数,以适应信道特性的变化,消除码间干扰。
在实现自适应均衡器之前需要了解自适应算法。
LMS算法(Least Mean Square)根据均方误差准则设计的一种有效算法,它要求均方估计误差达到最小,即
式中,
LMS算法可以用下面一组公式递推来表示:
式中,W(n)为滤波器系数向量,也称权值;X(n)是输入信号组成的一组向量;y(n)是输出信号;d(n)是期望信号;e(n)是误差信号;u是加权向量更新时的步长因子,u越大,算法收敛越快,但同时收敛后的误差信号也越大;u越小,则算法的收敛速度越慢,但同时收敛后的误差信号也相应减小,稳态性能更好。
图1 LMS算法的一般实现结构
如果所有运算均串行执行,则完成一次权值更新需要2N次乘法运算、2N+1次加减法运算和N次移位运算。又因为LMS算法本身是严格的闭环系统,每次权值更新均需要在一个数据周期内完成,因此FPGA的系统时钟频率需要远高于数据速率,这对FPGA的时钟要求很高。
LMS算法的步骤虽然不能减少,但对滤波器系数更新的运算可以简化。符号LMS算法只给出梯度迭代的方向,而不具体给出数值,即:
在性能上不如LMS算法稳定,且误差相对较大,但运算速度增加、运算量减小并节约了硬件资源。
自适应均衡器的性能仿真主要分为两个方面:相同多径干扰条件下信噪比与系统性能改善情况,以及相同信噪比条件下多径干扰与系统性能改善情况。
Matlab代码如下:
- len = 20000;
- Tlen = 2000;
- step = 1/128;
-
- s = zeros(1,len);
- s1 = zeros(1,len);
- x = zeros(1,N);
- w = ones(1,N);
-
- s = randsrc(1,len);
- s1(2:len) = s(1:len-1);
-
- p = 0.1;
- SNR = [0:10];
- for db = 1:length(SNR)
- s2 = sqrt(1-p)*s + sqrt(p)*s1;
- s3 = awgn(s2,db,'measured');
- for i=N:len
- x(1:N) = s3(i:-1:i-N+1);
- y(i) = x*w';
- e(i) = s(i-3) - y(i);
- w = w + 2*step*sign(x)*e(i)';
- if y(i)>0
- y1(i) = 1;
- else
- y1(i) = -1;
- end
- if s3(i)>0
- y2(i) = 1;
- else
- y2(i) = -1;
- end
- end
- errornum1 = sum(y1(Tlen:end)~=s(Tlen-3:end-3));
- errornum2 = sum(y2(Tlen:end)~=s(Tlen:end));
- ber1(db) = errornum1/(len-Tlen);
- ber2(db) = errornum2/(len-Tlen);
- end
-
- subplot(121);
- semilogy(SNR,ber1,'+-',SNR,ber2,'-');
- xlabel('信噪比(SNR)');ylabel('误码率');title('信噪比与误码率的关系(P=0.1)');
- legend('有均衡器','无均衡器');
-
- db=10;
- p=[0:0.05:1];
- for m=1:length(p)
- s2=sqrt(1-m*0.05)*s+sqrt(m*0.05)*s1;
- s3=awgn(s2,db,'measured');
- for i=N:len
- x(1:N)=s3(i:-1:i-N+1);
- y(i)=x*w';
- e(i)=s(i-3)-y(i);
- w=w+2*step*sign(x)*e(i)';
- if y(i)>0
- y1(i)=1;
- else
- y1(i)=-1;
- end
- if s3(i)>0
- y2(i)=1;
- else
- y2(i)=-1;
- end
- end
- errornum1=sum(y1(Tlen:end)~=s(Tlen-3:end-3));
- errornum2=sum(y2(Tlen:end)~=s(Tlen:end));
- ber3(m)=errornum1/(len-Tlen);
- ber4(m)=errornum2/(len-Tlen);
- end
-
- subplot(122);
- semilogy(p,ber3,'+-',p,ber4,'-');
- xlabel('多径损耗(P)');ylabel('误码率');title('多径损耗因子与误码率的关系 (SNR=10dB)');
- legend('有均衡器 ','无均衡器');
图2 自适应均衡器对系统性能改善关系图
首先用matlab产生测试数据,将数据保存进txt文件中,利用modelsim仿真读取测试数据,再将仿真结果写入外部文件中,最后再matlab中对比FPGA结果和仿真结果。
Matlab代码如下:
- len = 2000;
- step = 1/128;
- N = 7;
- B = 16;
- p = 0.1;
- SNR = 10;
-
- s = randsrc(1,len);
- s1 = zeros(1,len);
- s1(2:len) = s(1:len-1);
- s2 = sqrt(1-p)*s + sqrt(p)*s1;
- s3 = awgn(s2,SNR,'measured');
-
- maxs3 = max(abs(s3));
- maxs = max(abs(s));
- m = max(maxs3,maxs)*1;
- s3 = s3/m;
- s = s/m;
- x = zeros(1,N);
- w = ones(1,N);
- Mw = 0;Me = 0;
-
- for i=N:len
- x(1:N) = s3(i:-1:i-N+1);
- y(i) = x*w';
- e(i) = s(i-3) - y(i);
- w = w + 2*step*sign(e(i)')*x;
- if max(abs(w))>Mw
- Mw = max(abs(w));
- end
- if max(abs(e))>Me
- Me = max(abs(e));
- end
- end
-
- fid =fopen('D:matlab\s_in.txt','w');
- for k=1:length(s_16)
- s_16_Bin = dec2bin(s_16(k) + (s_16(k)<0)*2^B,B);
- for q=1:B
- if s_16_Bin(q)=='1'
- tb = 1;
- else
- tb = 0;
- end
- fprintf(fid,'%d',tb);
- end
- fprintf(fid,'\r\n');
- end
- fprintf(fid,';');
- fclose(fid);
-
- fid = fopen('D:matlab\s3_in.txt','w');
- for k=1:length(s3_16)
- s3_16_Bin = dec2bin(s3_16(k) + (s3_16(k)<0)*2^B,B);
- for q=1:B
- if s3_16_Bin(q) == '1'
- tb = 1;
- else
- tb = 0;
- end
- fprintf(fid,'%d',tb);
- end
- fprintf(fid,'\r\n');
- end
- fprintf(fid,';');
- fclose(fid);
-
- fid = fopen('D:matlab\e_out.txt','w');
- fprintf(fid,'%8d\r\n',e_16);
- fprintf(fid,';');
- fclose(fid);
仿真中显示权值最大绝对值介于1、2之间,误差最大绝对值介于1、4之间,输出数据最大绝对值介于1、4之间,这些数据范围为FPGA中定点数小数位置设置了依据。
在此不对定点数的定义做介绍,单介绍其加减法运算。
在verilog中,所有二进制数都当成整数来处理,所以当一个小数想要进行加减运算时需要先对其进行量化,扩展到整数形式,那么这就会产生量化误差问题,这里不对其过多讨论,而是重点讨论如何判断小数点的位置问题。在设计verilog程序时,我们需要有意地设计定点数的小数点位置,例如:两个二进制数00101与00110,逐位相加结果为01011,表示5+6=11;如果小数点均看成在最高位与次高位之间,即0∆0101、0∆0110、0∆1011,则表示0.3125+0.375=0.6875,结果也正确;但如果是0∆0101、00∆101进行加法预算,如果直接逐位相加,若小数点位置与第一个数相同,则表示0.6875,若小数点位置与第二个数相同,则表示1.375,显然结果不正确;从而说明二进制小数的加法与十进制一样,都需要对齐小数点。因为实际上计算的都是00101+00110,也就是量化后的二进制数据,如果不对齐小数点,则计算结果必然出错。
根据符号LMS算法及自适应均衡器的原理,运算步骤如下:
输入数据和权值均为16bit向量。由于X(n)和d(n)数据范围均在±1之内,故小数点位于15位与14位之间;W(n)的范围在±2之内,故小数点位于14位与13位之间;y(n)及误差e(n)的范围在±4之内,故小数点在13位与12位之间;
Verilog代码如下:
- module Equalizer
- (
- input rst ,
- input clk ,
- input signed [15:0] Xin ,
- input signed [15:0] Rin ,
-
- output signed [15:0] Error,
- output signed [15:0] Yout
- );
-
- reg [2:0] count;
- always@(posedge clk or negedge rst)
- if(rst == 1'b0)
- count <= 3'd0;
- else if(count == 3'd5)
- count <= 3'd0;
- else
- count <= count + 1'b1;
- reg signed [15:0] Xin_Reg[6:0];
- reg signed [15:0] Rin_Reg[6:0];
- reg [2:0] i,j;
- always@(posedge clk or negedge rst)
- if(rst == 1'b0)
- begin
- for(i=0;i<7;i=i+1)
- begin
- Xin_Reg[i] <= 16'd0;
- Rin_Reg[i] <= 16'd0;
- end
- end
- else
- begin
- if(count == 3'd5)
- for(j=0;j<6;j=j+1)
- begin
- Xin_Reg[j+1] <= Xin_Reg[j];
- Rin_Reg[j+1] <= Rin_Reg[j];
- end
- Xin_Reg[0] <= Xin;
- Rin_Reg[0] <= Rin;
- end
- reg signed[15:0] W_Reg[6:0];
- reg signed[15:0] DW_Reg[6:0];
- reg [2:0] k,q;
- always@(posedge clk or negedge rst)
- if(rst == 1'b0)
- begin
- for(k=0;k<7;k=k+1)
- begin
- W_Reg[k] <= 16'b0010_0000_0000_0000;
- end
- end
- else
- begin
- if(count == 3'd5)
- for(q=0;q<7;q=q+1)
- W_Reg[q] <= W_Reg[q] + DW_Reg[q];
- end
-
- wire signed[31:0] Y_Reg[6:0];
- genvar v;
- generate
- for(v=0;v<=6;v=v+1)
- begin:u
- mult u
- (
- .aclr ( !rst ),
- .clock ( clk ),
- .dataa ( Xin_Reg[v] ),
- .datab ( W_Reg[v] ),
- .result ( Y_Reg[v] )
- );
- end
- endgenerate
-
- reg signed[34:0] Y1_out,Y2_out,Y_out;
- reg signed[20:0] E_out;
- always@(posedge clk or negedge rst)
- if(rst == 1'b0)
- begin
- Y1_out <= 35'd0;
- Y2_out <= 35'd0;
- E_out <= 21'd0;
- Y_out <= 35'd0;
- end
- else
- begin
- Y1_out <= {{3{Y_Reg[0][31]}},Y_Reg[0]} + {{3{Y_Reg[1][31]}},Y_Reg[1]} + {{3{Y_Reg[2][31]}},Y_Reg[2]};
- Y2_out <= {{3{Y_Reg[3][31]}},Y_Reg[3]} + {{3{Y_Reg[4][31]}},Y_Reg[4]} + {{3{Y_Reg[5][31]}},Y_Reg[5]} + {{3{Y_Reg[6][31]}},Y_Reg[6]};
- Y_out <= Y1_out + Y2_out;
- if(count == 3'd3)
- E_out <= {{5{Rin_Reg[3][15]}},Rin_Reg[3]} - Y1_out[34:14] - Y2_out[34:14];
- end
- assign Yout = Y_out[31:16];
- assign Error = E_out[20:5];
-
- reg [2:0] m,n;
- always@(posedge clk or negedge rst)
- if(rst == 1'b0)
- for(m=0;m<7;m=m+1)
- DW_Reg[m] <= 16'd0;
- else
- begin
- for(n=0;n<7;n=n+1)
- if(E_out[20])
- DW_Reg[n] <= -{{7{Xin_Reg[n][15]}},Xin_Reg[n][15:7]};
- else
- DW_Reg[n] <= {{7{Xin_Reg[n][15]}},Xin_Reg[n][15:7]};
- end
- endmodule
其中使用到的乘法器IP核配置如下:
图3 乘法器IP核配置
求取滤波器系数与输入数据乘法操作占2个周期;7个32bit加法运算分为2次并行加法以及1个32bit加法运算,前者占1个周期,后者与求e(n)的减法运算占1个周期,共占2个周期;求取W(n)的1次判断与取反操作占1个周期;最后更新滤波器系数的加法运算占1个周期;整个符号LMS算法占6个周期,故时钟频率应该位抽样频率的6倍。
具体如图所示:
图4 各步骤所需时钟周期
将FPGA处理数据保存到外部txt文件,用matlab读取文件,完成仿真测试
Matlab代码如下:
- fid = fopen('D:Error_out.txt','r');
- [Cout,N_n] = fscanf(fid,'%lg',inf);
- fclose(fid);
- error = Cout/max(abs(Cout));
-
- fid = fopen('D:matlab\e_out.txt','r');
- [Cout,N_n] = fscanf(fid,'%lg',inf);
- fclose(fid);
- e = Cout/max(abs(Cout));
-
- Error = error(1:length(error));
- Se = e.^2;
- Serror = Error.^2;
-
- N = 1:length(error);
- subplot(211);plot(Serror);title('FPGA仿真误差收敛图');xlabel('数据长度');ylabel('归一化误差');
- subplot(212);plot(Se);title('matlab仿真误差收敛图');xlabel('数据长度');ylabel('归一化误差');
图5 仿真测试结果
可见,算法收敛后,误差信号在很小的范围内波动。Matlab仿真误差收敛图与FPGA实现后的误差收敛图基本相同,说明FPGA结果接近理论值。
内容仍然有很多未解之处,也有很多地方没有完全写明白,在此先定一个草稿,未完待续…
参考文献:
[1] 杜勇.数字滤波器的MATLAB与FPGA实现——Altera/Verilog版(第二版).北京:电子工业出版社,2019
[2] 刘威,邵高平. 基于FPGA的高速低功耗自适应滤波器的实现. 数据采集与处理.2006.(B12):150-152
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。