m基于FPGA的gardner环定时同步实现,含testbench测试程序

发布时间 2023-10-13 00:05:43作者: 我爱C编程

1.算法仿真效果

使用vivaod2019.2版本仿真结果如下:

 

将基带信号放大可以看到:

 

 

 

整个系统的RTL图如下图所示:

 

 

 

其中gardner环的结构如下图所示:

 

 

 

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

       基于FPGAGardner环定时同步是一种用于数字通信系统中实现定时同步的高效方法。该方法通过提取接收信号中的定时误差信息,在不需要先进行载波同步的情况下,实现位同步信号的调整。

 

2.1 Gardner环概述

      首先,我们来看一下Gardner环定时同步的基本原理。Gardner算法是一种非判决指向方法,其基本思想是提取相邻码元最佳采样点的幅度和极性变化信息,以及相邻码元过渡点是否为零这一信息,从而从采样信号中提取出定时误差。在系统设计中,Gardner锁相环位于Costas载波同步锁相环之后,主要由四部分组成:内插器、时钟误差提取模块、环路滤波器以及控制器模块。

 

        Gardner算法中,相互正交的IQ两路信号的采样点会通过运算每个符号期间产生一个定时错误样点。通过定时误差检测,定时错误序列经过环路滤波器后送给数控振荡器。数控振荡器根据接收到的采样时刻信号和误差信号产生参数控制插值滤波器。插值滤波器会根据这些信号进行采样时刻调整,从而完成整个符号同步过程。

 

2.2 Gardner环定时同步的原理

     Gardner定时误差算法通常用在BPSKQPSK信号,通过改进可以应用在QAM等多进制基带信号中。Gardner定时误差算法,该算法的一个特点是每个符号只需要使用两个采样点,一个是strobe点,即最佳观察点,另外一个是midstrobe点,即两个观察点之间的采样点。Gardener环中的数控振荡器与锁相环路中的NCO功能完全不同,这里的NCO作用是产生时钟,即确定内插基点mk,同时完成分数间隔uk的计算,以提供给内插器进行内插。位同步环路中的数控振荡器(NCO)是一个相位递减器,它的差分方程为:

η(m+1)=[η(m)-ω(m)]mod1

 

       式中,η(m)是第m个工作时钟NCO寄存器的内容,ω(m)NCO的控制字,两者都是正小数。NCO的工作周期是T s(采样周期),内插器的周期为T i,ω(m)由环路滤波器进行调节,使NCO在最佳采样时刻溢出。当环路达到平衡时,ω(m)近似是个常数,此时平均每隔1/ω(m)个采样周期,NCO就溢出一次,所以

 

 

 

2.3 Gardner环定时同步的实现步骤

具体来说,Gardner定时同步算法的实现过程可以概括为以下几个步骤:

 

采样信号处理:从接收到的数字基带信号中提取出IQ两路正交信号,并对它们进行采样。每个采样点都包含了一个符号的幅度和相位信息。

定时误差检测:在每个符号周期内,通过计算相邻码元最佳采样点的幅度和极性变化信息,以及相邻码元过渡点是否为零的信息,得到定时误差信号。

滤波处理:利用数控振荡器产生的插值滤波器对定时误差信号进行滤波处理。插值滤波器的作用是根据采样时刻信号和误差信号来调整采样时刻,以减小定时误差。

反馈控制:将滤波后的定时误差信号反馈给数控振荡器,再由数控振荡器根据这个信号调整插值滤波器的参数,以实现更精确的定时同步。

       总的来说,基于FPGAGardner环定时同步是一种利用插值算法实现位同步信号调整的有效方法。它能在不改变采样时钟的频率和相位的情况下,根据采样值以及数控振荡器输出的采样时刻信号和误差信号获取最佳采样值。这种方法对于高采样频率和受限本地时钟的数字通信系统来说非常有优势,可以有效地提高数字通信系统的性能。

 

3.verilog核心程序

 

`timescale 1ns / 1ps
//
// Company: 
// Engineer: 
// 
// Create Date: 2023/09/27 02:16:25
// Design Name: 
// Module Name: TEST
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//
 
 
module TEST();
 
reg i_clk;
reg i_clkr;
reg i_clkSYM;
reg i_rst;
reg i_dat;
 
 
wire signed[15:0]o_Ifir_T;
wire signed[15:0]o_Qfir_T;
 
//QPSK调制
Tqpsk Tqpsku(
.i_clk  (i_clk),
.i_clkSYM(i_clkSYM),
.i_rst  (i_rst),
.i_dat(i_dat),
 
.o_Ifir (o_Ifir_T),
.o_Qfir (o_Qfir_T)
);
 
wire signed [17:0]	o_inserti;     //插值I支路数据
wire signed [17:0]	o_insertq;     //插值Q支路数据
wire signed [15:0]	o_uk;      //插值间隔输出
 
wire o_syn_clock;                 //位同步脉冲/1MHz
Gardner_tops Gardner_topsuut(
.i_clk             (i_clkr),
.i_rst             (i_rst),
.i_basei           (o_Ifir_T),
.i_baseq           (o_Qfir_T),
.o_inserti         (o_inserti),
.o_insertq         (o_insertq),
.o_syn_clock       (o_syn_clock),
.o_uk              (o_uk)
); 
 
 
 
 
 
 
 
initial
begin
    i_clk = 1'b1;
    i_clkr= 1'b1;
    i_clkSYM=1'b1;
    i_rst = 1'b1;
    #160000
    i_rst = 1'b0;
end
always #1000 i_clk=~i_clk;
always #1005 i_clkr=~i_clkr;
always #8000 i_clkSYM=~i_clkSYM;
initial
begin
    i_dat = 1'b0;
    #144000
    repeat(1000)
    begin
    #16000 i_dat = 1'b1;
    #16000 i_dat = 1'b1;
    #16000 i_dat = 1'b0;
    #16000 i_dat = 1'b0;
    #16000 i_dat = 1'b0;
    #16000 i_dat = 1'b1;
    #16000 i_dat = 1'b0;
    #16000 i_dat = 1'b1;
    #16000 i_dat = 1'b1;
    #16000 i_dat = 1'b0;
    #16000 i_dat = 1'b0;
    #16000 i_dat = 1'b0;
    #16000 i_dat = 1'b0;
    #16000 i_dat = 1'b0;
    #16000 i_dat = 1'b0;
    #16000 i_dat = 1'b0;
    #16000 i_dat = 1'b0;
    #16000 i_dat = 1'b0;
    #16000 i_dat = 1'b1;
    #16000 i_dat = 1'b1;
    #16000 i_dat = 1'b1;
    #16000 i_dat = 1'b0;
    #16000 i_dat = 1'b0;
    #16000 i_dat = 1'b1;
    #16000 i_dat = 1'b0;
    #16000 i_dat = 1'b0;
    #16000 i_dat = 1'b1;
    #16000 i_dat = 1'b1;
    #16000 i_dat = 1'b0;
    #16000 i_dat = 1'b0;
    #16000 i_dat = 1'b1;
    #16000 i_dat = 1'b1;
    #16000 i_dat = 1'b0;
    #16000 i_dat = 1'b0;
    #16000 i_dat = 1'b0;
    #16000 i_dat = 1'b1;
    #16000 i_dat = 1'b0;
    #16000 i_dat = 1'b1;
    #16000 i_dat = 1'b0;
    #16000 i_dat = 1'b0;
    #16000 i_dat = 1'b1;
    #16000 i_dat = 1'b1;
    #16000 i_dat = 1'b0;
    #16000 i_dat = 1'b0;
    #16000 i_dat = 1'b0;
    #16000 i_dat = 1'b1;
    #16000 i_dat = 1'b1;
    #16000 i_dat = 1'b1;
    #16000 i_dat = 1'b1;
    #16000 i_dat = 1'b0;
    #16000 i_dat = 1'b1;
    #16000 i_dat = 1'b0;
    #16000 i_dat = 1'b1;
    #16000 i_dat = 1'b1;
    #16000 i_dat = 1'b0;
    #16000 i_dat = 1'b0;
    #16000 i_dat = 1'b0;
    #16000 i_dat = 1'b0;
    
    end
    $stop();
end
 
endmodule