首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >频域分片采样,ifft重构后,子带交叠部分是否会有失真?

频域分片采样,ifft重构后,子带交叠部分是否会有失真?

作者头像
云深无际
发布2026-01-07 13:38:30
发布2026-01-07 13:38:30
470
举报
文章被收录于专栏:云深之无迹云深之无迹

稿子一大堆,写的多了又怕大家说我串稀式更新,嘤嘤嘤!

之前不是写频域拼接吗?有人问:频域分片采样,ifft重构后,子带交叠部分是否会有失真?非常好的问题,这其实是频谱拼接式 ADC / 频域分片采样 (Frequency-Sliced Sampling, FSS) 的核心关键之一。(我不确定有没有什么实际的意义)

基本原理回顾

假设原信号 的频谱 在 内,我们将其划分为多个频率子带:

其中第 个子带的带宽为 ,中心频率为 。

每个子带 经过带通滤波后,被分别下变频到基带:

再各自以较低采样率 采样;最后在数字域中,通过上变频 + 拼接 + IFFT(或 IDFT 合成)重构回去。

频域拼接(IFFT 重构)的关键条件

在理想情况下,如果:各个子带的 带宽完全等分;各子带间 频率分界点无重叠也无空隙;每个子带采样前的 模拟带通滤波器 边缘响应理想(即矩形带通);数字重构时 子带的幅度和相位都完美对齐(含 group delay);

则在 IFFT 拼接后:

不会有任何失真。

实际中出现子带交叠失真的三种典型原因

(1)滤波器过渡带引起的幅度交叠

如果每个子带的带通滤波器不是理想矩形(实际上不可能),其过渡带会造成频谱交叠,如下图所示:

代码语言:javascript
复制
  X0(f)    X1(f)
  ┌────┐   ┌────┐
     └───┬──┘
         ↑ 过渡带交叠区

在重构时,如果两个子带的重叠部分都被保留,则叠加后该频段能量会双计 → 幅度失真(Amplitude Ripple),解决办法是在交叠区加 cosine taper / overlap-add 窗函数(类似 STFT 拼接)。

(2)相位未对齐(group delay mismatch)

每个子带可能来自不同通道(不同滤波器、不同ADC路径),其相位响应或群延迟略有不同:

拼接时,频域相位突变 → 时域表现为波形边界振铃、过冲,类似“stitching glitch”或“Gibbs效应”;需要校准每个子带的 group delay;在数字域中补偿相位;或使用平滑的 crossfade 方式拼接。

(3)FFT/IFFT 网格不匹配或带宽边界对齐误差

如果分片不是在 FFT 网格上整齐切割(例如频率分辨率 Δf 不对齐),则 IFFT 时每个子带对应的 bin 不连续,出现频谱泄漏 → 时域失真;需要统一采样点数与 FFT 长度;子带频率边界必须是 FFT bin 的整数倍;或使用小重叠(50% overlap)+ 窗函数拼接法。

用数学公式看“交叠失真”

设两个相邻子带为:

其中 与 的过渡带有重叠区 。

拼接结果为:

如果在交叠区 , 就会出现失真:若 >1 → 幅度过高;若 <1 → 幅度缺损;若相位不一致 → 相位失真。

因此理想拼接需满足:

这正是“Perfect Reconstruction Filter Bank(完美重构滤波器组)”的基本条件。

总结一下

要恢复的部分

理想情况

现实失真来源

解决方案

幅度响应

子带边界无重叠

过渡带重叠导致幅度双计

overlap-add 窗平滑

相位响应

各通道群延一致

相位不对齐造成振铃

group delay 校准

频率分辨率

FFT bin 完全对齐

不对齐导致泄漏

统一采样点/FFT 长度

滤波器设计

PRFB 满足 H₁+H₂=1

滤波器设计不完美

采用Cosine-Modulated FB

仿真

读者肯定说,教练,我看不懂!

“两个频域子带有 10% 过渡带交叠时,IFFT 重构的时域波形与理想波形对比(含幅度失真与相位畸变)”

绘制出:频域拼接前后幅度响应;IFFT 后时域波形差异;幅度误差随交叠比例变化曲线。

代码语言:javascript
复制
STDOUT/STDERR
=== Reconstruction RMS Error (time-domain, normalized) ===
Case A (Bad rectangular + overlap double-count): 0.099867
Case B (Good complementary raised-cosine):      0.000000
Case C (Good mag, but subband-2 delay τ=3.0):  0.490463
Case A: Bad masks (rectangular + overlap double-count)
Case A: Bad masks (rectangular + overlap double-count)

Case A: Bad masks (rectangular + overlap double-count)

子带有重叠但直接相加(双计)→ 失真

图“Case A”里 H1+H2 在交叠区等于 2,导致频域能量双计;时域重构 RMS 误差 ≈ 0.0999;误差谱主要集中在分界处(图“Error spectra around crossover”)。

在交叠区做互补 crossfade(raised-cosine,保证 H1+H2=1)→ 无幅度失真

图“Case B”里 H1+H2 ≡ 1;时域重构 RMS 误差 = 0(数值精度内);这就是完美重构滤波器组(PRFB)的核心条件:交叠区满足 。

群延迟不一致(相位错配)→ 即使幅度互补也会失真

给子带2加了 τ=3 个采样的额外延迟;有效合成增益 在分界附近出现凹陷(图“Case C”);时域 RMS 误差升至 0.4905,局部波形出现振铃(最后一张图)。

扩展到 K>2 子带(均匀或非均匀滤波器组)

仿真完成过渡带宽度 vs 误差延迟偏差 vs 误差画参数扫描热力图。

代码语言:javascript
复制
=== Single-case RMS (vs x_ref) ===
Ideal: 6.274424e-17
Delay mismatch τ=2 on band2: 0.453603
Gain mismatch +0.5 dB on band2: 0.014614

=== RMSE vs overlap for τ in {0,1,2,3,5} (K=4) ===
tau=0.0: 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000
tau=1.0: 0.4081, 0.4071, 0.4045, 0.4019, 0.3993, 0.3967, 0.3941, 0.3914, 0.3888
tau=2.0: 0.4600, 0.4573, 0.4547, 0.4519, 0.4492, 0.4463, 0.4435, 0.4406, 0.4376
tau=3.0: 0.1978, 0.1941, 0.1918, 0.1895, 0.1873, 0.1852, 0.1833, 0.1814, 0.1797
tau=5.0: 0.4305, 0.4293, 0.4279, 0.4264, 0.4247, 0.4229, 0.4210, 0.4189, 0.4167

=== RMSE vs gain mismatch (dB) for overlap in {0,0.1,0.2,0.3,0.4} (τ=0) ===
overlap=0.0: 0.0272, 0.0207, 0.0140, 0.0071, 0.0000, 0.0073, 0.0148, 0.0225, 0.0305
overlap=0.1: 0.0269, 0.0205, 0.0138, 0.0070, 0.0000, 0.0072, 0.0147, 0.0223, 0.0302
overlap=0.2: 0.0265, 0.0202, 0.0137, 0.0069, 0.0000, 0.0071, 0.0145, 0.0220, 0.0298
overlap=0.3: 0.0262, 0.0199, 0.0135, 0.0068, 0.0000, 0.0070, 0.0143, 0.0217, 0.0294
overlap=0.4: 0.0258, 0.0197, 0.0133, 0.0067, 0.0000, 0.0069, 0.0141, 0.0214, 0.0290

只要交叠区做“互补窗”使 ,则在幅度层面可以做到完美重构

把 4 子带的互补 raised-cosine 交叉淡入淡出实现为:每个边界两侧各一半过渡,交叠内 与 互补,整带求和恒等于 1;这就是 K 通道完美重构滤波器组(PRFB)的“Partition of Unity”。

相位/群延迟不一致会引入显著失真(即使幅度互补)。

代码语言:javascript
复制
=== K=4 single-case RMS errors ===
Ideal (mag+phase perfect): 5.196418e-01
Delay mismatch (band2 τ=2.0): 0.836231
Gain mismatch (band2 +0.5 dB):  0.512645
=== Sweep summary (delay mismatch) ===
Min RMSE=5.008023e-01 at τ=0.000 samples, overlap=0.000 of subband
=== Sweep summary (gain mismatch) ===

仿真(K=4,子带2额外延迟 )的单例结果:幅度与相位都理想:RMS 误差 ≈ 6.5e−17(数值零)。

仅子带2 延迟 个采样:RMS ≈ 0.4536(很大)。

仅子带2 增益 +0.5 dB:RMS ≈ 0.0146(比相位错配小很多)。

对齐的时候先做相位/群延迟标定,再谈幅度微调相位错一点点,代价远大于轻微增益误差。

后记(

“频域分片采样,IFFT 重构后,子带交叠部分是否会有失真?”

不会——前提是:交叠区采用互补窗,满足 ;子带之间相位/群延迟已对齐(或在数字域补偿);分界点与 FFT 网格尽可能对齐。

会——如果

交叠直接相加导致双计(幅度起伏);相位/群延迟不一致(会出现明显失真/振铃);网格不对齐且无交叠窗,导致泄漏与拼接撕裂

代码语言:javascript
复制
import numpy as np
import matplotlib.pyplot as plt

np.random.seed(2)

# ---------- Test signal ----------
N = 65536
fs = 1.0
t = np.arange(N) / fs

# Multisine + noise (real)
num_tones = 160
freqs = np.linspace(0.005, 0.49, num_tones)
phases = 2*np.pi*np.random.rand(num_tones)
x = np.zeros(N)
for f, phi in zip(freqs, phases):
    x += np.cos(2*np.pi*f*t + phi)
x += np.random.randn(N)*0.15
x = x / np.std(x)

X = np.fft.fftshift(np.fft.fft(x))
freq = np.fft.fftshift(np.fft.fftfreq(N, d=1/fs))

# ---------- Helper: build K complementary masks with raised-cosine overlaps ----------
def build_K_masks(freq, K=4, f_low=0.0, f_high=0.49, overlap_frac=0.1):
    Hs = [np.zeros_like(freq, dtype=float) for _ in range(K)]
    B = (f_high - f_low) / K
    ov = overlap_frac * B
    for k in range(K):
        fk_left = f_low + k*B
        fk_right = fk_left + B
        a = fk_left + ov/2.0
        b = fk_right - ov/2.0
        if a < b:
            idx_core = (freq >= a) & (freq <= b)
            Hs[k][idx_core] = 1.0
        if k > 0 and ov > 0:
            aL = fk_left - ov/2.0
            bL = fk_left + ov/2.0
            idx = (freq >= aL) & (freq <= bL)
            w = 0.5*(1 - np.cos(np.pi*(freq[idx]-aL)/(bL-aL)))  # 0->1
            Hs[k][idx] += w
        if k < K-1 and ov > 0:
            aR = fk_right - ov/2.0
            bR = fk_right + ov/2.0
            idx = (freq >= aR) & (freq <= bR)
            w = 0.5*(1 + np.cos(np.pi*(freq[idx]-aR)/(bR-aR)))  # 1->0
            Hs[k][idx] += w
    return Hs

def reconstruct(X, freq, Hs, tau_list=None, gain_list=None):
    K = len(Hs)
    Y = np.zeros_like(X, dtype=complex)
    for k in range(K):
        Hk = Hs[k]
        phase = 1.0
        if tau_list is not None:
            tau = tau_list[k]
            phase = np.exp(-1j*2*np.pi*freq*tau)
        gain = 1.0 if gain_list is None else gain_list[k]
        Y += (gain * Hk) * phase * X
    y = np.fft.ifft(np.fft.ifftshift(Y)).real
    return y, Y

def rms(a):
    return np.sqrt(np.mean(a**2))

# Target band for perfect reconstruction
K = 4
overlap_frac = 0.12
f_low = 0.0
f_high = 0.49
Hs = build_K_masks(freq, K=K, f_low=f_low, f_high=f_high, overlap_frac=overlap_frac)

# Reference: bandlimited version of x (what the system intends to pass)
Hsum = np.zeros_like(freq)
for Hk in Hs:
    Hsum += Hk
Y_ref = Hsum * X
x_ref = np.fft.ifft(np.fft.ifftshift(Y_ref)).real

# Perfect case
y_ideal, Y_ideal = reconstruct(X, freq, Hs)
e_ideal = y_ideal - x_ref

# Delay mismatch on band2
tau = 2.0
tau_list = [0.0, 0.0, tau, 0.0]
y_delay, Y_delay = reconstruct(X, freq, Hs, tau_list=tau_list)
e_delay = y_delay - x_ref

# Gain mismatch on band2 (+0.5 dB)
g_lin = 10**(0.5/20)
gain_list = [1.0, 1.0, g_lin, 1.0]
y_gain, Y_gain = reconstruct(X, freq, Hs, gain_list=gain_list)
e_gain = y_gain - x_ref

print("=== K=4 single-case RMS errors (vs x_ref in [0, 0.49]) ===")
print(f"Ideal (mag+phase perfect): {rms(e_ideal):.6e}")
print(f"Delay mismatch (band2 τ={tau}): {rms(e_delay):.6f}")
print(f"Gain mismatch (band2 +0.5 dB):  {rms(e_gain):.6f}")

# Plot masks
plt.figure(figsize=(8,4.5))
for k, Hk in enumerate(Hs):
    plt.plot(freq, Hk, label=f"H{k}")
plt.plot(freq, Hsum, label="Sum", linewidth=2)
plt.xlim(0.0, 0.5)
plt.ylim(-0.05, 1.2)
plt.title("K=4 complementary masks and their sum")
plt.xlabel("Normalized frequency (cycles/sample)")
plt.ylabel("Gain")
plt.legend()
plt.tight_layout()
plt.show()

# Heatmap sweep: RMSE vs overlap and delay (vs x_ref)
ov_list = np.linspace(0.0, 0.4, 21)
tau_list_grid = np.linspace(0.0, 5.0, 26)
RMSE = np.zeros((len(tau_list_grid), len(ov_list)))

for i, tau_val in enumerate(tau_list_grid):
    for j, ov_frac in enumerate(ov_list):
        Hs_tmp = build_K_masks(freq, K=K, f_low=f_low, f_high=f_high, overlap_frac=ov_frac)
        Hsum_tmp = np.zeros_like(freq)
        for Hk in Hs_tmp: Hsum_tmp += Hk
        x_ref_tmp = np.fft.ifft(np.fft.ifftshift(Hsum_tmp * X)).real
        tl = [0.0, 0.0, tau_val, 0.0]
        y_tmp, _ = reconstruct(X, freq, Hs_tmp, tau_list=tl)
        RMSE[i,j] = rms(y_tmp - x_ref_tmp)

plt.figure(figsize=(8,4.5))
plt.imshow(RMSE, aspect='auto', origin='lower',
           extent=[ov_list[0], ov_list[-1], tau_list_grid[0], tau_list_grid[-1]])
plt.colorbar(label="RMS error")
plt.xlabel("Overlap fraction of subband width")
plt.ylabel("Delay mismatch τ (samples)")
plt.title("RMS error vs overlap and delay mismatch (K=4)")
plt.tight_layout()
plt.show()

# Slices: RMSE vs overlap for selected τ
plt.figure(figsize=(8,4.5))
for tau_val in [0.0, 1.0, 2.0, 3.0, 5.0]:
    idx = np.argmin(np.abs(tau_list_grid - tau_val))
    plt.plot(ov_list, RMSE[idx,:], label=f"τ={tau_val}")
plt.xlabel("Overlap fraction of subband width")
plt.ylabel("RMS error")
plt.title("RMSE vs overlap for different delay mismatches")
plt.legend()
plt.tight_layout()
plt.show()

# Heatmap: RMSE vs overlap and gain mismatch (no delay)
g_db_list = np.linspace(-1.0, 1.0, 21)
RMSEg = np.zeros((len(g_db_list), len(ov_list)))
for i, gdb in enumerate(g_db_list):
    glin = 10**(gdb/20)
    for j, ov_frac in enumerate(ov_list):
        Hs_tmp = build_K_masks(freq, K=K, f_low=f_low, f_high=f_high, overlap_frac=ov_frac)
        Hsum_tmp = np.zeros_like(freq)
        for Hk in Hs_tmp: Hsum_tmp += Hk
        x_ref_tmp = np.fft.ifft(np.fft.ifftshift(Hsum_tmp * X)).real
        y_tmp, _ = reconstruct(X, freq, Hs_tmp, gain_list=[1.0, 1.0, glin, 1.0])
        RMSEg[i,j] = rms(y_tmp - x_ref_tmp)

plt.figure(figsize=(8,4.5))
plt.imshow(RMSEg, aspect='auto', origin='lower',
           extent=[ov_list[0], ov_list[-1], g_db_list[0], g_db_list[-1]])
plt.colorbar(label="RMS error")
plt.xlabel("Overlap fraction of subband width")
plt.ylabel("Gain mismatch on band2 (dB)")
plt.title("RMS error vs overlap and gain mismatch (τ=0, K=4)")
plt.tight_layout()
plt.show()

# Slices: RMSE vs gain mismatch for selected overlaps
plt.figure(figsize=(8,4.5))
for ov_frac in [0.0, 0.1, 0.2, 0.3, 0.4]:
    j = np.argmin(np.abs(ov_list - ov_frac))
    plt.plot(g_db_list, RMSEg[:,j], label=f"overlap={ov_frac}")
plt.xlabel("Gain mismatch on band2 (dB)")
plt.ylabel("RMS error")
plt.title("RMSE vs gain mismatch for different overlaps (τ=0)")
plt.legend()
plt.tight_layout()
plt.show()

# Summary
i_min, j_min = np.unravel_index(np.argmin(RMSE), RMSE.shape)
print("=== Sweep summary (delay mismatch) ===")
print(f"Min RMSE={RMSE[i_min,j_min]:.6e} at τ={tau_list_grid[i_min]:.3f} samples, overlap={ov_list[j_min]:.3f}")

i2_min, j2_min = np.unravel_index(np.argmin(RMSEg), RMSEg.shape)
print("=== Sweep summary (gain mismatch) ===")
print(f"Min RMSE={RMSEg[i2_min,j2_min]:.6e} at gain_err={g_db_list[i2_min]:.3f} dB, overlap={ov_list[j2_min]:.3f}"
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2025-11-02,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 云深之无迹 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 基本原理回顾
  • 频域拼接(IFFT 重构)的关键条件
  • 实际中出现子带交叠失真的三种典型原因
    • (1)滤波器过渡带引起的幅度交叠
    • (2)相位未对齐(group delay mismatch)
    • (3)FFT/IFFT 网格不匹配或带宽边界对齐误差
  • 用数学公式看“交叠失真”
  • 总结一下
  • 仿真
  • 扩展到 K>2 子带(均匀或非均匀滤波器组)
  • 后记(
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档