从冲击响应函数计算 FIR 系数
在数字通信系统中,成型滤波器(Pulse Shaping Filter)用于限制信号带宽并减少码间串扰(ISI)。通常,滤波器系数通过对理论冲击响应 h(t)h(t)h(t) 采样得到,或通过在频域直接设计满足目标幅度响应的 FIR 滤波器。本文将以一个具体的根升余弦(RRC)脉冲为例,给出两种计算 FIR 系数的方法,并提供完整的 Python 实现。
1. 问题描述
给定基带信号的复包络定义:
x(t)=p(t)[∑k=−∞+∞akh(t−kT)] x(t) = p(t) \left[ \sum_{k=-\infty}^{+\infty} a_k h(t-kT) \right] x(t)=p(t)[k=−∞∑+∞akh(t−kT)]
其中 TTT 为码片宽度,h(t)h(t)h(t) 为成型滤波器的冲击响应,其解析表达式为:
h(t)=57T2π((57T)2−t2)[cos(1.35πtT)+5T7tsin(0.65πtT)] h(t) = \frac{\frac{5}{7}T^2}{\pi\left(\left(\frac{5}{7}T\right)^2 - t^2\right)} \left[ \cos\left(\frac{1.35\pi t}{T}\right) + \frac{5T}{7t}\sin\left(\frac{0.65\pi t}{T}\right) \right] h(t)=π((75T)2−t2)75T2[cos(T1.35πt)+7t5Tsin(T0.65πt)]
对应的幅度响应为:
∣H(f)∣=12{1−sin[π(2∣f∣T−1)0.7]} |H(f)| = \sqrt{\frac{1}{2} \left\{ 1 - \sin \left[ \frac{\pi(2|f|T-1)}{0.7} \right] \right\}} ∣H(f)∣=21{1−sin[0.7π(2∣f∣T−1)]}
该幅度响应实际上是滚降因子 α=0.35\alpha = 0.35α=0.35 的**根升余弦(Root Raised Cosine, RRC)**滤波器,其通带截止频率 fp=(1−α)/(2T)=0.325/Tf_p = (1-\alpha)/(2T) = 0.325/Tfp=(1−α)/(2T)=0.325/T,阻带起始频率 B=(1+α)/(2T)=0.675/TB = (1+\alpha)/(2T) = 0.675/TB=(1+α)/(2T)=0.675/T。
我们的目标:获得可直接在 FPGA/DSP 中使用的 FIR 滤波器系数。
2. 方法一:时域直接采样法
最直观的方法是对解析表达式 h(t)h(t)h(t) 进行等间隔采样,并截断至有限长度。由于 h(t)h(t)h(t) 是偶函数,系数具有对称性。
2.1 参数选择
- 码片宽度 T=1T = 1T=1(归一化)
- 过采样率 M=4M = 4M=4 → 采样间隔 Ts=T/M=0.25T_s = T/M = 0.25Ts=T/M=0.25
- 截断时间 t∈[−5T, 5T]t \in [-5T,\,5T]t∈[−5T,5T] → 单边点数 N=5T/Ts=20N = 5T / T_s = 20N=5T/Ts=20,总抽头数 L=2N+1=41L = 2N+1 = 41L=2N+1=41
2.2 处理 t=0t=0t=0 处的奇点
直接代入 t=0t=0t=0 时分母为零,需取极限。通过洛必达法则或级数展开可得:
h(0)=0.65+75π≈1.095633 h(0) = 0.65 + \frac{7}{5\pi} \approx 1.095633 h(0)=0.65+5π7≈1.095633
2.3 Python 实现
python
import numpy as np
T = 1.0 # 码片宽度
M = 4 # 过采样率
Ts = T / M # 采样间隔
N = 20 # 半长度
t = np.arange(-N, N + 1) * Ts # 时间采样点
h = np.zeros_like(t)
# t = 0 处
t0_mask = (t == 0)
h[t0_mask] = 0.65 + 7 / (5 * np.pi)
# t ≠ 0 处
t_nonzero = t[~t0_mask]
A = 35.0 / (np.pi * (25.0 - 49.0 * t_nonzero**2))
B = np.cos(1.35 * np.pi * t_nonzero) + (5.0 / (7.0 * t_nonzero)) * np.sin(0.65 * np.pi * t_nonzero)
h[~t0_mask] = A * B
print("FIR coefficients (41 taps):")
for i, coeff in enumerate(h):
print(f"{i:2d} {coeff:.8f}")
运行结果节选(系数对称,仅显示前 10 个):
0 0.00750264
1 -0.00237777
2 -0.01162662
3 -0.01027066
4 0.00204301
5 0.01332026
6 0.00957238
7 -0.00954908
8 -0.02545367
9 -0.01477516
10 0.02561499
...
此方法简单直接,但截断会引起频域 Gibbs 现象。若需更精确的频域特性,应采用频域设计法。
3. 方法二:频域采样法(频率采样设计)
频率采样法的核心思想:在离散频域指定期望的幅度响应和线性相位,然后通过 IFFT 得到 FIR 系数 。该方法可以保证在采样频点上严格匹配 ∣H(f)∣|H(f)|∣H(f)∣。
3.1 目标幅度响应(根升余弦)
归一化 T=1T=1T=1,滚降因子 α=0.35\alpha = 0.35α=0.35:
∣H(f)∣={1,0≤∣f∣≤1−α212[1+cos(πα(∣f∣−1−α2))],1−α2<∣f∣≤1+α20,∣f∣>1+α2 |H(f)| = \begin{cases} 1, & 0 \le |f| \le \frac{1-\alpha}{2} \\[0.5em] \sqrt{\dfrac12 \left[1 + \cos\left(\dfrac{\pi}{\alpha}\left(|f| - \frac{1-\alpha}{2}\right)\right)\right]}, & \frac{1-\alpha}{2} < |f| \le \frac{1+\alpha}{2} \\[0.5em] 0, & |f| > \frac{1+\alpha}{2} \end{cases} ∣H(f)∣=⎩ ⎨ ⎧1,21[1+cos(απ(∣f∣−21−α))] ,0,0≤∣f∣≤21−α21−α<∣f∣≤21+α∣f∣>21+α
该表达式与题目中给出的 ∣H(f)∣|H(f)|∣H(f)∣ 完全等价(可利用三角恒等式 (1−sinθ)/2=cos(θ/2+π/4)\sqrt{(1-\sin\theta)/2} = \cos(\theta/2 + \pi/4)(1−sinθ)/2 =cos(θ/2+π/4) 等证明)。
3.2 线性相位约束
为使滤波器具有线性相位(群延迟恒定),令:
H(f)=∣H(f)∣⋅e−jπ(L−1)fTs H(f) = |H(f)| \cdot e^{-j\pi (L-1) f T_s} H(f)=∣H(f)∣⋅e−jπ(L−1)fTs
其中 LLL 为抽头数(奇数),Ts=1/FsT_s = 1/F_sTs=1/Fs 为采样间隔。该相位对应时域冲击响应的对称中心位于 (L−1)/2(L-1)/2(L−1)/2 处。
3.3 参数化 Python 实现
python
import numpy as np
import matplotlib.pyplot as plt
from scipy import signal
# ---------- 用户可调参数 ----------
T = 1.0 # 码片宽度(归一化)
alpha = 0.35 # 滚降因子
M = 8 # 过采样率(采样频率 Fs = M/T)
K = 5 # 截断时间 ±K*T
# --------------------------------
Fs = M / T
Ts = 1 / Fs
N = int(K * T / Ts) # 单边采样点数
L = 2 * N + 1 # 总抽头数(奇数)
print(f"抽头数: {L} (M={M}, K={K})")
# 目标幅度响应函数
def rrc_magnitude(f):
f_abs = np.abs(f)
mag = np.zeros_like(f_abs)
fp = (1 - alpha) / (2 * T) # 0.325
B = (1 + alpha) / (2 * T) # 0.675
# 通带
idx1 = f_abs <= fp
mag[idx1] = 1.0
# 滚降段
idx2 = (f_abs > fp) & (f_abs <= B)
arg = (np.pi / alpha) * (f_abs[idx2] - fp)
mag[idx2] = np.sqrt(0.5 * (1 + np.cos(arg)))
# 阻带 mag = 0(已初始化)
return mag
# 频率采样
freq = np.fft.fftfreq(L, d=Ts)
freq = np.fft.fftshift(freq) # 顺序: -Fs/2 ... 0 ... Fs/2
H_mag = rrc_magnitude(np.abs(freq))
# 添加线性相位
delay = (L - 1) / 2
phase = -2 * np.pi * freq * delay * Ts
H = H_mag * np.exp(1j * phase)
# IFFT 得脉冲响应
H_shifted = np.fft.ifftshift(H) # 恢复 FFT 标准顺序
h_freq = np.fft.ifft(H_shifted)
h_freq = np.real(h_freq) # 应为实序列
h_freq = h_freq / np.sum(h_freq) # 归一化通带增益为 1
# 验证频率响应
w, H_fir = signal.freqz(h_freq, worN=8192, fs=Fs)
H_mag_fir = np.abs(H_fir)
# 绘图对比
plt.figure(figsize=(12,5))
plt.subplot(1,2,1)
plt.stem(h_freq, basefmt=" ")
plt.title(f"FIR 脉冲响应 (抽头数 {L})")
plt.xlabel("样本索引")
plt.ylabel("幅度")
plt.subplot(1,2,2)
plt.plot(w, H_mag_fir, label="设计滤波器", lw=2)
plt.plot(w, rrc_magnitude(w), '--', label="目标 |H(f)|", lw=2)
plt.xlim(0, 1.2 * (1+alpha)/(2*T))
plt.ylim(-0.1, 1.2)
plt.xlabel("频率 (Hz, T=1)")
plt.ylabel("幅度")
plt.legend()
plt.grid(True)
plt.tight_layout()
plt.show()
# 输出系数(对称,仅正半轴)
print("\nFIR 系数(正索引部分):")
for i in range(N+1):
print(f"h[{i:2d}] = {h_freq[N+i]:.8f}")
运行结果示例(M=8, K=5 → 81 抽头):
抽头数: 81 (M=8, K=5)
FIR 系数(正索引部分):
h[ 0] = 0.13676205
h[ 1] = 0.13232285
h[ 2] = 0.11955565
h[ 3] = 0.10002697
h[ 4] = 0.07607989
...
h[40] = 0.00204880
频率响应对比图显示,设计滤波器的幅度在采样频点上严格等于目标值,整体逼近效果良好。
4. 两种方法的比较
| 特性 | 时域直接采样法 | 频域采样法 |
|---|---|---|
| 原理 | 对解析 h(t)h(t)h(t) 采样并截断 | 在频域指定幅度+线性相位,IFFT |
| 实现难度 | 简单 | 稍复杂(需构造频域向量) |
| 频域匹配 | 有截断误差,通带边缘可能不精确 | 在采样频点上严格匹配 |
| 适用场景 | 快速原型,理论脉冲已知 | 需要精确频域形状的工程实现 |
| 参数影响 | 过采样率 MMM、截断长度 NNN | 过采样率 MMM、滤波器长度 LLL |
结论:若系统对带外抑制要求较高,或需要严格符合标准频谱模板,推荐使用频域采样法(或等效的 Parks-McClellan 最优设计)。时域采样法更适合理论验证或资源受限且对频域精度要求不高的场合。
5. 总结
本文以根升余弦成型滤波器为例,详细介绍了从冲击响应解析式计算 FIR 系数的两种方法,并给出了可直接运行的 Python 代码。关键点总结如下:
- 时域采样法:直接对 h(t)h(t)h(t) 采样,注意处理奇点,系数对称。
- 频域采样法:构造目标幅度响应,附加线性相位,IFFT 得到系数。
- 过采样率 MMM 和滤波器长度 LLL 可根据系统要求灵活调整。
- 提供的代码具有良好可读性和可扩展性,只需修改顶部参数即可生成不同规格的滤波器。
研究学习不易,点赞易。
工作生活不易,收藏易,点收藏不迷茫 :)