首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >使用ADC欠采样直采与使用混频器进行频谱搬移后采样,怎么衡量最终的SNDR?

使用ADC欠采样直采与使用混频器进行频谱搬移后采样,怎么衡量最终的SNDR?

作者头像
云深无际
发布2025-05-25 14:19:46
发布2025-05-25 14:19:46
35400
代码可运行
举报
文章被收录于专栏:云深之无迹云深之无迹
运行总次数:0
代码可运行

也是昨天的群友问的,今天写第二篇。

首先这个问题涉及 欠采样(undersampling)混频采样(heterodyne sampling,或下变频) 两种不同的频谱搬移策略,但最终的 SNDR(Signal-to-Noise and Distortion Ratio) 衡量方式有共通也有差异之处。

SNDR 是什么?

信噪失真比(也称为 SINAD)指输入正弦波时,RMS 信号功率与 (a) 总噪声功率和 (b) 输出端(不含 DC)的所有其他频率分量功率加上所有其他谐波分量功率的 RMS 和的比值。用于衡量数据转换器的动态性能的关键参数之一,因为 SNDR 包含奈奎斯特带宽上的所有噪声和杂散。

也说明的是输入信号的质量;SNDR 越大,输入功率中的噪声和杂散比率越小;我理解SNDR 就是衡量一个信号“干净不干净”的指标。

通俗来说这个参数可以告诉你:在你想要的信号旁边,有没有太多杂七杂八的噪声或奇怪的东西(比如谐波、干扰);

表达式是:SNDR = 信号功率 ÷ (噪声 + 失真功率)

通常在 FFT 分析中:

看公式知道信号的能量越大、杂音和失真越小,SNDR 就越高。

衡量标准是看信号在目标频段(通常是基带)中,相对于其他杂散成分的清晰度。


两种采样方式简述

测一个高频信号,但 ADC(模拟转数字转换器)“听不到”那么高的声音(频率太高了),所以有两个方法采集:

1. ADC 欠采样(Undersampling)

也称 带通采样(Bandpass Sampling),直接用低采样率 ADC 采样高频信号,但要求信号本身是带通信号;频谱会“折叠”进入 [0, Fs/2] 范围,对时钟抖动和 ADC 带宽要求高,这里补充一点,欠采样是和时钟抖动也有关系的。

ADC 时钟抖动对欠采样的严重影响

ADC 的 SNR 受采样时钟抖动(jitter)影响:

假设:抖动为 10ps(很优秀了),输入信号频率 f = 40 kHz vs 5 kHz

计算:

40 kHz:SNR ≈ 105 dB

5 kHz:SNR ≈ 119 dB

大概可以这样说越高频,越容易因为抖动造成 SNR 损失。

接上文,也就是把 ADC 的采样速度故意设得低一点;利用数学特性,让高频信号“折叠”下来,变成低频信号。

比如听见一台发动机的嗡嗡声,其实它是高速运转的,但你听到的是一个比较低的频率。容易混进别的声音(其他频率的信号也折叠过来)。

2. 混频器下变频 + 采样

使用模拟混频器将 RF 信号混频(与 LO 相乘)后变到基带或中频(IF),再用 ADC 采样;适用于宽带或多个信号搬移,需要 LO 和滤波器。

就像你把一个超高音的乐器录音,先用一个转换器把它的音调调低,再用普通麦克风录下来。这个转换器就是“混频器”。它把信号“下变频”到了 ADC 能处理的范围里。

可以看到两种采样方式:都在做“频率搬家”

比如听一场音乐会:

  1. 欠采样就像用一个便宜的录音笔在远处听——有时听得见高音,但会混进很多杂音。
  2. 混频器就像是给每种乐器都配了一个调音器,先调到你能听的音调再录,听起来更清楚。

最后你拿录音文件来评分,不管你是怎么录的,只看最终录到的声音有没有杂音和失真——这就是 SNDR 的评估。

所以无论哪种方式,最终的 SNDR 都是对进入 ADC 后的频谱结果进行如下处理

  1. 采集 ADC 输出的数据
  2. 对采样结果进行 FFT
  3. 找出目标信号分量的主峰(通常为最大 bin)
  4. 在整个带宽范围(或指定带宽)内计算总噪声 + 失真功率 如失真分量包括谐波(HD2、HD3)、杂散(IMD、spur)等
  5. 计算 SNDR

评估项

欠采样(Undersampling)

混频器搬移(Heterodyne)

最终 SNDR 测量方式

在基带频谱窗口内分析(FFT 取峰值与噪声+谐波)

同样在基带窗口分析

关键影响因素

- 采样时钟抖动(直接影响 SNR)- ADC 采样带宽(需能采样原始频率分量)- 频谱折叠失真

- 混频器非线性失真(IM3、LO 泄漏)- LO 相位噪声- 中频滤波器品质

是否要频谱搬移回原频点

否,直接分析折叠后的频谱

否,变频后分析

带宽设定

通常设定带通信号带宽

通常设定中频通带范围

校准策略

对时钟抖动 / 带宽效应建模

对混频器非线性和杂散分量做建模

我这里仿真以前的模型有问题,但是出来结果还是挺好看的(

要处理的是 40 kHz 的窄带信号):

混频 + 滤波 + 正常采样(比如 100 kSps 或更高)

代码语言:javascript
代码运行次数:0
运行
复制
原信号(40kHz) × LO(35kHz) →→ 混频后产生 5kHz(目标)+ 75kHz(镜像)→ 滤波器抑制 75kHz 分量(-30 dB)→ ADC 正常采样(比如 100kHz)

直接降采样(欠采样)

代码语言:javascript
代码运行次数:0
运行
复制
原信号(40kHz) →→ 直接用 35kSps ADC 采样(欠采样)→ 频谱折叠 → 5kHz 落入基带
image-20250523182828606
image-20250523182828606

结果

方案

SNDR(模拟值)

说明

混频方案(频率搬移+滤波后采样)

约 37.7 dB

目标频率集中,失真和噪声有效抑制

欠采样方案(35kSps 直采 40kHz)

约 23.9 dB

虽然目标频率落在基带,但混叠导致失真更大

总结一下:

应用情况

推荐方案

对 SNDR 要求高(>90 dB)

混频方案,可更稳定控制频谱和失真

对系统复杂度要求低,功耗和硬件极简

欠采样方案可行,但要小心混叠和带外干扰

只处理一个窄带信号,带外无干扰

欠采样也能达到较好效果(注意前级滤波)

可以容忍一点复杂度换更高性能

混频 + 滤波后采样是更稳妥选择

代码语言:javascript
代码运行次数:0
运行
复制
import numpy as np
import matplotlib.pyplot as plt
from scipy.signal import butter, lfilter, fftconvolve
from numpy.fft import fft, fftfreq

# 设置参数

fs_high = 100_000   # 高采样率用于混频方案
fs_low = 35_000     # 欠采样率
f_signal = 40_000   # 原始信号频率
f_lo = 35_000       # 混频本振频率

duration = 0.01  # 10ms 信号持续时间
t_high = np.arange(0, duration, 1/fs_high)
t_low = np.arange(0, duration, 1/fs_low)

# 生成信号:40kHz 正弦波 + 微小失真项 + 噪声

signal = np.sin(2 * np.pi * f_signal * t_high)
signal += 0.01 * np.sin(2 * np.pi * 2 * f_signal * t_high)  # 二次谐波
signal += 0.005 * np.random.randn(len(t_high))  # 噪声

# 混频方案

lo = np.sin(2 * np.pi * f_lo * t_high)
mixed_signal = signal * lo  # 乘法混频

# 滤波器设计:带通滤波器,通带5kHz ± 2.5kHz

def bandpass_filter(data, fs, lowcut, highcut, order=5):
    nyq = 0.5 * fs
    b, a = butter(order, [lowcut/nyq, highcut/nyq], btype='band')
    return lfilter(b, a, data)

filtered_mixed = bandpass_filter(mixed_signal, fs_high, 2.5e3, 7.5e3)

# 重新采样(模拟ADC采样)

def downsample(signal, orig_fs, target_fs):
    factor = int(orig_fs / target_fs)
    return signal[::factor]

mixed_sampled = downsample(filtered_mixed, fs_high, fs_low)

# 欠采样方案(直接35k采样原始40k信号)

signal_alias = np.sin(2 * np.pi * f_signal * t_low)
signal_alias += 0.01 * np.sin(2 * np.pi * 2 * f_signal * t_low)
signal_alias += 0.005 * np.random.randn(len(t_low))

# 计算 SNDR

def compute_sndr(signal, fs):
    N = len(signal)
    fft_vals = np.abs(fft(signal))[:N//2]
    freqs = fftfreq(N, d=1/fs)[:N//2]

    peak_idx = np.argmax(fft_vals)
    signal_power = fft_vals[peak_idx] ** 2
    total_power = np.sum(fft_vals ** 2)
    noise_dist_power = total_power - signal_power
    sndr = 10 * np.log10(signal_power / noise_dist_power)
    return sndr, freqs, fft_vals

sndr_mixed, freq_mixed, fft_mixed = compute_sndr(mixed_sampled, fs_low)
sndr_alias, freq_alias, fft_alias = compute_sndr(signal_alias, fs_low)

# 画图

plt.figure()
plt.plot(freq_mixed, 20*np.log10(fft_mixed), label=f'Mixed Sampling SNDR={sndr_mixed:.2f} dB')
plt.plot(freq_alias, 20*np.log10(fft_alias), label=f'Undersampling SNDR={sndr_alias:.2f} dB')
plt.title("FFT Comparison of Mixed Sampling vs Undersampling")
plt.xlabel("Frequency (Hz)")
plt.ylabel("Magnitude (dB)")
plt.grid(True)
plt.legend()
plt.tight_layout()
plt.show()

参考文献?没有,我就是自己的参考文献(😂)

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2025-05-23,如有侵权请联系 cloudcommunity@tencent.com 删除

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

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

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

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • SNDR 是什么?
  • 两种采样方式简述
    • 1. ADC 欠采样(Undersampling)
  • ADC 时钟抖动对欠采样的严重影响
    • 2. 混频器下变频 + 采样
    • 混频 + 滤波 + 正常采样(比如 100 kSps 或更高)
    • 直接降采样(欠采样)
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档