赞
踩
由数字信号处理方面的知识我们了解到,对于数字信号的插值,在时域上看,就是将信号的采样率 Fs 变成原来的 L 倍,其中 L 便是插值倍率。最简单的插值就是在信号中间补零,如图所示
下面的信号就是由上面的信号补零而来的,可以看见原来相邻的数字信号之间补了一个零,这就是最简单的信号插值。
但是问题又出现了,我们想的是插值以后可以让波形更细腻,但是单纯补零好像并没有达到这个要求,那我们为什么还要这么做呢?
补零前后时域表达式如下,
v(n)是补完零后的信号,这时再将其傅里叶变换,得到频域表达式如下
可以见得插值前后信号的频域关系如下
由此可见,在时域 补零,实际上是将原来的频谱压缩,原来频率分量为的经过插值后变到了的位置上,如下图
因此如果想实现真正的插值,就需要将镜像频率剔除,就是上图蓝色的部分,这也是时域补零带来的失真。
这时就需要将补零后的信号通过滤波器滤除,由分析可知,插值滤波器仅需一个低通滤波器就可以实现。
PS:实际上在时域波形来看,补零后的零点和下一个实际信号点的突变就是一个高频分量,因此通过滤除高频分量就可以实现真正的插值。
由上述分析,仅需要低通滤波器就可以实现,卷积公式如下图:
如果按照卷积公式直接进行运算,将会有很多0值参与乘法运算,这样会浪费资源与时间,为解决这个问题,将上式改写为
由上式可以看到,这种高效结构将原来带零点的v(n)直接改写成补零前的x(n),这样就节省了无用的计算时间。
利用此结构对信号进行25倍插值,设置100个滤波器系数,结构如下图
利用MATLAB对插值滤波器进行仿真,设置输入信号长度为256,进行25倍插值,滤波器阶数为99。
- % 采样序列为input_seq,长度为256
- input_seq = xn; % 输入的采样序列
-
- % 25倍插值后的序列长度
- interp_length = 256 * 25;
-
- % 进行插零值
- zero_padded_seq = zeros(1, interp_length);
- zero_padded_seq(1:25:end) = input_seq;
-
- % 设计低通滤波器
- filter_order = 99; % 滤波器阶数
- cutoff_freq = 0.04; % 截止频率
- filter_coeff = fir1(filter_order, cutoff_freq);
-
-
- % 应用低通滤波器
- filtered_seq = conv(zero_padded_seq, filter_coeff, 'same');
- %filtered_seq = conv(zero_padded_seq, fix_x, 'same');
- % 输出插值后的序列
- output_seq = 25*filtered_seq;
结果如图
可以看到滤波器实现了想要的结果,对正弦波数据进行了插值。
通过MATLAB验证,实现了信号的插值,接下来将滤波器系数转入coe文件。
- % 放大2的16次幂
- x = filter_coeff * 20;
- qpath = quantizer('fixed','round','saturate',[20,0]);
- fix_x = quantize(qpath,x);
-
- % 转换为16进制有符号数
- % x_hex = dec2hex(typecast(int16(x), 'uint16')); % 转换为无符号整型,并转换为16进制字符串
- % x_hex = reshape(x_hex, [], 4); % 将16进制字符串按4个字符一组划分
- % fix_x = flipud(fix_x); % 翻转顺序,以满足coe文件的顺序要求
- % fix_x = cellstr(fix_x); % 转换为cell数组
- % fix_x = strcat(fix_x, {';'}); % 添加分号
-
- % 存储coe文件
-
- % 2. 将输入数组转换为16进制字符串
- hex_string = dec2hex(fix_x)
- % 3. 将16进制字符串写入coe文件
- filename = ' G:\BaiduNetdiskWorkspace\English_path\Interpolation\Interpolation_fir_99order.coe'; % 文件名
-
- fileID = fopen(filename, 'w');
- fprintf(fileID, 'memory_initialization_radix=16;\nmemory_initialization_vector=\n');
- for i = 1:size(hex_string, 1)
- fprintf(fileID, '%s,\n', hex_string(i,:));
- end
- fclose(fileID);
再将正弦波序列也存为coe文件,再存入FPGA中ROM IP核中(实际上应该用RAM,因为可以随时可以改,要不然滤波器只有一种截止频率,或者不使用低通滤波器进行插值滤波,本设计仅针对低通滤波器)
顶层结构由三个单元组成,分别为正弦波ROM IP核,地址控制模块,以及运算模块组成,其中运算模块是依照高效滤波结构进行设计,调用四个乘法IP核,并且通过控制滤波器系数的地址,来获取参与运算的滤波器系数,并将其输入至乘法器,最终将四路乘法器输出相加,得到插值滤波结果。
首先是寄存器地址,以上四路代表了四组滤波器系数寄存器的地址,可见是由上面分析的每组的系数是分别从h[0]、h[25]、h[50]、h[75]开始,到h[24]、h[49]、h[74]、h[99]结束一轮卷积。
可以看到红色的信号是输入信号,是每隔25个时钟输入,意味着信号的采样率是2MSPS,而25倍插值后就是50MSPS。四路信号经过延时,依次落后一拍,与理论相符。(至于滤波后数变大,有一个原因是在MATLAB导出滤波器系数的时候,对滤波器系数进行了放大后再定点化,这样做的原因是滤波器系数都是小数,如果不放大直接定点化,则会出现全零或全一的结果,因此放大再定点是可以解决这个问题的)
红色的为插值前的信号,绿色的为插值后的信号,可以看到实现了25倍插值。上图为数字,下图为模拟波形
最终在硬件中实现了25倍插值滤波器。
完整代码可见github:
https://github.com/ZFXS55/Interpolation-filter-based-on-the-FPGA
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。