m基于FPGA的带相位偏差64QAM调制信号相位估计和补偿算法verilog实现,包含testbench

目录

1.算法仿真效果

2.算法涉及理论知识概要

2.1、基本原理

2.2、VV算法原理

3.Verilog核心程序

4.完整算法代码文件


1.算法仿真效果


本系统进行了Vivado2019.2平台的开发,其中Vivado2019.2仿真结果如下:

将FPGA的仿真结果导入到matlab中,显示星座图,结果如下所示: 

2.算法涉及理论知识概要


        在现代通信系统中,调制技术是实现高速数据传输和频谱效率优化的重要手段。其中,64QAM调制技术是一种常见的高阶调制技术,可以实现每个符号传输6个比特的信息,从而提高数据传输速率。然而,在实际应用中,调制信号往往会受到各种干扰和失真的影响,导致传输错误率增加。因此,相位估计和补偿技术是调制信号解调和恢复的关键环节之一。介绍一种用于带相位偏差64QAM调制信号相位估计和补偿的VV算法,并详细阐述其实现步骤和数学原理。

2.1、基本原理

在64QAM调制中,每个符号可以表示为4个基带信号的线性叠加,即:

$$ s_k = I_k + jQ_k = \sum_{n=0}^3 a_{kn}\cdot e^{j\theta_{kn}},\ \ \ \ \ k=0,1,...,N-1 $$

       其中,I_k和Q_k分别代表第$k$个符号的实部和虚部,a_{kn}和\theta_{kn}分别代表第k个符号的第n个基带信号的幅度和相位。j为虚数单位。

       在理想情况下,调制信号的相位是连续平滑的,但在实际应用中,调制信号往往会受到各种干扰和失真的影响,导致相位偏差的出现。因此,需要对接收到的调制信号进行相位估计和补偿,以实现信号解调和恢复。相位估计和补偿的基本思路是利用已知的信号结构和特征,通过计算和比较接收信号和理想信号的相位差,得出相位偏差,并对接收信号进行相位校正。在带相位偏差的64QAM调制信号中,可以使用VV算法来实现相位估计和补偿。

2.2、VV算法原理

       VV算法是一种基于矢量旋转的相位估计和补偿算法,其基本思路是通过将接收信号转换为矢量形式,并对矢量进行旋转和缩放,以实现相位校正。VV算法的实现步骤如下:
        在VV算法中,需要选择一个参考信号作为理想信号。一般情况下,可以选择已知的信号子载波或导频符号作为参考信号。假设参考信号为s_{ref}。

        将接收到的符号$s_k$和参考信号$s_{ref}$分别表示为矢量形式:

$$ \mathbf{s}k = \left[\begin{matrix} I_k \ Q_k \end{matrix}\right],\ \ \ \ \ \mathbf{s}{ref} = \left[\begin{matrix} I_{ref} \ Q_{ref} \end{matrix}\right] $$

矢量旋转
        计算接收信号与参考信号之间的相位差$\Delta\theta$:

$$ \Delta\theta = \text{angle}(\mathbf{s}k) - \text{angle}(\mathbf{s}{ref}) $$

其中,$\text{angle}(\cdot)$表示矢量的相位角。根据欧拉公式,可以将矢量旋转表示为:

$$ \mathbf{s}_k^{'} = \mathbf{s}_k e^{-j\Delta\theta} $$

其中,$\mathbf{s}_k^{'}$表示经过相位旋转后的接收信号。

矢量缩放
        为了消除相位偏差,需要对旋转后的接收信号进行缩放。根据矢量旋转后的模长公式,可以得到:

$$ \left|\mathbf{s}_k^{'}\right| = \left|\mathbf{s}_k\right| $$

因此,可以将旋转后的接收信号缩放为:

$$ \mathbf{s}_k^{''} = \frac{\mathbf{s}_k^{'}}{\left|\mathbf{s}_k^{'}\right|} \cdot \left|\mathbf{s}_k\right| $$

相位平滑
        为了消除相位偏差的抖动,需要对相位进行平滑处理。一般情况下,可以使用低通滤波器或移动平均滤波器对相位进行平滑处理。假设滤波器输出为$\Delta\hat{\theta}$,则可以得到经过相位估计和补偿后的符号为:

$$ \hat{s}_k = \left|\mathbf{s}_k^{''}\right| \cdot e^{j(\text{angle}(\mathbf{s}_k^{''})+\Delta\hat{\theta})} $$

        在上述VV算法的实现中,涉及到矢量的旋转和缩放等操作,需要使用复数运算和欧拉公式。下面将介绍一些相关的数学原理。
       复数可以表示为实部和虚部的线性组合,即:

$$ z = a + jb $$

其中,$a$和$b$分别为实部和虚部,$j$为虚数单位。复数加减法和乘法可以表示为:

$$ (a_1+jb_1) + (a_2+jb_2) = (a_1+a_2) + j(b_1+b_2) $$$$ (a_1+jb_1) - (a_2+jb_2) = (a_1-a_2) + j(b_1-b_2) $$$$ (a_1+jb_1)\cdot(a_2+jb_2) = (a_1a_2-b_1b_2) + j(a_1b_2+a_2b_1) $$

复数除法可以表示为:

$$ \frac{a_1+jb_1}{a_2+jb_2} = \frac{(a_1a_2+b_1b_2)+j(b_1a_2-a_1b_2)}{a_2^2+b_2^2} $$

欧拉公式
欧拉公式是一种将指数函数表示为三角函数的公式,可以表示为:

$$ e^{j\theta} = \cos\theta + j\sin\theta $$

其中,$\theta$为角度。欧拉公式可以用于将复数表示为幅度和相位的形式,即:

$$ z = |z|e^{j\theta} $$

其中,$|z|$为复数$z$的模长,$\theta$为复数$z$的相位角。

矢量旋转
在VV算法中,需要对接收信号进行矢量旋转,实现相位偏差的校正。矢量旋转可以表示为:

$$ \mathbf{s}_k^{'} = \mathbf{s}_k e^{-j\Delta\theta} $$

其中,$\mathbf{s}_k$为接收信号的矢量形式,$\Delta\theta$为接收信号和参考信号之间的相位差。矢量旋转可以使用欧拉公式和复数运算表示为:

$$ \mathbf{s}_k^{'} = |\mathbf{s}_k|e^{j(\text{angle}(\mathbf{s}_k)-\Delta\theta)} $$

矢量缩放
在VV算法中,需要对旋转后的接收信号进行缩放,以消除相位偏差。矢量缩放可以表示为:

$$ \mathbf{s}_k^{''} = \frac{\mathbf{s}_k^{'}}{\left|\mathbf{s}_k^{'}\right|} \cdot \left|\mathbf{s}_k\right| $$

         其中,$\mathbf{s}_k^{'}$为旋转后的接收信号的矢量形式,$\left|\mathbf{s}_k^{'}\right|$为$\mathbf{s}_k^{'}$的模长,$\left|\mathbf{s}_k\right|$为接收信号$\mathbf{s}_k$的模长。矢量缩放可以使用欧拉公式和复数运算表示为:

$$ \mathbf{s}_k^{''} = |\mathbf{s}_k|e^{j(\text{angle}(\mathbf{s}_k)-\Delta\theta)} $$

其中,$\text{angle}(\mathbf{s}_k)$为接收信号$\mathbf{s}_k$的相位角。

相位平滑
        在VV算法中,为了消除相位偏差的抖动,需要对相位进行平滑处理。一般情况下,可以使用低通滤波器或移动平均滤波器对相位进行平滑处理。假设滤波器输出为$\Delta\hat{\theta}$,则可以得到经过相位估计和补偿后的符号为:

$$ \hat{s}_k = \left|\mathbf{s}_k^{''}\right| \cdot e^{j(\text{angle}(\mathbf{s}_k^{''})+\Delta\hat{\theta})} $$

其中,$\mathbf{s}_k^{''}$为缩放后的接收信号的矢量形式,$\left|\mathbf{s}_k^{''}\right|$为$\mathbf{s}_k^{''}$的模长,$\text{angle}(\mathbf{s}_k^{''})$为$\mathbf{s}_k^{''}$的相位角。

这样就完成了带相位偏差64QAM调制信号相位估计和补偿的实现。

3.Verilog核心程序

...........................................................................
module TEST;

	reg clk;
	reg rst;
	reg start;

    wire  [5:0] parallel_data;
    wire [15:0]sin;
    wire [15:0]cos;
	wire signed[19:0]  I_com;
	wire signed[19:0]  Q_com;
	wire signed[19:0]  I_com2;
	wire signed[19:0]  Q_com2;
    wire signed[15:0]I_comcos;
    wire signed[15:0]Q_comsin;
	 

	// DUT
	tops_64QAM_mod  top(
	   .clk(clk),
	   .rst(rst),
	   .start(start),
	   .parallel_data(parallel_data),
	   .sin(sin),
	   .cos(cos),
	   .I_com(I_com),
	   .Q_com(Q_com),
	   .I_com2(I_com2),
	   .Q_com2(Q_com2),
	   .I_comcos(I_comcos),
	   .Q_comsin(Q_comsin)
	   );
	   
	   
wire signed[23:0]I_comcos2;
wire signed[23:0]Q_comsin2;
wire signed[7:0]o_Ifir;
wire signed[7:0]o_Qfir;
wire signed[15:0]o_Ifir_phase;
wire signed[15:0]o_Qfir_phase;
wire signed[31:0]o_phase;
tops_64QAM_phase_est  top2(
	   .clk(clk),
	   .rst(rst),
	   .start(start),
	   .I_comcos(I_comcos),
	   .Q_comsin(Q_comsin),
	   .I_comcos2(I_comcos2),
	   .Q_comsin2(Q_comsin2),
	   .o_Ifir(o_Ifir),
	   .o_Qfir(o_Qfir),
	   .o_I_phase(o_Ifir_phase),
	   .o_Q_phase(o_Qfir_phase),
	   .o_phase(o_phase)
	   );  
	   
 
	initial begin
		clk = 0;
		rst = 0;
		start = 1;
		#10;
		rst = 1;
	end
	
	always #5
	clk <= ~clk;
	
	
reg writeen;
initial
begin
    writeen = 1'b0;
    #150000
    writeen = 1'b1;
end
	
reg[4:0]divcnt;	
always @(posedge clk or negedge rst)
begin
     if(~rst)
	  begin
	  divcnt<={5{1'b0}};	
	  end
else begin
	  divcnt<=divcnt+{5{1'b1}};
     end
end 
	
	
	
integer fout1;
integer fout2;
initial begin
 fout1 = $fopen("It.txt","w");
 fout2 = $fopen("Qt.txt","w"); 
end

always @ (posedge divcnt[4])
 begin
     if(writeen==1)
     begin
   	 $fwrite(fout1,"%d\n",I_com2);
	 $fwrite(fout2,"%d\n",Q_com2);
	 end
end




integer fout3;
integer fout4;
initial begin
 fout3 = $fopen("IR.txt","w");
 fout4 = $fopen("QR.txt","w"); 
end

always @ (posedge divcnt[4])
 begin
     if(writeen==1)
     begin
   	 $fwrite(fout3,"%d\n",o_Ifir_phase);
	 $fwrite(fout4,"%d\n",o_Qfir_phase);
	 end
end
 

endmodule
00_022m

4.完整算法代码文件

V

猜你喜欢

转载自blog.csdn.net/hlayumi1234567/article/details/131537644
今日推荐