基于FPGA实现正弦插值算法
创始人
2024-06-01 14:45:41
0

1、正弦插值的算法分析

1.1 信号在时域与频域的映射关系

        在进行正弦算法分析之前,我们回顾一下《数字信号处理》课程中,对于信号在时域与频域之间的映射关系,如下图。

 

        对于上图中的原始信号x(t),使用ADC对信号进行采样,即实现了时域信号的离散化,得到x[k]。根据时域与频域之间的映射关系:时域的离散化对应着频域的周期化,即x[k]的频域响应为X(e^{j\Omega })

        那么离散化的x[k]如何还原为原来的x(t)呢?时域上分析较为复杂,我们可以从频域上进行分析,即如何将频域响应X(e^{j\Omega })还原成X(jw)。这样就比较直观了,只需要截取X(e^{j\Omega })一个周期的信号,就可以还原成X(jw),示例如下图。

 1.2 sinc函数

         上面用到的窗函数,实际上是一个理想的低通滤波器,其时域形态是什么样子的呢?如下图。

        其时域形态公式:sinc(t)=\frac{sin(\pi t))}{\pi t} ,sinc函数在频域上是一个矩形的方窗,如下图。

 1.3 使用sinc函数实现正弦插值的算法分析

        综上所述,对于我们可以使用sinc函数实现离散信号还原为连续信号,即使用sinc函数实现离散信号的无限插值。

        而频域的乘积对应着时域的卷积,因此我们可以将离散的采样信号与sinc函数进行卷积运算,从而获得插值后的信号。

        y(n)=\sum x(\imath)h(n-\imath )

        其中,y(n)为插值后信号;x(n)为待插值的离散信号;h(n)为sinc函数,也称为插值核或者插值因子。

        假设有一个离散信号x(n),含有8个离散点,x(n)={-10,0,10,0,-10,0,10,0};如下图:

         我们使用前后共计8个原始采样点,来求插值点x(0.5)的数值。套用上面描述的公式y(n)=\sum x(\imath)h(n-\imath ),即x(0.5)=x(-3)h(0.5-(-3)) +  x(-2)h(0.5-(-2)) + x(-1)h(0.5-(-1)) + x(0)h(0.5-(0)) + x(1)h(0.5-(1)) + x(2)h(0.5-(2)) + x(3)h(0.5-(3)) =-6.3056,即求得插值点的数值。

         根据这种思想,我们可以无限的求得每个插值点的数值。那么如何在工程也能够中使用sinc函数实现信号的插值,是我们接下来要讲述的。

2、使用matlab计算插值核

        在使用FPGA实现插值算法时,由于sinc函数的计算实现起来比较消耗硬件资源,所以一般预先计算好插值核存放在ROM或者寄存器中,直接进行调用。

        假设我们要使用前后8个点计算插值,插值倍数为10,那么使用matlab计算插值核的代码如下:

interp_mul = 10; %插值倍数为10
orig_point_num = 8;%原始点数8个coe_group = zeros(interp_mul,orig_point_num); %定义系数组
for group = 1:1:interp_mul %计算interp_mul组系数for Hn = 1:1:orig_point_num %计算每组系数的orig_point_num个加权值if(group == 1)if(Hn==4)coe_group(group,Hn) = 1; %原始点保持数据不变[0,0,0,1,0,0,0,0]elsecoe_group(group,Hn) = 0; %原始点保持数据不变[0,0,0,1,0,0,0,0]endelsecoe_group(group,Hn) = sinc(4- Hn + 0.1*(group-1));%找规律映射endend
end

        运行结果保存在coe_group数组中,结果如下:

         上述算法计算得到的插值核为小数,而FPGA计算小数运算时,需要现将数值左移相应位数进行放大,方便进行计算,输出结果时,截取高bit即可。由于后续FPGA实现卷积和时使用18bits的插值核进行运算,因此实际工程中的matlab代码如下:

interp_mul = 10; %插值倍数为10
orig_point_num = 8;%原始点数8个coe_group = zeros(interp_mul,orig_point_num); %定义系数组
for group = 1:1:interp_mul %计算interp_mul组系数for Hn = 1:1:orig_point_num %计算每组系数的orig_point_num个加权值if(group == 1)if(Hn==4)coe_group(group,Hn) = 1; %原始点保持数据不变[0,0,0,1,0,0,0,0]elsecoe_group(group,Hn) = 0; %原始点保持数据不变[0,0,0,1,0,0,0,0]endelsecoe_group(group,Hn) = sinc(4- Hn + 0.1*(group-1)) *2^17;%找规律映射,小数放大2^17倍endend
end

结果如下:

 3、使用FPGA实现离散采样点的插值

         本文中仅使用一个测试程序,说明FPGA实现插值的原理,结构相对简单,架构图如下:

         离散采样RAM模块代码如下:

module disc_samp_gen(input clk,input disc_rd,//读待插值数据标志output [7:0] disc_samp  //离散采样点输出 有符号数据);
reg [1:0]addra=0;always@(posedge clk)
beginif(disc_rd)addra <= addra + 1'b1;
endDataSrc_ROM DataSrc_ROM (.clka(clk),    // input wire clka.addra(addra),  // input wire [1 : 0] addra.douta(disc_samp)  // output wire [7 : 0] douta
);endmodule

         插值处理模块,主要通过乘法器实现卷积和,如下:

module sinc_process(input clk,input sinc_en,//插值使能标志input [7:0]disc_samp,//待插值数据,有符号数output disc_rd,//读取插值数据标志output sinc_valid,//插值数据有效标志output [7:0]sinc_data//插值后数据);
//插值倍数10,使用8个离散采样点进行插值处理
第0组插值核  
//localparam [17:0] group0_coe[7:0] = {18'h0,18'h0,18'h0,18'h0,
//                                     18'h1FFFF,18'h0,18'h0,18'h0};//临时粗略将18'h1FFFF视为1
第1组插值核  
//localparam [17:0] group1_coe[7:0] = {18'h3F317,18'h0115D,18'h3E57F,18'h037F5,
//                                     18'h1F79E,18'h3D238,18'h017FB,18'h3EFC2};
第2组插值核  
//localparam [17:0] group2_coe[7:0] = {18'h3E6CB,18'h02236,18'h3CAC8,18'h077BE,
//                                     18'h1DEF8,18'h3B02C,18'h02B8A,18'h3E211};
第3组插值核  
//localparam [17:0] group3_coe[7:0] = {18'h3DC5E,18'h030D5,18'h3B272,18'h0BC5B,
//                                     18'h1B77F,18'h39A94,18'h03953,18'h3D80C};
第4组插值核  
//localparam [17:0] group4_coe[7:0] = {18'h3D4F2,18'h03B9D,18'h39F21,18'h10254,
//                                     18'h1837E,18'h3914A,18'h04095,18'h3D26A};
第5组插值核  
//localparam [17:0] group5_coe[7:0] = {18'h3D170,18'h04130,18'h3935A,18'h145F3,
//                                     18'h145F3,18'h3935A,18'h04130,18'h3D170};
第6组插值核  
//localparam [17:0] group6_coe[7:0] = {18'h3D26A,18'h04095,18'h3914A,18'h1837E,
//                                     18'h10254,18'h39F21,18'h03B9D,18'h3D4F2};
第7组插值核  
//localparam [17:0] group7_coe[7:0] = {18'h3D80C,18'h03953,18'h39A94,18'h1B77F,
//                                     18'h0BC5B,18'h3B272,18'h030D5,18'h3DC5E};
第8组插值核  
//localparam [17:0] group8_coe[7:0] = {18'h3E211,18'h02B8A,18'h3B02C,18'h1DEF8,
//                                     18'h077BE,18'h3CAC8,18'h02236,18'h3E6CB};
第9组插值核  
//localparam [17:0] group9_coe[7:0] = {18'h3EFC2,18'h017FB,18'h3D238,18'h1F79E,
//                                     18'h037F5,18'h3E57F,18'h0115D,18'h3F317};//第0组插值核  
localparam [17:0] group0_coe[7:0] = {18'h0,18'h0,18'h0,18'h1FFFF,18'h0,18'h0,18'h0,18'h0};//临时粗略将18'h1FFFF视为1
//第1组插值核  
localparam [17:0] group1_coe[7:0] = {18'h3EFC2,18'h017FB,18'h3D238,18'h1F79E,18'h037F5,18'h3E57F,18'h0115D,18'h3F317};
//第2组插值核  
localparam [17:0] group2_coe[7:0] = {18'h3E211,18'h02B8A,18'h3B02C,18'h1DEF8,18'h077BE,18'h3CAC8,18'h02236,18'h3E6CB};
//第3组插值核  
localparam [17:0] group3_coe[7:0] = {18'h3D80C,18'h03953,18'h39A94,18'h1B77F,18'h0BC5B,18'h3B272,18'h030D5,18'h3DC5E};
//第4组插值核  
localparam [17:0] group4_coe[7:0] = {18'h3D26A,18'h04095,18'h3914A,18'h1837E,18'h10254,18'h39F21,18'h03B9D,18'h3D4F2};
//第5组插值核  
localparam [17:0] group5_coe[7:0] = {18'h3D170,18'h04130,18'h3935A,18'h145F3,18'h145F3,18'h3935A,18'h04130,18'h3D170};
//第6组插值核  
localparam [17:0] group6_coe[7:0] = {18'h3D4F2,18'h03B9D,18'h39F21,18'h10254,18'h1837E,18'h3914A,18'h04095,18'h3D26A};
//第7组插值核  
localparam [17:0] group7_coe[7:0] = {18'h3DC5E,18'h030D5,18'h3B272,18'h0BC5B,18'h1B77F,18'h39A94,18'h03953,18'h3D80C};
//第8组插值核  
localparam [17:0] group8_coe[7:0] = {18'h3E6CB,18'h02236,18'h3CAC8,18'h077BE,18'h1DEF8,18'h3B02C,18'h02B8A,18'h3E211};
//第9组插值核  
localparam [17:0] group9_coe[7:0] = {18'h3F317,18'h0115D,18'h3E57F,18'h037F5,18'h1F79E,18'h3D238,18'h017FB,18'h3EFC2};reg [17:0] sinc_coe [7:0];//对应插值点使用的插值核
reg [3:0] sinc_cnt = 0;//插值计数,0时为原始点,1~9为插值点,其余数值保留
reg [7:0] samp_data [7:0];//每次从RAM读取数据时进行锁存更新//8个乘法结果
reg [25:0] mult_data[7:0];assign disc_rd = (sinc_cnt == 4'd8) ? 1'b1:1'b0; //插值第8个点时,读取新的数据//插值倍数10,插值点计数
always@(posedge clk)
beginif(sinc_en == 1'b1) beginif(sinc_cnt == 4'd9) sinc_cnt <= 4'd0;elsesinc_cnt <= sinc_cnt + 1;endelsesinc_cnt <= 4'd0;
end
//原始点锁存
always@(posedge clk)
beginif(sinc_en == 1'b1 && sinc_cnt == 4'd0) begin //插值使能samp_data[0] <= disc_samp;//新输入的点samp_data[1] <= samp_data[0];samp_data[2] <= samp_data[1];samp_data[3] <= samp_data[2];samp_data[4] <= samp_data[3];samp_data[5] <= samp_data[4];samp_data[6] <= samp_data[5];samp_data[7] <= samp_data[6];end
end//插值核系数选择
always@(posedge clk)
begincase(sinc_cnt)4'd0:sinc_coe <= group0_coe;//原始点系数4'd1:sinc_coe <= group1_coe;//第1个插值点系数4'd2:sinc_coe <= group2_coe;//第2个插值点系数4'd3:sinc_coe <= group3_coe;//第3个插值点系数4'd4:sinc_coe <= group4_coe;//第4个插值点系数4'd5:sinc_coe <= group5_coe;//第5个插值点系数4'd6:sinc_coe <= group6_coe;//第6个插值点系数4'd7:sinc_coe <= group7_coe;//第7个插值点系数4'd8:sinc_coe <= group8_coe;//第8个插值点系数4'd9:sinc_coe <= group9_coe;//第9个插值点系数default:;endcase
end//乘法器0
mult_gen mult_gen_0 (.CLK(clk),  // input wire CLK.A(samp_data[0]),      // input wire [7 : 0] A.B(sinc_coe[0]),      // input wire [17 : 0] B.P(mult_data[0])      // output wire [25 : 0] P
);//乘法器1
mult_gen mult_gen_1 (.CLK(clk),  // input wire CLK.A(samp_data[1]),      // input wire [7 : 0] A.B(sinc_coe[1]),      // input wire [17 : 0] B.P(mult_data[1])      // output wire [25 : 0] P
);//乘法器2
mult_gen mult_gen_2 (.CLK(clk),  // input wire CLK.A(samp_data[2]),      // input wire [7 : 0] A.B(sinc_coe[2]),      // input wire [17 : 0] B.P(mult_data[2])      // output wire [25 : 0] P
);//乘法器3
mult_gen mult_gen_3 (.CLK(clk),  // input wire CLK.A(samp_data[3]),      // input wire [7 : 0] A.B(sinc_coe[3]),      // input wire [17 : 0] B.P(mult_data[3])      // output wire [25 : 0] P
);//乘法器4
mult_gen mult_gen_4 (.CLK(clk),  // input wire CLK.A(samp_data[4]),      // input wire [7 : 0] A.B(sinc_coe[4]),      // input wire [17 : 0] B.P(mult_data[4])      // output wire [25 : 0] P
);//乘法器5
mult_gen mult_gen_5 (.CLK(clk),  // input wire CLK.A(samp_data[5]),      // input wire [7 : 0] A.B(sinc_coe[5]),      // input wire [17 : 0] B.P(mult_data[5])      // output wire [25 : 0] P
);//乘法器6
mult_gen mult_gen_6 (.CLK(clk),  // input wire CLK.A(samp_data[6]),      // input wire [7 : 0] A.B(sinc_coe[6]),      // input wire [17 : 0] B.P(mult_data[6])      // output wire [25 : 0] P
);//乘法器7
mult_gen mult_gen_7 (.CLK(clk),  // input wire CLK.A(samp_data[7]),      // input wire [7 : 0] A.B(sinc_coe[7]),      // input wire [17 : 0] B.P(mult_data[7])      // output wire [25 : 0] P
);//使用加法器实现有符号数相加//二点相加
wire [25:0] add2_0;
wire [25:0] add2_1;
wire [25:0] add2_2;
wire [25:0] add2_3;
adder2 adder2_inst0 (.A(mult_data[0]),      // input wire [25 : 0] A.B(mult_data[1]),      // input wire [25 : 0] B.CLK(clk),  // input wire CLK.S(add2_0)      // output wire [26 : 0] S
);adder2 adder2_inst1 (.A(mult_data[2]),      // input wire [25 : 0] A.B(mult_data[3]),      // input wire [25 : 0] B.CLK(clk),  // input wire CLK.S(add2_1)      // output wire [26 : 0] S
);adder2 adder2_inst2 (.A(mult_data[4]),      // input wire [25 : 0] A.B(mult_data[5]),      // input wire [25 : 0] B.CLK(clk),  // input wire CLK.S(add2_2)      // output wire [26 : 0] S
);adder2 adder2_inst3 (.A(mult_data[5]),      // input wire [25 : 0] A.B(mult_data[6]),      // input wire [25 : 0] B.CLK(clk),  // input wire CLK.S(add2_3)      // output wire [26 : 0] S
);//四点相加
reg [25:0] add4_0;
reg [25:0] add4_1;
adder4 adder4_inst0 (.A(add2_0),      // input wire [26 : 0] A.B(add2_1),      // input wire [26 : 0] B.CLK(clk),  // input wire CLK.S(add4_0)      // output wire [27 : 0] S
);adder4 adder4_inst1 (.A(add2_2),      // input wire [26 : 0] A.B(add2_3),      // input wire [26 : 0] B.CLK(clk),  // input wire CLK.S(add4_1)      // output wire [27 : 0] S
);//八点相加
wire [25:0] add8;
adder8 adder8 (.A(add4_0),      // input wire [26 : 0] A.B(add4_1),      // input wire [26 : 0] B.CLK(clk),  // input wire CLK.S(add8)      // output wire [27 : 0] S
);assign sinc_valid = 1'b1;
assign sinc_data = add8[25:18];
endmodule

        编写testbench,对插值模块进行仿真验证,testbench代码如下:

module testbench();
reg clk = 0; //100M时钟always 
begin# 5clk <= ~clk;
endwire disc_rd;//采样点有效标志
wire [7:0] disc_samp;  //离散采样点输出
disc_samp_gen disc_samp_gen(.clk(clk),.disc_rd(disc_rd),//采样点有效标志.disc_samp(disc_samp)//离散采样点输出);sinc_process sinc_process(.clk(clk),.sinc_en(1'b1),.disc_rd(disc_rd),//采样点有效标志.disc_samp(disc_samp),//离散采样点输出.sinc_valid(),//插值数据有效标志.sinc_data()//插值后数据);endmodule

仿真验证结果如下:

         可以看到,图中黄色部分为原始采样点的直线连接波形,紫色部分为正弦插值后的波形。正弦插值仿真成功。

vivado工程以及学习sinc插值的过程文件(主要是为了存档,方便后续自己使用):https://download.csdn.net/download/yindq1220/87557975?spm=1001.2014.3001.5501

相关内容

热门资讯

与龙共舞的曲目列表 与龙共舞的曲目列表01 The Dragon´s Legend(Medley)02 Br...
孩子得了一种病毒,看他这样我也... 孩子得了一种病毒,看他这样我也很痛苦自从生下地第二天孩子就做院了,刚开始只是起了一片红的,还以是烫着...
带头盔的一个卡通人物叫什么? 带头盔的一个卡通人物叫什么?带头盔的一个卡通人物叫什么? 目前最新的……应该就是无头骑士异闻录了吧!...
小主播如何月入过万 小主播如何月入过万
地矿局野外工作艰苦吗 地矿局野外工作艰苦吗地矿局野外工作比较艰苦的,他们要扛着相关测量探测仪器,到野外探测矿藏,挖样块,做...
没离婚老婆不管孩子怎么办,老婆... 没离婚老婆不管孩子怎么办,老婆离家出走不管孩子我该咋办?可以诉讼。老婆离家出走不管孩子,可以向法院提...
阴历中的润月有的是润五月有的是... 阴历中的润月有的是润五月有的是润九月是怎么回事这润月是怎样算的呀还有这为什么有润月哟求解答三年一润,...
歌曲经典老歌 歌曲经典老歌歌曲经典老歌有:《斯卡薯郑罩布罗集市》、《AuldLangSyne》、《Yesterda...
怎样找到自己的梦想 怎样找到自己的梦想首先要给自己定一个小目标一点去奋斗,当在奋斗中就能获得自己的梦想梦想是什么?俗话都...
吉利星瑞的油耗怎么样? 吉利星瑞的油耗怎么样?吉利星瑞的油耗怎么样?估测实际油耗多少?不可能6.7L油,平均油耗最低要8个油...
佛山什么建筑最高? 佛山什么建筑最高?罗浮宫国际家居总部大厦最高。罗浮宫国际家居总部大厦位于佛山乐从,总高度为236.0...
怎么写未来一个月的活动计划? 怎么写未来一个月的活动计划?如果你是一个学校的学习部部长,为了提高学校的学习气氛的一些活动,你要怎样...
背起我的行囊走在那老路上。这首... 背起我的行囊走在那老路上。这首歌叫什么名字?这首歌曲叫:流浪兄弟
九十年代的一部武打电视剧有一个... 九十年代的一部武打电视剧有一个称为白衣人的出手就让对方眉心中剑的叫什么名字九十年代的一部武打电视剧有...
给我讲五个幽默笑话? 给我讲五个幽默笑话?幽默笑话幽默笑话幽默笑话幽默笑话幽默笑话
穷人与富人的差别只有一个字不是... 穷人与富人的差别只有一个字不是吗?同样的有钱没钱都是人!有钱没钱都是人,但不能随意、堕落啊
金色琴弦 12话具体内容 金色琴弦 12话具体内容那个因为时间不够来不及看完,只好来求助一下对柚木突然的暴变,香穗子无法隐藏内...
内心自卑敏感的人,适合学习心理... 内心自卑敏感的人,适合学习心理学吗是可以的,学习心理是自我成长的过程,不过要改善这个过程,还是通过系...
求和光之子类似的小说 求和光之子类似的小说主角要坚强,能拼,是魔法师,也有元素云云,不是现代太多了~~~~~主角要坚强,能...
男生对女生说想吃你是什么意思? 男生对女生说想吃你是什么意思?这是我男朋友对我这么说的,因为我是初恋,经验不足,想请教各位哥哥姐姐是...