一、FIR数字滤波器设计原理

 本实验采用窗函数法设计FIR数字低通滤波器。我们希望设计的滤波器系统函数如下:

\(H_{d}\left( e^{jw} \right) = \left\{ \begin{array}{l}
{e^{- jw\alpha},~~~\left| w \right| \leq w_{c}} \\
{0,~~~{\rm otherwise}} \\
\end{array} \right.\)

 它对应的单位冲激响应是:

\(h_{d}\left( n \right) = {\sin{\frac{\left\lbrack {w_{c}\left\lbrack {n - \alpha} \right\rbrack} \right\rbrack}{\pi\left( n - \alpha \right)},~~~n \neq \alpha}}\)

\(w_c\)是截止频率。

 对它进行加窗操作后,单位冲激响应变为:

\(h\left( n \right) = h_{d}\left( n \right)w\left( n \right)\)

其中\(w(n)\)是窗函数的单位冲激响应。

 为了满足FIR滤波器的线性相位特性,我们取系统群时延:

\(\alpha = \frac {N-1}{2}\)

其中\(N\)是FIR滤波器的长度。这样,\(h(n)\)可以关于\(n=α\)偶对称。

二、基于FFT的滤波操作原理

 记待滤波信号为\(x(n)\),其长度也为\(N\),则滤波结果信号:

\(y(n)=x(n)*h(n)\)

 左右同作\(2N\)点FFT,有:

\({\rm FFT}_{2N} [y(n)]={\rm FFT}_{2N} [x(n)]∙{\rm FFT}_{2N} [h(n)]\)

 作\(2N\)点FFT的原因是,\(y(n)\)的结果长度为\(2N-1\),故至少应作\(2N-1\)点FFT才能获得完整结果。又我们在实验1实现的基2-FFT要求点数为2的幂次方,故应作\(2N\)点FFT,且实际上\(2N\)应是2的幂次。

 再做反变换后有:

\(y(n)={\rm IFFT}[{\rm FFT}[y(n)]]\)

这样就获得了滤波结果信号的时域表示。

三、FIR数字滤波器的具体实现

 程序首部可以调节\(w_c\)和\(N\)的取值。本报告中固定\(w_c=0.2π\),\(N=32\),后续不再说明。

 首先设计滤波器的无限长冲激响应。我们利用\(N\)的三倍来模拟所谓的无限长。

  1. def h_d(w_c):
  2. alpha = (N - 1) / 2. # 系统群时延
  3. vals = []
  4. for i in range(N * 3):
  5. vals.append(math.sin(w_c * (i - alpha)) / float(math.pi * (i - alpha)))
  6. return vals

 接下来,我们用一个窗函数来截断\(h_d (n)\)。窗函数的选取对滤波器的效果有很大影响。本实验主要关注如下三种窗函数:



 加窗相关代码如下,以\(N\)为输出长度作对应位相乘即可:

  1. def get_windowed(h_d, w_n):
  2. cutted = []
  3. for i in range(N):
  4. cutted.append(h_d[i] * w_n[i])
  5. return cutted

以下是相关的时域图像:



 利用实验1写的相关代码,并利用课本193页从\(X(k)\)求取\(X(e^{jw})\)的插值公式,可以方便地绘制\(H(e^{jw})\)的幅度函数曲线,举例代码如下:

  1. fft_h_n_rect = fft_dit2(convert_to_complex(add_zero_to_length(h_n_rect, 2 * N))) # 注意是2N点FFT
  2. plt.title('|H(e^jw)|(矩形窗截断)')
  3. xs, dtft_h_n_rect = interp(fft_h_n_rect, 500)
  4. plt.plot(xs, convert_to_amplitude(dtft_h_n_rect), '-')

绘制结果如下:



 从这三张幅度频谱图中已经可以清楚地看到不同的窗函数对滤波器的影响:

  • 矩形窗的旁瓣抑制性能差,旁瓣峰值大,在阻带仍有一定幅度值;巴特雷特窗旁瓣抑制性能稍好;汉明窗旁瓣抑制能力最强,在阻带基本没有幅度值。
  • 矩形窗的主瓣窄;巴特雷特窗的主瓣宽度与汉明窗基本一致。
  • 主瓣的宽度影响频率的分辨率,旁瓣的强度影响干扰程度。在实际工程中,应在两者之间进行权衡,选择合适的窗函数。

四、使用FIR数字滤波器进行低通滤波

 本节,我们利用上一节得到的汉明窗截断的FIR滤波器进行低通滤波,使用被噪声污染的正弦函数作为待滤波信号:

\(x\left( n \right) = {\sin n} + {\rm uniform\_ random}\left( - 0.3,~0.3 \right)\)

  1. def get_sin(dot_len):
  2. xs = np.linspace(-math.pi, math.pi, dot_len)
  3. ys = [math.sin(x) + random.uniform(-0.3, 0.3) for x in xs]
  4. return ys

 在\([-π,π]\)上采样的\(N\)点\(x(n)\)的时域图像和\(2N\)点幅度频谱图如下:



可以看到高频分量具有较大的强度。

 按照第二节提到的公式,在频率域进行滤波,即对应位相乘:

  1. fft_y_n_Hamming = []
  2. for i in range(2 * N):
  3. fft_y_n_Hamming.append(fft_h_n_Hamming[i] * fft_sin_to_filter[i]) # 计算频域输出

然后把结果作反变换,获得\(2N\)点时域序列,截去最后一个值,即为卷积的时域结果。

  1. y_n_Hamming = convert_to_real(ifft(fft_y_n_Hamming)) # IFFT变回时域
  2. y_n_Hamming = y_n_Hamming[0:-1] # 去掉最后一个,因为卷积结果长度应为2N-1

 绘制相关图像如下:



 可以看到,高频部分被有效抑制了,噪声被有效消除了,正弦信号被较好地还原了。该FIR滤波器设计和应用成功。

附录:完整代码

fft.py请见实验1

https://www.cnblogs.com/zxuuu/p/12425321.html


fir.py

  1. # coding: utf-8
  2. # 《数字信号处理》课程实验
  3. # FIR数字滤波器设计
  4. # 09017227 卓旭
  5. import numpy as np
  6. import matplotlib.pyplot as plt
  7. plt.rcParams['font.sans-serif'] = ['KaiTi'] # 指定默认字体
  8. plt.rcParams['axes.unicode_minus'] = False
  9. import math
  10. import random
  11. from fft import *
  12. W_C = 0.2 * math.pi
  13. N = 32
  14. '''
  15. 未加窗的无限长h_d(n)
  16. '''
  17. def h_d(w_c):
  18. alpha = (N - 1) / 2. # 系统群时延
  19. vals = []
  20. for i in range(N * 3):
  21. vals.append(math.sin(w_c * (i - alpha)) / float(math.pi * (i - alpha)))
  22. return vals
  23. '''
  24. 矩形窗
  25. '''
  26. def rect_window(one_len):
  27. res = []
  28. for i in range(one_len):
  29. res.append(1.)
  30. return res
  31. '''
  32. Hamming窗
  33. '''
  34. def Hamming_window(length):
  35. res = []
  36. for i in range(length):
  37. res.append(0.54 - 0.46 * math.cos(2 * math.pi * i / (length - 1)))
  38. return res
  39. '''
  40. Barlett窗
  41. '''
  42. def Barlett_window(length):
  43. res = []
  44. for i in range(0, (length - 1) // 2, 1): # [0, (N-1)/2)
  45. res.append(2. * i / (length - 1))
  46. for i in range((length - 1) // 2, N, 1): # [(N-1)/2, N-1]
  47. res.append(2. - 2. * i / (length - 1))
  48. return res
  49. '''
  50. 待滤波的sin(x)
  51. '''
  52. def get_sin(dot_len):
  53. xs = np.linspace(-math.pi, math.pi, dot_len)
  54. ys = [math.sin(x) + random.uniform(-0.3, 0.3) for x in xs]
  55. return ys
  56. '''
  57. 获取加窗结果
  58. '''
  59. def get_windowed(h_d, w_n):
  60. cutted = []
  61. for i in range(N):
  62. cutted.append(h_d[i] * w_n[i])
  63. return cutted
  64. '''
  65. 把DFT结果X(k)插值到X(e^jw)
  66. '''
  67. def interp(xk, dot_len=500):
  68. N = len(xk)
  69. res = []
  70. eps = 0.000001
  71. def phi(w):
  72. w += eps
  73. exp_part = Complex(math.cos((1 - N) * w / 2.), math.sin((1 - N) * w / 2.))
  74. factor = Complex(1 / N * math.sin(N * w / 2.) / math.sin(w / 2.), 0)
  75. return factor * exp_part
  76. xs = np.linspace(0, math.pi, dot_len)
  77. for x in xs:
  78. sum = Complex(0, 0)
  79. for i in range(N):
  80. sum = sum + xk[i] * phi(x - 2. * math.pi * i / N)
  81. res.append(sum)
  82. return (xs, res)
  83. if __name__ == '__main__':
  84. # 生成滤波器无限长冲激响应并绘图
  85. h_d_n = h_d(W_C)
  86. plt.title('滤波器的无限长冲激响应')
  87. plt.xlabel('n')
  88. plt.ylabel('h_d(n)')
  89. xs = range(0, N * 3, 1)
  90. plt.plot(xs, h_d_n, '.')
  91. plt.show()
  92. # 生成三种窗
  93. R_N = rect_window(N)
  94. Barlett_N = Barlett_window(N)
  95. Hamming_N = Hamming_window(N)
  96. # 绘制三种加窗结果(时域)
  97. h_n_rect = get_windowed(h_d_n, R_N)
  98. h_n_Barlett = get_windowed(h_d_n, Barlett_N)
  99. h_n_Hamming = get_windowed(h_d_n, Hamming_N)
  100. plt.subplot(131)
  101. plt.xlabel('n')
  102. plt.ylabel('h(n)')
  103. plt.title('h(n) (矩形窗截断)')
  104. xs = range(0, N)
  105. plt.plot(xs, h_n_rect, '.')
  106. plt.subplot(132)
  107. plt.xlabel('n')
  108. plt.ylabel('h(n)')
  109. plt.title('h(n) (巴雷特窗截断)')
  110. plt.plot(xs, h_n_Barlett, '.')
  111. plt.subplot(133)
  112. plt.xlabel('n')
  113. plt.ylabel('h(n)')
  114. plt.title('h(n) (汉明窗截断)')
  115. plt.plot(xs, h_n_Hamming, '.')
  116. plt.show()
  117. # 将三种加窗转到频域,并绘制其幅度频谱
  118. fft_h_n_rect = fft_dit2(convert_to_complex(add_zero_to_length(h_n_rect, 2 * N))) # 注意是2N点FFT
  119. fft_h_n_Barlett = fft_dit2(convert_to_complex(add_zero_to_length(h_n_Barlett, 2 * N)))
  120. fft_h_n_Hamming = fft_dit2(convert_to_complex(add_zero_to_length(h_n_Hamming, 2 * N)))
  121. plt.subplot(131)
  122. plt.xlabel('w')
  123. plt.ylabel('|H(e^jw)|')
  124. plt.title('|H(e^jw)|(矩形窗截断)')
  125. xs, dtft_h_n_rect = interp(fft_h_n_rect, 500)
  126. plt.plot(xs, convert_to_amplitude(dtft_h_n_rect), '-')
  127. plt.subplot(132)
  128. plt.xlabel('w')
  129. plt.ylabel('|H(e^jw)|')
  130. plt.title('|H(e^jw)|(巴雷特窗截断)')
  131. xs, dtft_h_n_Barlett = interp(fft_h_n_Barlett, 500)
  132. plt.plot(xs, convert_to_amplitude(dtft_h_n_Barlett), '-')
  133. plt.subplot(133)
  134. plt.xlabel('w')
  135. plt.ylabel('|H(e^jw)|')
  136. plt.title('|H(e^jw)|(汉明窗截断)')
  137. xs, dtft_h_n_Hamming = interp(fft_h_n_Hamming, 500)
  138. plt.plot(xs, convert_to_amplitude(dtft_h_n_Hamming), '-')
  139. plt.show()
  140. # 生成噪声干扰后的sin信号待滤波
  141. sin_to_filter = get_sin(N) # 获取N点待滤波信号
  142. plt.subplot(121)
  143. plt.xlabel('n')
  144. plt.ylabel('x(n)')
  145. plt.title('待滤波信号sin(n)+noise(时域)')
  146. plt.plot(range(0, N), sin_to_filter, '.')
  147. # 待滤波信号也做2N点FFT
  148. fft_sin_to_filter = fft_dit2(convert_to_complex(add_zero_to_length(sin_to_filter, 2 * N)))
  149. plt.subplot(122)
  150. plt.xlabel('k')
  151. plt.ylabel('X(k)')
  152. plt.title('待滤波信号sin(n)+noise(幅度频谱)')
  153. plt.plot(range(0, 2 * N), convert_to_amplitude(fft_sin_to_filter), '.')
  154. plt.show()
  155. # 使用汉明窗截断的滤波器进行低通滤波
  156. fft_y_n_Hamming = []
  157. for i in range(2 * N):
  158. fft_y_n_Hamming.append(fft_h_n_Hamming[i] * fft_sin_to_filter[i]) # 计算频域输出
  159. plt.subplot(122)
  160. plt.xlabel('k')
  161. plt.ylabel('Y(k)')
  162. plt.title('滤波结果(幅度频谱)')
  163. plt.plot(range(0, 2 * N), convert_to_amplitude(fft_y_n_Hamming), '.')
  164. y_n_Hamming = convert_to_real(ifft(fft_y_n_Hamming)) # IFFT变回时域
  165. y_n_Hamming = y_n_Hamming[0:-1] # 去掉最后一个,因为卷积结果长度应为2N-1
  166. plt.subplot(121)
  167. plt.xlabel('n')
  168. plt.ylabel('y(n)')
  169. plt.title('滤波结果(时域)')
  170. plt.plot(range(0, 2 * N - 1, 1), y_n_Hamming, '.')
  171. plt.show()

《数字信号处理》课程实验2 – FIR数字滤波器设计的更多相关文章

  1. 《数字信号处理》课程实验1 – FFT的实现

    一.按时间抽选的基-2 FFT实现原理 观察DIT(基2)FFT的流图(N点,N为2的幂次),可以总结出如下规律: (1)共有\(L=\log_2⁡N\)级蝶形运算: (2)输入倒位序,输出自然顺序: ...

  2. 数字信号处理实验(五)——IIR滤波器的设计

    一.使用自编函数设计IIR滤波器 1.冲激响应法 (1)注给出的数字滤波器指标先化成模拟指标 (2)设计出模拟滤波器: (3)使用冲激响应法转化成数字滤波器 (4)一个demo clear all; ...

  3. 转载--关于FPGA设计数字信号处理电路的心得

    FPGA使用的越来越广泛,除了可用于设计控制电路以为,数字信号处理电路更是FPGA的强项和难点.个人可以说才刚刚入门FPGA设计,也做过一些数字信号处理方面的电路设计,记录下个人心得体会. (一)善用 ...

  4. FIR数字滤波器的设计要点

    源:http://blog.sina.com.cn/s/blog_493520900101gt0a.html FIR数字滤波器的设计要点

  5. FPGA与数字信号处理

    过去十几年,通信与多媒体技术的快速发展极大地扩展了数字信号处理(DSP)的应用范围.眼下正在发生的是,以更高的速度和更低的成本实现越来越复杂的算法,这是针对高级信息服更高带宽以及增强的多媒体处理能力等 ...

  6. FIR滤波器设计

    FIR滤波器的优越性: 相位对应为严格的线性,不存在延迟失真,仅仅有固定的时间延迟: 因为不存在稳定性问题,设计相对简单: 仅仅包括实数算法,不涉及复数算法,不须要递推运算,长度为M,阶数为M-1,计 ...

  7. 数字信号处理专题(3)——FFT运算初探

    一.前言 FFT运算是目前最常用的信号频谱分析算法.在本科学习数字信号处理这门课时一直在想:学这些东西有啥用?公式推来推去的,有实用价值么?到了研究生后期才知道,广义上的数字信号处理无处不在:手机等各 ...

  8. Python中的音频和数字信号处理(DSP)

    翻译自Python For Engineers. 1. 创建一个正弦波 在这个项目中,我们将创建一个正弦波,并将其保存为wav文件. 但在此之前,你应该知道一些理论. 频率:频率是正弦波重复一秒的次数 ...

  9. 数字信号处理专题(1)——DDS函数发生器环路Demo

    一.前言 会FPGA硬件描述语言.设计思想和接口协议,掌握些基本的算法是非常重要的,因此开设本专题探讨些基于AD DA数字信号处理系统的一些简单算法,在数字通信 信号分析与检测等领域都会或多或少有应用 ...

随机推荐

  1. vim 复制 单个 单词: 移动光标到单词词首,快速摁 yw

    vim 复制 单个 单词:   移动光标到单词词首,快速摁 yw

  2. GitHub 代码仓库提示:“We found a potential security vulnerability in one of your dependencies”

    github代码仓库提示:“We found a potential security vulnerability in one of your dependencies” 问题描述: Github上 ...

  3. python学习笔记(13)常用模块列表总结

    os模块: os.remove() 删除文件 os.unlink() 删除文件 os.rename() 重命名文件 os.listdir() 列出指定目录下所有文件 os.chdir() 改变当前工作 ...

  4. sqlserver 命令执行sql脚本

    osql -S 主机名 -U 用户名 -P 密码 -i E:\20190723.sql

  5. linux系统开机静态分配ip地址

    在/etc/sysconfig/network-scripts/ifcfg-eth0文件中 添加: IPADDR=192.168.1.100(设置静态地址) NETMASK=255.255.255.0 ...

  6. OpenCV Canny 边缘检测

    #include "opencv2/imgproc/imgproc.hpp" #include "opencv2/highgui/highgui.hpp" #i ...

  7. 用两个栈实现一个队列(C++)

    分析 栈:后进先出 队列:先进先出 要使用两个栈实现队列(先进先出),主要思路是 1.插入一个元素:直接将元素插入stack1即可. 2.删除一个元素:当stack2不为空时 ,直接弹出栈顶元素,当s ...

  8. bootstrap 和datapicker 样式不兼容修复

    修改 datepicker.js内的 layout 方法 function(el) { var options = $(el).data('datepicker'); var cal = $('#' ...

  9. mudbox安装未完成,某些产品无法安装的解决方法

    mudbox提示安装未完成,某些产品无法安装该怎样解决呢?,一些朋友在win7或者win10系统下安装mudbox失败提示mudbox安装未完成,某些产品无法安装,也有时候想重新安装mudbox的时候 ...

  10. python 3新式类的多继承

    因为我用的是python3,所以所用到的类都是新式类,这里我说的都是新式类,python2类的继承复杂一些,主要有新式类和老式类.python3类(新式类)的继承是是广度优先(BFS),实例如下: c ...