m基于FPGA的带相位偏差16QAM调制信号相位估计和补偿算法verilog实现
1.算法仿真效果
本系统进行了Vivado2019.2平台的开发,Vivado2019.2仿真结果如下:
将FPGA的仿真结果导入到matlab显示星座图。
2.算法涉及理论知识概要
从以下几个方面进行介绍:16QAM调制信号的基本原理、相位偏差的处理方法、VV算法的原理和实现步骤等。
2.1. 16QAM调制信号的基本原理
16QAM调制是一种常见的数字调制方式,其将四个二进制比特映射到一个复平面上的16个点,如图1所示。16QAM调制信号可以表示为:
其中,$A_c$是载波幅度,$a_n$是二进制比特,$p(t)$是脉冲成形滤波器,$T$是符号间隔,$f_c$是载波频率,$\phi_n$是相位。
<center>图1. 16QAM调制信号映射图</center>
2.2. 相位偏差的处理方法
在实际应用中,16QAM调制信号的相位可能会受到多种因素的影响,如噪声、多径衰落等,从而导致信号存在相位偏差。为了保证信号的正确解调,需要对信号进行相位估计和补偿。
相位估计和补偿的一般流程包括以下几个步骤:
选择合适的相位参考信号,例如载波信号或训练序列等。
提取接收信号的相位信息,例如使用差分解调器提取相位差等。
对相位信息进行滤波和处理,例如使用低通滤波器平滑相位信息,使用PLL等算法进行相位跟踪等。
根据估计得到的相位信息,对信号进行相位补偿,以消除相位偏差。
2.3. VV算法的原理和实现步骤
VV算法是一种基于相位差的相位估计算法,它可以对16QAM调制信号的相位进行高精度的估计和补偿。VV算法的原理如下:
将16QAM调制信号分为实部和虚部两路,分别进行差分解调,得到相位差$\Delta\phi$。
对相位差进行累积,并进行非线性处理,得到相位估计值。
根据相位估计值,对信号进行相位补偿。
VV算法的实现步骤如下:
对接收信号进行采样和定时,并进行载波恢复,得到16QAM调制信号的实部和虚部。
对实部和虚部分别进行差分解调,得到相位差$\Delta\phi$。
最后,将补偿后的信号进行解调和解码,得到原始的二进制比特流。需要注意的是,VV算法的实现需要使用高精度的浮点运算,因此需要使用FPGA等硬件加速器来实现。在实现过程中,需要根据具体的硬件平台和信号特性进行优化,以达到较高的运算速度和较低的资源消耗。
3.Verilog核心程序
module TEST; reg clk; reg rst; reg start; wire [3: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_16QAM_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_16QAM_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