【芯片测试】:DSP 测试基础

DSP 测试基础

混合信号测试中的数字信号处理基础

本教程系统讲解混合信号测试中四个基础专题:采样与量化、DAC 正弦波形生成、DAC 输出波形与 SINC 加权、多音信号生成。

每一条理论结论都配有可独立运行的标准 Python(NumPy / Matplotlib / SciPy)验证代码

你不需要任何测试机就能在自己的电脑上把每一个结论亲手跑出来、验证一遍。


0. 先建立一张"全局地图":什么是 DSP-Based Testing

在混合信号(Mixed Signal)测试领域,最典型的被测器件(DUT, Device Under Test)就是两类转换器:

  • DAC(数模转换器):把数字码转换成模拟电压。
  • ADC(模数转换器):把模拟电压转换成数字码。

测试机是怎么测它们的呢?测试机内部其实也用着同样的两类转换器:

测试机资源 内部核心 作用
AWG(任意波形发生器) 内含一个 DAC 用"数学方法"算出一段波形数据,送给 DAC,产生模拟激励信号
数字化仪 / 采样器(Digitizer / Sampler) 内含一个 ADC 把被测模拟信号采集成数字数据,再用"数学方法"算出各种参数

也就是说:激励信号是"算"出来的,测量结果也是"算"出来的 。这套"用数学方法生成与分析信号"的方法论,本质就是数字信号处理(DSP, Digital Signal Processing) ,因此整套测试方法被称为 DSP-Based Testing(基于 DSP 的测试) 。其中最强大的工具是 FFT(快速傅里叶变换)

作为混合信号方向的测试 / 应用工程师,必须把这套基础吃透。本教程覆盖以下四个专题:

  1. 专题一:离散信号、采样定理、量化定理(SNR 与 ENOB 的来历)。
  2. 专题二 :用 DAC 生成正弦波,最少需要多少个数据点?以及一个常见的"缺码"陷阱。
  3. 专题三:DAC 的实际输出波形(零阶保持)、SINC 加权、平滑滤波器与 AWG 结构、多音信号。
  4. 专题四:多音(Multi-tone)信号的三种生成方法及其工程取舍。

环境准备 :本教程所有代码基于 Python 3,依赖 numpymatplotlibscipy

安装:pip install numpy matplotlib scipy


1. 离散信号、采样定理与量化定理

1.1 离散信号的两个维度

DSP 就是 discrete signal processing(离散信号处理) 。一个离散信号有两个维度(见下图):

  • 横轴(X 轴)= 时间:把连续时间切成一个个采样时刻 → 这一维由**采样定理(Sampling Theorem)**约束。
  • 纵轴(Y 轴)= 幅度(通常是电压):把连续电压近似成一个个离散电平 → 这一维由**量化定理(Quantization Theorem)**约束。

理解这两条定理,是一切混合信号测试的起点。

1.2 采样定理(Sampling Theorem)

定理内容 :如果一个信号是带限的 (频率有上限),并且采样频率大于信号带宽的两倍 ,那么就可以从采样点完美重建原始信号,不丢失任何信息。

几个必须背下来的术语(设采样率为 FsF_sFs):

  • 奈奎斯特频率(Nyquist frequency) :Fs/2F_s/2Fs/2。
  • 奈奎斯特带(Nyquist band) :从 0 到 Fs/2F_s/2Fs/2 的频带。
  • 采样定理的硬性要求 :Fs>2⋅FmaxF_s > 2 \cdot F_{max}Fs>2⋅Fmax,其中 FmaxF_{max}Fmax 是输入信号的最高频率。

如果违反这条规则会怎样? 频率高于 Fs/2F_s/2Fs/2 的信号会"折叠"落回基带(奈奎斯特带内),这种现象叫 混叠(Aliasing) 。混叠后你无法再把混叠信号和原始基带信号分开------信息已经损坏了。

下图左:正确采样(Fs=10 HzF_s=10\,\text{Hz}Fs=10Hz 采 2 Hz2\,\text{Hz}2Hz 信号),采样点忠实还原原波形;

下图右:违反规则(Fs=10 HzF_s=10\,\text{Hz}Fs=10Hz 采 8 Hz8\,\text{Hz}8Hz 信号),8 Hz8\,\text{Hz}8Hz 折叠成了一个假的 2 Hz2\,\text{Hz}2Hz 信号(红色虚线),完全骗过了我们。

工程对策

  • 数字化仪 测量时,必须把输入信号限制在奈奎斯特带内。为此数字化仪内部带有多个低通滤波器(LPF)来滤掉高频噪声和不需要的信号,这种滤波器叫 抗混叠滤波器(Anti-aliasing filter)
  • 信号频率越高,对数字化仪的采样率要求就越高,很容易超出数字化仪的能力。这时就改用另一种测量资源------波形采样器(Sampler)

关于采样器与"欠采样" :在常规数字化仪应用中混叠是有害的,但采样器却故意利用混叠 。采样器有极宽的输入带宽,通常不带 滤波器,因为它就是要捕获混叠信号。这种"故意违反采样定理"的做法常被称为 欠采样(Under-sampling)------"欠"指采样(数字化)频率低于目标测试信号频率。

1.3 量化定理(Quantization Theorem)

现在看另一个维度。把模拟电压近似成若干离散电平,这个过程叫 量化(Quantization) 。最典型的量化器件就是 ADC

量化过程中永远存在量化噪声 / 量化误差。下面我们来推导那条"ADC/DAC 领域最重要的公式"。

量化误差的模型

参考一个 nnn 位线性 ADC:它有 2n2^n2n 个码,从码 0 到码 2n−12^n-12n−1。设单个码的大小为 QQQ(即 1 LSB 对应的电压),则:

  • 落在 Vk−Q/2V_k - Q/2Vk−Q/2 到 Vk+Q/2V_k + Q/2Vk+Q/2 之间的模拟输入电压,都会被表示成码 kkk(VkV_kVk 是码 kkk 的量化电平,即两个相邻跳变阈值的中点)。
  • 在一个码内部,量化误差是一条在 −Q/2,+Q/2-Q/2, +Q/2−Q/2,+Q/2 之间均匀分布的锯齿。

下图直观展示了量化(3 位、Q=1Q=1Q=1):左边是阶梯式量化输出,右边是被限制在 ±Q/2\pm Q/2±Q/2 之间的量化误差。

推导(一):量化噪声的 RMS

量化误差在 −Q/2,+Q/2-Q/2, +Q/2−Q/2,+Q/2 内均匀分布,对它求均方根(RMS):

Quant.Noise(RMS)=1Q∫−Q/2Q/2x2 dx=Q212=Q12=Q23(1) \text{Quant.Noise(RMS)} = \sqrt{\frac{1}{Q}\int_{-Q/2}^{Q/2} x^2\,dx} = \sqrt{\frac{Q^2}{12}} = \frac{Q}{\sqrt{12}} = \frac{Q}{2\sqrt{3}} \tag{1} Quant.Noise(RMS)=Q1∫−Q/2Q/2x2dx =12Q2 =12 Q=23 Q(1)

记住这个结论:理想量化噪声的 RMS 恒等于 Q/12Q/\sqrt{12}Q/12

推导(二):满量程正弦信号的 RMS

ADC 输入范围的满量程是 2nQ2^n Q2nQ。当一个满量程正弦波 加到 ADC 上时,其幅度(峰值)为满量程的一半 =2nQ/2= 2^n Q / 2=2nQ/2,而正弦波的 RMS 等于峰值除以 2\sqrt{2}2 :

Signal_Amplitude(RMS)=Full_Scale/22=2nQ/22=2nQ22(2) \text{Signal\_Amplitude(RMS)} = \frac{\text{Full\_Scale}/2}{\sqrt{2}} = \frac{2^n Q / 2}{\sqrt{2}} = \frac{2^n Q}{2\sqrt{2}} \tag{2} Signal_Amplitude(RMS)=2 Full_Scale/2=2 2nQ/2=22 2nQ(2)

推导(三):理论 SNR

SNR(信噪比)= 信号 RMS / 噪声 RMS:

SNRdB=20log⁡10 ⁣(2nQ/(22)Q/(23))=20log⁡10 ⁣(2n32)≈6.02 n+1.76(3) \text{SNRdB} = 20\log_{10}\!\left(\frac{2^n Q / (2\sqrt{2})}{Q/(2\sqrt{3})}\right) = 20\log_{10}\!\left(2^n\sqrt{\tfrac{3}{2}}\right) \approx 6.02\,n + 1.76 \tag{3} SNRdB=20log10(Q/(23 )2nQ/(22 ))=20log10(2n23 )≈6.02n+1.76(3)

这是 ADC/DAC 领域最重要的公式:

SNRdB≈6.02 n+1.76\boxed{\text{SNRdB} \approx 6.02\,n + 1.76}SNRdB≈6.02n+1.76

它定义了一个 nnn 位转换器的理论上限 SNR

工程直觉 :例如一个优秀的 8 位 ADC,其 SNR 大约是 6.02×8+1.76≈49.9 dB6.02\times8 + 1.76 \approx 49.9\,\text{dB}6.02×8+1.76≈49.9dB(约 48--50 dB)。如果你测出来某个 8 位器件的 SNR 超过 50 dB ,那一定是哪里算错了,或者你(用硬件或软件)限制了测量带宽------因为噪声功率正比于带宽,缩小带宽会让噪声看起来变小、SNR 虚高。

推导(四):有效位数 ENOB

反过来,如果你测得了一个器件的实际 SNR,就能反算它的有效位数(ENOB, Effective Number Of Bits)

ENOBbits=SNRdB−1.766.02(4) \text{ENOBbits} = \frac{\text{SNRdB} - 1.76}{6.02} \tag{4} ENOBbits=6.02SNRdB−1.76(4)

ENOB 是衡量转换器实际性能的关键指标------它把"测得的 SNR"翻译成"相当于几位的理想转换器"。

下图给出理论 SNR 随分辨率 nnn 的变化(每多 1 位,SNR 提高约 6 dB):

Python 验证

下面这段 Python 代码用蒙特卡洛方法,亲手验证上面四条公式。它生成一个满量程正弦、对其量化、统计量化噪声 RMS、计算 SNR,并与理论公式对比。

python 复制代码
import numpy as np

def quantize(x, n_bits):
    """把信号量化成 n 位整数码 (Q=1),返回量化后的浮点电平。"""
    levels = 2 ** n_bits
    # 限幅到 [0, levels-1],四舍五入到最近的码
    code = np.clip(np.round(x), 0, levels - 1)
    return code

def simulate_snr(n_bits, N=1 << 16):
    Q = 1.0
    # 满量程正弦:幅度 = 半满量程,居中放置
    amp = (2 ** n_bits) / 2 - 0.5
    offset = (2 ** n_bits - 1) / 2
    # 用非整数周期数避免相干特例(任意取 997 个周期)
    k = 997
    t = np.arange(N)
    x = offset + amp * np.sin(2 * np.pi * k * t / N)

    xq = quantize(x, n_bits)
    err = x - xq                      # 量化误差

    noise_rms  = np.sqrt(np.mean(err ** 2))
    signal_rms = amp / np.sqrt(2)     # 正弦 RMS = 峰值 / sqrt(2)
    snr_db     = 20 * np.log10(signal_rms / noise_rms)
    return noise_rms, snr_db

print("理论量化噪声 RMS = Q/sqrt(12) = %.4f" % (1 / np.sqrt(12)))
for n in [8, 12, 16]:
    nrms, snr = simulate_snr(n)
    snr_theory = 6.02 * n + 1.76
    enob = (snr - 1.76) / 6.02
    print(f"n={n:2d}bit | 噪声RMS={nrms:.4f} | 仿真SNR={snr:5.2f}dB | "
          f"理论SNR={snr_theory:5.2f}dB | 反算ENOB={enob:.2f}")

运行结果(与公式高度吻合):

复制代码
理论量化噪声 RMS = Q/sqrt(12) = 0.2887
n= 8bit | 噪声RMS=0.2856 | 仿真SNR=49.98dB | 理论SNR=49.92dB | 反算ENOB=8.01
n=12bit | 噪声RMS=0.2879 | 仿真SNR=74.03dB | 理论SNR=74.00dB | 反算ENOB=12.00
n=16bit | 噪声RMS=0.2902 | 仿真SNR=98.04dB | 理论SNR=98.08dB | 反算ENOB=15.99

结论 :量化噪声 RMS 确实是 Q/12≈0.2887Q/\sqrt{12}\approx0.2887Q/12 ≈0.2887;满量程正弦的 SNR 确实约为 6.02n+1.766.02n+1.766.02n+1.76;用公式 (4) 反算出的 ENOB 精确回到了原始位数。四条公式全部验证通过。


2. 用 DAC 生成正弦波:最少需要多少个点?

2.1 问题的由来

DAC 是混合信号测试中最典型的 DUT,AWG 又是现代测试机里的模拟信号源(AWG = DAC + 波形存储器)。所以"如何生成一段波形数据"是基本功。

一个线性 nnn 位 DAC 有 2n2^n2n 个输出电平。测一个 DAC 时,你需要激励全部码 (从 0 到 2n−12^n-12n−1)。

  • 如果你简单地从 0 递增到 2n−12^n-12n−1,得到的是一个单调的斜坡(ramp)------这对器件来说是个比较温和、不够严苛的测试。
  • 如果想做更接近真实时钟速度的动态测试 ,可以用正弦波的码去激励 DAC。

那么问题来了:用正弦波激励一个 nnn 位 DAC,最少需要编程多少个数据点 NNN?

2.2 推导:N=2n+1N = 2^{n+1}N=2n+1

设正弦波为(AAA 为幅度,ω=2πf\omega = 2\pi fω=2πf,fff 为频率):

V(t)=Asin⁡(ωt)(5) V(t) = A\sin(\omega t) \tag{5} V(t)=Asin(ωt)(5)

正弦的最大斜率 出现在它最陡的地方,也就是过零点。对 (5) 求导得到 (6),其绝对值的最大值出现在 ωt=kπ\omega t = k\piωt=kπ 处,即 (7):

dVdt=Aωcos⁡(ωt)(6) \frac{dV}{dt} = A\omega\cos(\omega t) \tag{6} dtdV=Aωcos(ωt)(6)

dVdt∣ωt=kπ=Aω(7) \left.\frac{dV}{dt}\right|_{\omega t = k\pi} = A\omega \tag{7} dtdV ωt=kπ=Aω(7)

设 DAC 一个时钟周期为 Δt\Delta tΔt,则在最陡处,DAC 一步要走过的最大电压步进为:

ΔV=Aω⋅Δt(8) \Delta V = A\omega \cdot \Delta t \tag{8} ΔV=Aω⋅Δt(8)

由于正弦是对称的,只需考虑半个周期即可。半个周期跨越满量程 2A2A2A。我们用"半周期内的平均步进"来作为约束。要保证不丢码,最陡处的瞬时步进 (8) 不能超过半周期内应有的平均步进

Aω⋅Δt  ≤  2A2n/2(9,10) A\omega \cdot \Delta t \;\le\; \frac{2A}{2^{n}/2} \tag{9,10} Aω⋅Δt≤2n/22A(9,10)

两边消去 AAA,并代入 ω=2πf\omega = 2\pi fω=2πf 与"一个周期占 NNN 个点"的关系 f=1NΔtf = \dfrac{1}{N\Delta t}f=NΔt1:

ωΔt≤12 n−2  ⇒  2πfΔt≤12 n−2  ⇒  2πN≤12 n−2(11,12,13) \omega\Delta t \le \frac{1}{2^{\,n-2}} \;\Rightarrow\; 2\pi f\Delta t \le \frac{1}{2^{\,n-2}} \;\Rightarrow\; \frac{2\pi}{N} \le \frac{1}{2^{\,n-2}} \tag{11,12,13} ωΔt≤2n−21⇒2πfΔt≤2n−21⇒N2π≤2n−21(11,12,13)

解出 NNN:

N  ≥  π⋅2 n−1  ≈  2 n+1(因为 π=3.14...<4=22)(14,15) N \;\ge\; \pi \cdot 2^{\,n-1} \;\approx\; 2^{\,n+1} \qquad(\text{因为 } \pi = 3.14\ldots < 4 = 2^2) \tag{14,15} N≥π⋅2n−1≈2n+1(因为 π=3.14...<4=22)(14,15)

向上取整到 2 的幂(这样后续做 FFT 很方便),最终结论:

N=2 n+1(16) \boxed{N = 2^{\,n+1}} \tag{16} N=2n+1(16)

一句话记住 :对一个 nnn 位 DAC,要用正弦波激励到全部码,至少需要 2n+12^{n+1}2n+1 个数据点。

例如 4 位 DAC:N=24+1=25=32N = 2^{4+1} = 2^5 = 32N=24+1=25=32 个点。

2.3 实际编程:一个会"缺码"的陷阱

我们用 4 位 DAC(N=32N=32N=32)来验证结论 (16),并故意先踩一个非常经典的坑。

最直观的 Python 写法是:

python 复制代码
import numpy as np

n      = 4
Ndata  = 2 ** (n + 1)      # = 32 点
Ncycle = 1                 # 单周期
dP     = 2 * np.pi * Ncycle / Ndata   # 相位步进
dA     = (2 ** n - 1) / 2  # 幅度 = 满量程/2 = 15/2 = 7.5
dO     = dA                # 直流偏置(把波形抬到正区间)

# 朴素写法:iWave[i] = round(dO + dA*sin(dP*i))
wave0 = [int(dO + dA * np.sin(dP * i) + 0.5) for i in range(Ndata)]
print("产生的码集合:", sorted(set(wave0)))
print("缺失的码    :", sorted(set(range(16)) - set(wave0)))

说明:int(... + 0.5) 是"加 0.5 再取整"的四舍五入写法,用于把浮点电压取整成最近的整数码。

运行结果:

复制代码
产生的码集合: [0, 1, 2, 3, 5, 6, 8, 9, 10, 12, 13, 14, 15]
缺失的码    : [4, 7, 11]

奇怪! 我们明明用了足够的点数(32 个),却有 3 个码(4、7、11)始终产生不出来!难道结论 (16) 错了?

不是公式错,是编程方式的问题。 朴素写法里,正弦在 iii 和某些对称位置上恰好命中了完全相同的电平(因为采样点关于波峰/波谷对称),导致一部分中间码被"跳过"。

解决办法:给采样相位加一个 0.25 的偏移,让采样点稍微错开,不再两两命中同一电平:

python 复制代码
# 修正写法:在采样相位上加 0.25 个采样周期的偏移
wave1 = [int(dO + dA * np.sin(dP * (i + 0.25)) + 0.5) for i in range(Ndata)]
print("修正后码集合:", sorted(set(wave1)))
print("缺失的码    :", sorted(set(range(16)) - set(wave1)))

运行结果:

复制代码
修正后码集合: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
缺失的码    : []

加了 0.25 相位偏移后,全部 16 个码 0--15 都被覆盖。下图直观对比了修正前后:

工程要点 :用 DAC 做"全码"测试时,生成激励码后一定要检查是否真的覆盖了所有可用码。一个简单的相位偏移就能避免"漏码"这种隐蔽错误。


3. DAC 的真实输出波形:零阶保持与 SINC 加权

3.1 零阶保持波形(Zero-Order Hold)

当 DAC 收到一个码时,它输出对应的直流电压,并保持 这个电平直到下一个码到来。因此 DAC 的输出波形理论上是一个阶梯(staircase) ------这种波形叫 零阶保持波形(Zero-Order Hold, ZOH)

3.2 同步数字化(Synchronous Digitizing)

用数字化仪测试 DAC 时,这个阶梯波形其实很有用:如果数字化仪能与 DAC 同步运行 ,并且恰好在每一级台阶稳定的电平上 采样,测试就能非常高效。这种技术适用于相对低速 的 DAC,但要求数字化仪本身的直流(DC)性能远优于被测 DUT

3.3 零阶保持波形的频谱:镜像谱与 SINC 包络

零阶保持波形的频谱不只 包含我们想要的基波(频率 FtF_tFt),还包含大量镜像谱(image spectra)。它们的位置在:

k⋅Fs±Ft,k=1,2,3,4,... k\cdot F_s \pm F_t, \qquad k = 1, 2, 3, 4, \ldots k⋅Fs±Ft,k=1,2,3,4,...

也就是说,在采样频率 FsF_sFs 及其整数倍附近,对称地各出现一对镜像。更关键的是:所有这些谱线(基波和镜像)的幅度都被一条 sin⁡xx\dfrac{\sin x}{x}xsinx 曲线加权 ,这条曲线常被称为 SINC 函数

下图左:DAC 的零阶保持波形(红色阶梯)与理想正弦的对比;右:SINC 包络(频率以 FsF_sFs 为单位),可以看到在 Fs/2F_s/2Fs/2 处衰减约 −3.92 dB-3.92\,\text{dB}−3.92dB。

3.4 平滑滤波器与 AWG 结构

在 DAC 的实际工作模式中,阶梯波形需要用一个**低通滤波器(LPF)**来平滑:LPF 抑制掉那些不需要的镜像谱,DAC 的输出就变得干净、平滑了。因此这个 LPF 被称为 平滑滤波器(Smoothing filter)

在现代 DSP 测试机里,模拟激励由 AWG(任意波形发生器) 产生。一个典型 AWG 的结构是:波形存储器 → DAC → 平滑滤波器 → 输出 。AWG 通常集成了多个 不同截止频率的平滑滤波器,你可以根据采样率和测试信号频率选最合适的那个。如果 AWG 里没有合适的平滑滤波器,你可能需要在 DUT 板(DUT board) 上自己加一个滤波器。

3.5 多音信号与 SINC 加权

**多音信号(Multi-tone signal)**是测试频率响应(如滤波器测试)时非常有用的激励。例如把四个等幅音 Fa,Fb,Fc,FdF_a, F_b, F_c, F_dFa,Fb,Fc,Fd 编程进 DAC,由于 DAC 原始输出是零阶保持波形,会在 Fs/2F_s/2Fs/2 到 FsF_sFs 之间对称地出现它们的镜像 Fd′,Fc′,Fb′,Fa′F_d', F_c', F_b', F_a'Fd′,Fc′,Fb′,Fa′。

重点 :即使你生成的是"等幅"多音,经过 DAC 后,从 FaF_aFa 到 Fa′F_a'Fa′ 的所有谱线幅度都会被 SINC 函数加权 ------频率越高,被压得越低。SINC 在 Fs/2F_s/2Fs/2 处的加权值为:

sin⁡(π/2)π/2=2π≈0.6366⟹20log⁡10(0.6366)≈−3.92 dB \frac{\sin(\pi/2)}{\pi/2} = \frac{2}{\pi} \approx 0.6366 \quad\Longrightarrow\quad 20\log_{10}(0.6366) \approx -3.92\,\text{dB} π/2sin(π/2)=π2≈0.6366⟹20log10(0.6366)≈−3.92dB

如果你不满意这种 SINC 造成的电平倾斜,可以在生成原始波形时预先施加反 SINC(reverse SINC)补偿,把高频音预先抬高,抵消 SINC 的衰减。

Python 验证:SINC 加权与零阶保持频谱
python 复制代码
import numpy as np

# 1) 验证 SINC 在 Fs/2 处的加权值
v = np.sin(np.pi / 2) / (np.pi / 2)
print(f"SINC@Fs/2 = {v:.4f} = {20*np.log10(v):.2f} dB")

# 2) 直接构造一个零阶保持波形并看它的频谱镜像
Ft    = 3                  # 基波:在 Ndata 个采样点内放 3 个完整周期
Ndata = 256                # 原始采样点数(采样率 Fs 归一化为 1)
samples = np.sin(2 * np.pi * Ft * np.arange(Ndata) / Ndata)

# 用过采样模拟"保持"效果:每个样点重复 R 次 => 零阶保持
R = 16
zoh = np.repeat(samples, R)
spec = np.abs(np.fft.rfft(zoh))
spec /= spec.max()
freqs = np.fft.rfftfreq(len(zoh)) * R   # 归一化到原 Fs 为 1
peak_freqs = np.sort(freqs[np.argsort(spec)[-6:]])     # 取最强的 6 条谱线
print("频谱峰值出现在 f/Fs ≈", np.round(peak_freqs, 3))  # 基波 + 多个镜像

运行结果:

复制代码
SINC@Fs/2 = 0.6366 = -3.92 dB
频谱峰值出现在 f/Fs ≈ [0.012 0.988 1.012 1.988 2.012 2.988]

这些峰值正好对应:基波 Ft/Fs≈0.012F_t/F_s \approx 0.012Ft/Fs≈0.012,以及 Fs−Ft≈0.988F_s-F_t\approx0.988Fs−Ft≈0.988、Fs+Ft≈1.012F_s+F_t\approx1.012Fs+Ft≈1.012、2Fs−Ft≈1.9882F_s-F_t\approx1.9882Fs−Ft≈1.988 ...... 即一系列 kFs±Ftk F_s \pm F_tkFs±Ft 的镜像谱。

结论 :SINC 在 Fs/2F_s/2Fs/2 处的加权确实 ≈−3.92 dB\approx -3.92\,\text{dB}≈−3.92dB;零阶保持波形的频谱确实在 kFs±FtkF_s\pm F_tkFs±Ft 处出现镜像谱。


4. 多音信号的三种生成方法

多音信号是混合信号测试中很流行的激励:用一次测量就能在多个频点上同时获得增益和相位信息,做频率响应分析非常高效。

但"怎么把多个音叠加起来"大有讲究------关键在于每个音的相位怎么安排。本节用 100 个音、1024 个数据点为例,讲三种方法。

下面用 Python 实现这三种方法,并用 FFT 把每种方法的时域波形、频谱、峰均比(PRR)都验证一遍。

先准备一个通用的"构造 + 缩放"函数

python 复制代码
import numpy as np

def build_multitone(Ntones, Ndata, phase_mode, seed=1):
    """
    phase_mode: 'zero' 全零相位 / 'rand' 随机相位 / 'para' 抛物线相位
    返回峰值缩放到 0.5(半满量程)的多音波形。
    """
    rng = np.random.default_rng(seed)
    i = np.arange(Ndata)
    wave = np.zeros(Ndata)
    for j in range(Ntones):
        Ncycle = j + 1                       # 第 j 个音占 Ncycle 个周期
        dP = 2 * np.pi * Ncycle / Ndata      # 频率(相位步进)
        if   phase_mode == 'zero': dQ = 0.0
        elif phase_mode == 'rand': dQ = 2 * np.pi * rng.random()           # 随机相位
        elif phase_mode == 'para': dQ = np.pi / Ntones * (1 - Ncycle**2)   # 抛物线相位
        wave += np.sin(dP * i + dQ)
    # 把峰值缩放到 0.5(半满量程)
    wave *= 0.5 / np.max(np.abs(wave))
    return wave

def peak_to_rms_db(wave):
    """峰均比 PRR(dB):峰值 / RMS。"""
    return 20 * np.log10(np.max(np.abs(wave)) / np.sqrt(np.mean(wave**2)))

def tone_level_dbfs(wave, Ntones, Ndata):
    """各音平均电平(相对 0.5=半满量程,dBFS)。"""
    spec = np.abs(np.fft.rfft(wave) / Ndata * 2)
    return 20 * np.log10(np.mean(spec[1:Ntones+1]) / 0.5)

4.1 方法一:原始法(全零相位)

最朴素的想法:把所有单音直接相加,且所有正弦都从相位 0 开始

python 复制代码
w_zero = build_multitone(100, 1024, 'zero')
print("原始法   PRR = %.1f dB,  每音电平 = %.1f dBFS"
      % (peak_to_rms_db(w_zero), tone_level_dbfs(w_zero, 100, 1024)))

结果:

复制代码
原始法   PRR = 20.2 dB,  每音电平 = -37.2 dBFS

问题 :所有音都从 0 相位起步,会在某一点同时达到峰值,叠加成一个类似冲激(impulse)的极端波形(见下图最上一行)。它的峰均比(PRR, Peak-to-RMS Ratio)高达约 20 dB 。虽然频谱上确实是平坦的 100 个音、每音约 −37.1 dBFS-37.1\,\text{dBFS}−37.1dBFS,但这种"尖刺"波形把能量都堆在一个瞬间,动态范围很差,不适合常规测试

4.2 方法二:随机相位(Good Practice)

问题出在相位控制上。解决思路:给每个频率分量插入一个随机相位偏移,把能量在时间上摊开。

python 复制代码
w_rand = build_multitone(100, 1024, 'rand')
print("随机相位 PRR = %.1f dB,  每音电平 = %.1f dBFS"
      % (peak_to_rms_db(w_rand), tone_level_dbfs(w_rand, 100, 1024)))

结果:

复制代码
随机相位 PRR = 10.5 dB,  每音电平 = -27.5 dBFS

加了随机相位后,波形看起来像噪声 ,激活了整个码范围(见下图中间一行)。相比全零相位,频谱电平提升了约 9 dB (每音从约 −37-37−37 抬到约 −28 dBFS-28\,\text{dBFS}−28dBFS),PRR 降到约 10--15 dB

这种信号不仅适合频率响应测试,还很适合宽带信号------现代通信常用宽带信号,相位随机化的多音是真实调制信号的一个很好的替代品。

4.3 方法三:抛物线相位(Good Practice,针对滤波器测试)

随机相位波形像噪声,可用于频响测试(如滤波器特性测试,关注通带平坦度和阻带损耗)。但还能更好。

动态范围分析 :假设测量仪器是 16 位,其交流动态范围为 ±32768\pm 32768±32768,最小信号电平 =20log⁡10(1/32768)≈−90 dBFS= 20\log_{10}(1/32768) \approx -90\,\text{dBFS}=20log10(1/32768)≈−90dBFS。如果每个音只有 −28 dBFS-28\,\text{dBFS}−28dBFS,那么每个音可用的动态范围只剩 90−28=62 dB90 - 28 = 62\,\text{dB}90−28=62dB------对某些滤波器特性测试可能不够。

我们希望在有限满量程内,尽量把能量平均分配给每个音,以获得最大动态范围。这时**抛物线相位(Parabolic phase)**很有效------它用一个数学确定的二次函数来控制每个音的相位偏移:

θj=πNtones(1−Ncycle2) \theta_j = \frac{\pi}{N_{tones}}\left(1 - N_{cycle}^2\right) θj=Ntonesπ(1−Ncycle2)

python 复制代码
w_para = build_multitone(100, 1024, 'para')
print("抛物线   PRR = %.1f dB,  每音电平 = %.1f dBFS"
      % (peak_to_rms_db(w_para), tone_level_dbfs(w_para, 100, 1024)))

结果:

复制代码
抛物线   PRR = 5.6 dB,  每音电平 = -22.6 dBFS

抛物线相位的波形看起来像一段扫频(swept)信号 (见下图最下一行),因此不太适合 作通信类测试的替代信号;但每个音的电平比随机相位又高出约 5.6 dB ,可以测试阻带抑制高达约 67.6 dB67.6\,\text{dB}67.6dB 的滤波器。它非常适合宽带器件的频率响应测试

三种方法可视化对比

下图为三种方法的时域波形(左)与频谱(右):从上到下分别是原始法(冲激状)、随机相位(噪声状)、抛物线相位(扫频状)。三者频谱都是平坦的 100 个音,但时域峰均比(PRR)依次从 ~20 dB 降到 ~10 dB 再降到 ~5.6 dB------PRR 越低,同样满量程下每个音能分到的能量就越多。

一个重要的工程取舍

DAC / AWG 的满量程和分辨率都是有限的。当多音编程进去后,信号的峰值会被对齐到满量程。因此:

音的数量越多,每个音能承载的电平 / 功率就越低,信号的动态范围也越小。所以需要折中。

这就是为什么"降低 PRR"如此重要------它直接决定了同样满量程下每个音能用到多少动态范围。


5. 全教程小结与速查表

5.1 核心结论速查

章节 核心知识点 必记结论
1 采样定理 Fs>2FmaxF_s > 2F_{max}Fs>2Fmax,否则混叠;Fs/2F_s/2Fs/2 = 奈奎斯特频率
1 量化噪声 RMS=Q/12\text{RMS} = Q/\sqrt{12}RMS=Q/12
1 转换器 SNR SNRdB≈6.02n+1.76\text{SNRdB} \approx 6.02n + 1.76SNRdB≈6.02n+1.76
1 有效位数 ENOB=(SNR−1.76)/6.02\text{ENOB} = (\text{SNR}-1.76)/6.02ENOB=(SNR−1.76)/6.02
2 DAC 正弦最少点数 N=2n+1N = 2^{n+1}N=2n+1;注意加 0.25 相位偏移防缺码
3 DAC 输出 零阶保持 → 镜像谱在 kFs±FtkF_s\pm F_tkFs±Ft,被 SINC 加权
3 SINC@Fs/2F_s/2Fs/2 ≈0.6366=−3.92 dB\approx 0.6366 = -3.92\,\text{dB}≈0.6366=−3.92dB,需平滑滤波器
4 多音生成 全零→随机(+9dB)→抛物线(再+5.6dB);用 PRR 衡量优劣

5.2 关键术语对照(中 / 英)

  • 采样定理 --- Sampling Theorem;混叠 --- Aliasing;奈奎斯特频率 --- Nyquist frequency
  • 抗混叠滤波器 --- Anti-aliasing filter;欠采样 --- Under-sampling
  • 量化 --- Quantization;量化噪声 --- Quantization noise;有效位数 --- ENOB
  • 零阶保持 --- Zero-Order Hold;镜像谱 --- Image spectra;平滑滤波器 --- Smoothing filter
  • 任意波形发生器 --- AWG;数字化仪 --- Digitizer;采样器 --- Sampler
  • 多音 --- Multi-tone;峰均比 --- PRR (Peak-to-RMS Ratio);满量程 --- Full-scale (dBFS)

5.3 给初级工程师的三条提醒

  1. 测 8 位 ADC 却测出 >50 dB 的 SNR? 先怀疑自己------多半是带宽被限制了,或公式用错了(理论上限就是 6.02×8+1.76≈50 dB6.02\times8+1.76\approx50\,\text{dB}6.02×8+1.76≈50dB)。
  2. 做 DAC 全码测试时,永远检查"是否真的覆盖了所有码",一个相位偏移就能避开缺码陷阱。
  3. 生成多音激励时,先想清楚目的:要像真实通信信号 → 用随机相位;要最大动态范围测滤波器 → 用抛物线相位。

5.4 如何运行本教程的全部验证

把本教程中的每段 Python 代码复制到一个 .py 文件,或在 Jupyter Notebook 中逐段运行即可。统一依赖:

bash 复制代码
pip install numpy matplotlib scipy

所有结论都可以在你自己的电脑上、不依赖任何测试机 地复现出来。这正是 DSP-Based Testing 的精髓:信号是算出来的,结论也是能算出来、能验证的。

相关推荐
Meraki.Zhang13 天前
【ADC 测试技术】:3. DFT/FFT 频谱分析原理
芯片测试
Meraki.Zhang14 天前
【芯片测试】:6. 向量、Sequencer 指令与高速串行 IO
芯片测试
Meraki.Zhang15 天前
【芯片测试】:3. DUT Board Description 与 Measurement Specification 详解
芯片测试
Meraki.Zhang15 天前
【芯片测试】:SmarTest 开发环境入门
芯片测试
Meraki.Zhang15 天前
【芯片测试】:Driver、Comparator、PMU 与 Active Load
芯片测试
Meraki.Zhang16 天前
【芯片测试】:什么是 VCDSTIL?
芯片测试
Meraki.Zhang16 天前
【芯片测试】:基于时钟的时序提取
芯片测试
黑猫学长呀20 天前
存储宝典第6篇:测试机台的PE板和PPB板有什么区别?
测试工具·fpga开发·ssd·芯片测试·ate·存储芯片·测试机台
Meraki.Zhang21 天前
【芯片测试】:SmarTest 8 Software Overview
芯片测试