SAR ADC(逐次逼近型模数转换器)以其低功耗、结构简单、采样延迟短等优点在很多应用中广泛使用,尤其是在需要中等采样率(~几十kHz到几MHz)且低延迟的场合。然而,它对输入信号带宽内的噪声频谱的处理能力有其自身的特性与局限,本篇文章就试图使用理论和代码模拟的方法说明其中的细节。
文章的起因是因为之前写LTC2005,下面一位ADI的大哥提出的问题,这段日子研究了下,可以做个回答。
另外我发现ADI有个岗位叫PAE是:支持现场应用工程师(FAE)和远程应用团队,协助解决技术难题,提出产品/应用改进建议,好像也会负责FAE和内部研发之间的工作,就是做更深层次的支持,我们平时看见的文章就是这些写出来的,这里模拟一下写一篇看看(😂)。
ADI 官方书籍:Analog-Digital Conversion 3.125是今天的图。
先说结论:
SAR ADC 对带内噪声频谱的处理特性总结:实时性好,适合快速采样 , 无内建带内噪声抑制或滤波能力,可外加模拟滤波器抑制带外噪声, 对低频噪声(如 1/f 噪声)不敏感 ,可通过平均提升带内噪声鲁棒性。
SAR ADC 的采样和噪声频谱处理机制
1. 采样-保持机制导致等效带宽有限
SAR ADC 通过采样保持电路(S/H)对输入模拟信号进行瞬时采样,该过程类似于一个 带宽有限的开关采样网络。其行为可以理解为一个周期性抽样+带宽限制系统,具体有以下影响:
|
|
|
|---|---|
| 采样频率 fs |
|
| 采样瞬间 |
|
| 无抗混叠能力 |
|
| 采样时间有限 |
|
带宽内噪声处理能力对比
|
|
|
|
|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
对带内噪声的影响
因为采样瞬间的噪声会直接被转换进去,无法被平均或抑制,这对带宽内存在的 1/f 噪声、热噪声是个问题。也没有类似 Σ-Δ 架构中的噪声整形器或数字滤波器来处理带外噪声。
如果系统中带内噪声对性能影响较大,而实时性要求又不是特别极限,可以考虑以下两种方案:
SAR + 前置模拟低通滤波器 + 数字平均处理(也是后文中重点要使用的方法)
若允许延迟,改用 Σ-Δ ADC 或混合架构(如 LTC2500、MAX11905 等)
全差分输入 + 高精度 20-bit;高速 SAR 架构,无 pipeline 延迟
SAR ADC 的量化噪声建模
1.1 理想 ADC 的量化误差
假设 SAR ADC 有 位分辨率,满量程电压范围为 ,则其量化步长为:
理想情况下,量化误差服从均匀分布
则其均方根(RMS)为:
1.2 量化噪声功率谱密度(PSD)
若采样频率为 ,量化噪声在 0 到 范围均匀分布,则其功率谱密度为白噪声:
五、带宽内噪声积分与 SNR 计算
设系统信号带宽为 ,我们希望知道在这个频段内的量化噪声功率:
而满量程信号功率为:
因此,带宽内 SNR(信噪比)为:
代入 ,得到:
结论 1:
SAR ADC 的 SNR(在信号带宽内)依赖于过采样比。SNR 可以通过提高采样率来增强(即“过采样增益”):
其中 OSR 为 Over Sampling Ratio,
带宽内总噪声与热噪声、1/f 噪声的叠加
实际模拟前端存在三种噪声:
-
热噪声:白噪声,PSD 为常数 -
1/f 噪声:低频增强型,PSD 形式为 , -
量化噪声:如前所述,带宽内为平坦噪声
带内噪声总功率为(信号带宽为 ):
热噪声噪声量化噪声
可以看到:
SAR ADC 无法区分/抑制上述噪声;
积分型 ADC( Σ-Δ)可以通过噪声整形将低频噪声“削弱”。
SAR和 Σ-Δ ADC 的频谱比较(数学对比)
|
|
|
|
|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
结论(数学角度)
|
|
|
|
|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
很自然的,我们想加滤波器来改善这一现象,在 SAR ADC 前端加入一个 RC 低通滤波器(抗混叠滤波器) 后,它将如何影响输入信号的噪声功率谱密度以及总噪声功率(积分值)?下面的部分将会给出答案。
系统建模:RC 滤波器 + SAR ADC
设输入为某模拟信号 与其叠加噪声 ,系统结构如下:
噪声源 + 信号源 ──> RC 滤波器 ──> S/H + SAR ADC
其中 RC 滤波器为一阶低通:
电阻 R、 电容 C,截止频率(–3 dB)为:
其幅频响应为:
输入噪声 PSD 及其通过滤波器后的 PSD
2.1 白噪声输入 :
这是热噪声模型的常见近似,如运算放大器输出、采样保持器输入电流热噪声等。
2.2 RC 滤波器输出 PSD:
滤波器将输入的 PSD 乘以传递函数的幅值平方:
这个谱型就是一个Lorentzian 分布(洛伦兹谱),在频率域中呈钟形衰减。
噪声功率积分(总噪声)
总噪声功率为 PSD 的积分值,即
做变量代换:
积分上限变为 ,则:
总结:RC 滤波器后的总噪声功率为
特殊情况分析:
**当 **(常见情况):
这说明 RC 滤波器将白噪声带宽从无限降低为一个有限带宽 的过程,从而使噪声功率不再随带宽无限上升。
结合 ADC 采样过程
一旦经过 RC 滤波,ADC 的采样将等效于在 PSD 上施加一个周期抽样窗。带外频率(> fs/2)的信号会混叠进来,因此:
RC 滤波器同时也充当了 抗混叠滤波器(Anti-Aliasing Filter),若滤波器的截止频率 ,则混叠噪声将大幅减少。
对于 1/f 噪声源
如果输入噪声为 ,则:
这个积分可用换元积分解成两个区间,近似为:
-
低频区域()近似为 ,呈对数增长 -
高频区域()近似为 ,呈快速衰减
最终噪声功率变为:常数 → 滤波器可显著压缩 1/f 噪声高频拖尾
总结一下以上的计算结果
|
|
|
|
|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
在前面数学推导的基础上,我们从实际工程设计角度出发,再讨论一下:
SAR ADC 前端 RC 滤波器的设计建议,特别是:如何根据采样频率 合理选择 RC 常数;同时还要兼顾 抗混叠、噪声抑制、信号保真度 三个指标。
RC 滤波
RC 一阶低通滤波器的截止频率为:
它会决定信号和噪声的通过范围,因此需要针对抗混叠 + 保信号进行平衡设计。
设计目标权衡
|
|
|
|
|---|---|---|
| 抗混叠能力 |
|
|
| 信号保真度 |
|
|
| 噪声抑制能力 |
|
|
| 驱动能力匹配 |
|
|
这里使用一个经验设计流程,假设系统信号最大频率为 ,ADC 采样频率为 。
推荐设计公式:
然后用截止频率计算 RC 值:
对于抗混叠(aliasing):
由于奈奎斯特采样理论要求:
推荐设置为:
这是为了保证 ADC 带宽内噪声主要落在 0 ~ ,带外快速衰减。
结合起来,推荐区间为:
以 100kHz 采样为例
|
|
|
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
若选 ,则:
在最后的设计阶段需要选用无源器件搭建滤波器,需要考虑热噪音和功耗以及诸多非线形的问题,比如带宽等。
|
|
|
|---|---|
| R |
|
| C |
|
| f_c | |
| 带宽裕量 |
|
| 实际测试 |
|
若信号中存在突变或高频边缘(如步进信号),不要让 RC 滤波器严重削弱高频响应;
若 ADC 前有放大器,考虑运放带宽对 RC 滤波器的影响,避免形成非理想零极点;
若 ADC 输入电容较大(如 10pF~100pF),实际滤波器可能为 **R + (C + C_ADC)**,应计入总电容。
在这里我绘制了一阶 RC 滤波器在不同输入下(信号 + 噪声)的 频率响应曲线(Bode 图)以及 噪声功率谱密度(PSD)变化,来直观理解滤波器对信号和噪声的作用。
第一张图(上图):RC 滤波器的幅频响应曲线,显示信号在不同频率上的衰减。
第二张图(下图):虚线是原始白噪声 PSD,频率范围内为常数;实线是经过 RC 滤波器后的 PSD,截止频率之后逐渐衰减;可视化带内与带外噪声的区别。
继续仿真,模拟带有 1/f 噪声的输入信号通过 RC 滤波器之后,噪声功率谱密度(PSD)的变化。
1/f 噪声建模
1/f 噪声,也称作粉红噪声(Pink Noise),其功率谱密度满足:
我们令 ,即:
在频率较高时,这种噪声会逐渐减弱,但其总能量积分发散(必须设置下限)。
图中
原始 1/f 噪声曲线呈 –10 dB/decade(即 )下降;
滤波器起作用后,截止频率附近拐弯变陡峭,高频部分被抑制;
在频率远高于 时,滤波器衰减趋势为 ,因为:
RC 滤波器对低频 1/f 噪声无能为力;但能显著抑制1/f 噪声的高频拖尾成分。
我们从功率谱就是能量的角度再来看看这个问题:
图展示了1/f 噪声经过 RC 滤波器后的 PSD 变化,以及标出了:
信号通带区域(0–5 kHz):目标有用信号频率;
抽取后系统的奈奎斯特带宽(0–6.2 kHz):对应过采样比 OSR = 8 的情形;
滤波器截止频率(2.5 kHz) 和采样频率的一半(50 kHz);
为了直观计算了不同频率范围下的噪声功率(单位为 V²),通过频谱积分得到:
|
|
|
|
|---|---|---|
|
|
|
0.000851 |
|
|
|
0.000552 |
|
|
|
0.000541 |
|
|
|
0.000545 |
RC 滤波器显著削减了高频的 1/f 拖尾,整体噪声下降约 35%;信号通带内已包含大部分噪声能量(541 µV² vs 552 µV²);适当的抽取(如 OSR=8)不会引入过多额外噪声,因高频部分已经被滤波抑制;如果没有 RC 滤波器,抽取过程将混叠大量高频 1/f 噪声进低频 → 性能显著下降;这个问题也就是文章核心想说明的问题。
接下来分析:混合 1/f + 白噪声建模; 加入二阶滤波器对比; 以及采样/量化模拟后的 ENOB 估算:
图展示了混合 1/f 噪声 + 白噪声源通过不同滤波器(1阶 RC vs 2阶 Butterworth)后的 PSD 曲线比较。可以看到:
原始 PSD(黑色虚线)由 与常值白噪声叠加构成;
两种滤波器都在截止频率 kHz 处开始滚降;
2阶滤波器对带外高频噪声抑制明显强于 1阶,下降更陡峭(-40 dB/dec);
还是可以看到滤波器对低频 噪声无抑制能力,但能压制高频拖尾成分,特别有利于防止混叠。
ENOB 和噪声性能对比
|
|
|
|
|
|---|---|---|---|
|
|
|
|
2.29 bits |
|
|
|
|
2.56 bits |
-
混合噪声源下的 ENOB 由噪声积分主导,而非量化误差主导; -
提高滤波器阶数可提升 SNR 和 ENOB,特别是在高噪声场景; -
二阶滤波器带来约 1.4 dB SNR 提升,ENOB 提升约 0.27 bit,在精度敏感系统中是显著改进; -
带宽设置与抽取策略配合抗混叠滤波器至关重要,防止高频 1/f 拖尾噪声混入有效信号频带。
可以在后面加一个数字滤波器,比如基于采样率 kHz、过采样比 OSR = 8 设计的**FIR 抽取滤波器(Decimation Filter):
-
滤波器类型:FIR 低通
-
阶数(numtaps):64
-
截止频率:6.25 kHz(即抽取后系统的 Nyquist 频率)
-
采样率抽取:原始 100 kHz → 12.5 kHz
显示 FIR 滤波器具有 良好的通带平坦性和陡峭的带外衰减;红线标注为抽取后系统的新奈奎斯特频率(6.25 kHz)。可有效防止混叠的高频分量进入信号带内。
蓝色:原始过采样信号(带有高频噪声);
橙色:经过 FIR 滤波 + 8 倍抽取之后的信号;
明显看出:高频成分被平滑滤除,信号更清洁,适合后续处理或存储
也可以看到滤波器的第一个若干系数(总共 64 项)已列在表中,可直接用于硬件或 MCU 实现:
h[n] ≈ [-0.00016, -0.00048, -0.00080, ..., +0.01, ..., -0.00048]
该 FIR 滤波器为对称线性相位结构(适合 FPGA/MCU 实时实现)
上面只是对SAR架构的计算,在ADI的文档里面也有别的分析,也可以看一下:
在The Data Conversion Handbook 中的解释
这张图清晰地展示了三种不同的 ADC 结构与其对量化噪声频谱的影响,重点在于过采样(Oversampling)、数字滤波(Digital Filtering)、抽取(Decimation)和噪声整形(Noise Shaping)的配合方式对系统性能的改善。
我们逐一解读图中 A/B/C 三种结构及其频谱图:
图 A — Nyquist Operation(奈奎斯特采样操作)
左边结构框图:
使用标准 ADC(如 SAR ADC),采样频率为 fs,没有额外的处理。
右边频谱图:
在 0 ~ fs/2 的信号带宽中,量化噪声是均匀分布的(白噪声)。
噪声功率密度 = q²/12,其中 q 是 1 LSB。
特征总结:
优点:结构简单,响应快。
缺点:所有带宽内的量化噪声都直接影响有效信号,ENOB 受限,没有任何削弱噪声的手段。
图 B — Oversampling + Digital Filter + Decimation
左边结构框图:
ADC 的采样频率提高到 K·fs(K 倍过采样),仍然是常规 ADC(如 SAR ADC 或 Flash);后级加入数字滤波器,将信号带外的噪声滤除。
抽取(Decimation):把采样率降低回 fs。
右边频谱图:
-
初始量化噪声仍然均匀分布在 0 ~ Kfs/2,但经过滤波器抑制了带外噪声。 -
有效带宽在 fs/2 内,噪声被压低,因此信噪比(SNR)提升。 -
噪声“能量”没有减少,但因为能量分布在更宽的频带,单位频率的噪声密度变小,通过数字滤波器“削减”掉带外的噪声。
特征总结:
**ENOB 增加约 = 0.5 × log₂(K)**;比如 16× 过采样,可以提升 2 bit,SAR ADC 也能用这个方式提升性能(需要硬件允许高速采样)。
图 C — Oversampling + Noise Shaping + Digital Filter + Decimation
左边结构框图:
使用 Σ-Δ 模数转换器(ΣΔ Modulator);内部包含噪声整形器(Noise Shaper),本质上是反馈环结构,会将量化噪声“挤”到高频段。
同样使用数字滤波器和抽取来保留有效信号、滤除高频噪声。
右边频谱图:
原始量化噪声不是白噪声,而是被重定向到高频区域(越阶的ΣΔ调制器,整形越陡峭)。
带内频率区域的噪声密度显著下降,极大地提升带内 SNR 和 ENOB。
数字滤波器只保留带内部分并下采样,去除带外高能噪声。
特征总结:
这种结构是音频 ADC 和高精度低速测量 ADC 的主流架构(比如 ADS1256, AD717x, LTC2500 等)。
可以实现 >20 bit 的分辨率,带内噪声非常低。
总结比较:
|
|
|
|
|
|
|
|
|
|---|---|---|---|---|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
高速控制类 ADC(如电机控制、电源控制、SAR ADC),B 是可行方案。
高分辨率低速信号采集(如温度、电压、电流、传感器、生理信号等),C 是首选。
A 适用于预算有限或简单系统,但对抗噪声和提高精度非常有限。
使用 SAR ADC,但信号带宽中含有大量噪声(如 1/f 噪声或宽带热噪声),可以从以下几方面优化:
外部抗混叠滤波器(Anti-aliasing Filter)
设计一个低通滤波器,使噪声频谱能量集中于目标带宽内,避免 aliasing:
典型拓扑:RC、Sallen-Key、多级巴特沃斯或椭圆滤波器
截止频率设置为:略小于 fs/2
其中增加阶数可更有效抑制高频噪声,但也引入相位延迟。
低噪声驱动器匹配
SAR ADC 的输入电容会导致动态输入电流变化,驱动放大器必须具备:足够带宽和压摆率,低输出阻抗;输出负载稳定性好
否则会在采样瞬间引入额外噪声或非线性。
采样策略优化(平均/超采样)
如果系统带宽允许,可以用多次采样平均(不是过采样滤波),提高信噪比(SNR):
其中 为平均次数,对高频白噪声特别有效
import numpy as np
import matplotlib.pyplot as plt
from scipy import signal
# 采样频率和RC滤波器参数
fs = 100e3 # 100 kHz
fc = 25e3 # 截止频率 25 kHz
RC = 1 / (2 * np.pi * fc)
# 定义频率轴(对数刻度)
f = np.logspace(1, 5, 1000) # 从10Hz到100kHz
w = 2 * np.pi * f
# RC 滤波器幅频响应 H(f)
H = 1 / (1 + 1j * w * RC)
H_dB = 20 * np.log10(np.abs(H))
# 假设输入白噪声 PSD 是常数
S_white = np.ones_like(f)
# 滤波后 PSD = S_white × |H(f)|²
S_filtered = S_white * np.abs(H)**2
# 绘图
plt.figure(figsize=(10, 6))
plt.subplot(2, 1, 1)
plt.semilogx(f, H_dB, label="|H(f)| in dB")
plt.axvline(fc, color='gray', linestyle='--', label=f"fc = {fc/1e3:.1f} kHz")
plt.title("RC Low-Pass Filter Frequency Response")
plt.ylabel("Magnitude (dB)")
plt.grid(True, which='both')
plt.legend()
plt.subplot(2, 1, 2)
plt.semilogx(f, 10*np.log10(S_white), '--', label="Input PSD (white noise)")
plt.semilogx(f, 10*np.log10(S_filtered), label="Output PSD (filtered)")
plt.axvline(fc, color='gray', linestyle='--')
plt.ylabel("PSD (dB/Hz)")
plt.xlabel("Frequency (Hz)")
plt.grid(True, which='both')
plt.legend()
plt.tight_layout()
plt.show()
1/f 噪声 + RC 滤波器
import numpy as np
import matplotlib.pyplot as plt
# 频率轴(对数刻度)
f = np.logspace(1, 5, 1000) # 10 Hz 到 100 kHz
fs = 100e3 # 采样率
fc = 2.5e3 # RC 滤波器的截止频率
RC = 1 / (2 * np.pi * fc)
# RC 滤波器频率响应 H(f)
w = 2 * np.pi * f
H = 1 / (1 + 1j * w * RC)
# --- 1/f 噪声建模 ---
K = 1e-4 # 1/f 噪声强度
S_1f = K / f
# 滤波后 PSD
S_1f_filtered = S_1f * np.abs(H)**2
# 绘图
plt.figure(figsize=(10, 6))
plt.semilogx(f, 10 * np.log10(S_1f), '--r', label='1/f Noise (Input PSD)')
plt.semilogx(f, 10 * np.log10(S_1f_filtered), 'b', label='Filtered PSD (1/f × |H(f)|²)')
plt.axvline(fc, color='gray', linestyle='--', label=f'fc = {fc/1e3:.1f} kHz')
plt.title('RC Filtering Effect on 1/f Noise')
plt.xlabel('Frequency (Hz)')
plt.ylabel('PSD (dB/Hz)')
plt.legend()
plt.grid(True, which='both')
plt.tight_layout()
plt.show()
加入信号通带、噪声积分计算、抽取效果
import numpy as np
import matplotlib.pyplot as plt
# -----------------------------
# System parameters
# -----------------------------
fs = 100e3 # Sampling frequency
fc = 2.5e3 # RC cutoff frequency
RC = 1 / (2 * np.pi * fc)
alpha = 1.0 # 1/f noise exponent
K = 1e-4 # 1/f noise power coefficient
f_signal_max = 5e3 # Maximum signal frequency (passband edge)
# Frequency axis
f = np.logspace(1, 5, 2000) # 10 Hz to 100 kHz
w = 2 * np.pi * f
# -----------------------------
# Define 1/f noise PSD
# -----------------------------
S_1f = K / (f**alpha)
# RC filter transfer function magnitude squared
H_squared = 1 / (1 + (f / fc)**2)
# Filtered PSD
S_filtered = S_1f * H_squared
# -----------------------------
# Noise Power Integration
# -----------------------------
def integrate_psd(f, psd, f_low, f_high):
"""Integrate PSD over a specific frequency band using trapezoidal rule"""
mask = (f >= f_low) & (f <= f_high)
return np.trapz(psd[mask], f[mask])
# Total input noise (before filtering)
P_input_total = integrate_psd(f, S_1f, f[0], fs/2)
# Total output noise (after filtering)
P_output_total = integrate_psd(f, S_filtered, f[0], fs/2)

# Signal band noise power (after filtering)
P_output_signal_band = integrate_psd(f, S_filtered, f[0], f_signal_max)
# -----------------------------
# Decimation effect
# -----------------------------
OSR = 8
fs_decimated = fs / OSR
f_decimated_Nyquist = fs_decimated / 2
P_decimated_band = integrate_psd(f, S_filtered, f[0], f_decimated_Nyquist)
# -----------------------------
# Plot PSD and integration bands
# -----------------------------
plt.figure(figsize=(12, 8))
plt.semilogx(f, 10 * np.log10(S_1f), '--r', label='Original 1/f PSD')
plt.semilogx(f, 10 * np.log10(S_filtered), 'b', label='Filtered PSD (1/f × |H(f)|²)')
plt.axvspan(f[0], f_signal_max, color='green', alpha=0.1, label='Signal Band (0–5 kHz)')
plt.axvspan(f[0], f_decimated_Nyquist, color='purple', alpha=0.1, label=f'Decimated Nyquist Band (0–{fs_decimated/2/1e3:.1f} kHz)')
plt.axvline(fc, color='gray', linestyle='--', label=f'fc = {fc/1e3:.1f} kHz')
plt.axvline(fs/2, color='black', linestyle=':', label='Nyquist freq (fs/2)')
plt.xlabel('Frequency (Hz)')
plt.ylabel('PSD (dB/Hz)')
plt.title('1/f Noise PSD with RC Filtering and Integration Bands')
plt.legend()
plt.grid(True, which='both')
plt.tight_layout()
plt.show()
# -----------------------------
# Display integrated powers
# -----------------------------
import pandas as pd
import ace_tools as tools
df = pd.DataFrame({
"Noise Power (V²)": [
P_input_total,
P_output_total,
P_output_signal_band,
P_decimated_band
],
"Description": [
"Total 1/f noise (no filtering)",
"Total filtered noise (0–fs/2)",
"Filtered noise in signal band (0–5kHz)",
f"Filtered noise in decimated band (0–{fs_decimated/2/1e3:.1f}kHz)"
]
})
tools.display_dataframe_to_user(name="Integrated Noise Power Summary", dataframe=df)
Noise Power (V²) Description
0 0.000851 Total 1/f noise (no filtering)
1 0.000552 Total filtered noise (0–fs/2)
2 0.000541 Filtered noise in signal band (0–5kHz)
3 0.000545 Filtered noise in decimated band (0–6.2kHz)
量化模拟后的 ENOB 估算
import numpy as np
import matplotlib.pyplot as plt
# -----------------------------
# System parameters
# -----------------------------
fs = 100e3 # Sampling frequency
OSR = 8 # Oversampling ratio
fs_dec = fs / OSR # Decimated sampling rate
fc = 2.5e3 # RC or filter cutoff
RC = 1 / (2 * np.pi * fc)
f = np.logspace(1, 5, 2000)
w = 2 * np.pi * f
# -----------------------------
# Mixed noise PSD model: 1/f + white
# -----------------------------
K_1f = 1e-4 # 1/f noise coefficient
S_white = 1e-6 # White noise power spectral density (V^2/Hz)
S_1f = K_1f / f # 1/f noise
S_total_input = S_1f + S_white # Mixed noise PSD
# -----------------------------
# First-order RC filter
# -----------------------------
H1_sq = 1 / (1 + (f / fc)**2)
# -----------------------------
# Second-order Butterworth LPF (normalized)
# -----------------------------
# |H(jw)|^2 = 1 / (1 + (f/fc)^4 + 2*(f/fc)^2)
f_norm = f / fc
H2_sq = 1 / (1 + (f_norm)**4 + 2*(f_norm)**2)
# -----------------------------
# Filtered PSDs
# -----------------------------
S_filtered_1st = S_total_input * H1_sq
S_filtered_2nd = S_total_input * H2_sq
# -----------------------------
# Noise power integration
# -----------------------------
def integrate_psd(f, psd, f_low, f_high):
mask = (f >= f_low) & (f <= f_high)
return np.trapz(psd[mask], f[mask])
# Integration bounds
f_band = fs_dec / 2
# Integrated noise power (for ENOB calculation)
P1 = integrate_psd(f, S_filtered_1st, 0, f_band)
P2 = integrate_psd(f, S_filtered_2nd, 0, f_band)
# -----------------------------
# ENOB estimation (V_FS = 1 V assumed)
# -----------------------------
V_FS = 1.0
SNR1 = 10 * np.log10((V_FS**2 / 8) / P1)
SNR2 = 10 * np.log10((V_FS**2 / 8) / P2)
ENOB1 = (SNR1 - 1.76) / 6.02
ENOB2 = (SNR2 - 1.76) / 6.02
# -----------------------------
# Plot PSD comparisons
# -----------------------------
plt.figure(figsize=(12, 8))
plt.semilogx(f, 10 * np.log10(S_total_input), 'k--', label='Input PSD (1/f + white)')
plt.semilogx(f, 10 * np.log10(S_filtered_1st), 'b', label='1st-order RC filtered PSD')
plt.semilogx(f, 10 * np.log10(S_filtered_2nd), 'g', label='2nd-order Butterworth filtered PSD')
plt.axvline(fc, color='gray', linestyle='--', label=f'fc = {fc/1e3:.1f} kHz')
plt.axvline(f_band, color='purple', linestyle=':', label=f'Decimated Nyquist = {f_band/1e3:.1f} kHz')
plt.xlabel('Frequency (Hz)')
plt.ylabel('PSD (dB/Hz)')
plt.title('Mixed Noise PSD with 1st & 2nd Order Filtering')
plt.legend()
plt.grid(True, which='both')
plt.tight_layout()
plt.show()
# -----------------------------
# Display results
# -----------------------------
import pandas as pd
import ace_tools as tools
df = pd.DataFrame({
"Filter Type": ["1st-Order RC", "2nd-Order Butterworth"],
"Noise Power in Decimated Band (V²)": [P1, P2],
"SNR (dB)": [SNR1, SNR2],
"ENOB (bits)": [ENOB1, ENOB2]
})
tools.display_dataframe_to_user(name="ENOB and Noise Comparison", dataframe=df)
Filter Type Noise Power in Decimated Band (V²) SNR (dB)
0 1st-Order RC 0.003509 15.516666
1 2nd-Order Butterworth 0.002410 17.148220
ENOB (bits)
0 2.285161
1 2.556183
import numpy as np
import matplotlib.pyplot as plt
from scipy.signal import firwin, lfilter, freqz, decimate
# -----------------------------
# Signal parameters
# -----------------------------
fs = 100e3 # Original sampling frequency
OSR = 8 # Oversampling ratio
fs_dec = fs // OSR # Decimated frequency
T = 1 # Duration (1 second)
N = int(fs * T)
t = np.linspace(0, T, N, endpoint=False)
# -----------------------------
# Generate noisy input (same as before)
# -----------------------------
np.random.seed(0)
white_noise = np.random.normal(0, 1, N)
def generate_1f_noise(N, alpha=1.0):
freqs = np.fft.rfftfreq(N, 1/fs)
freqs[0] = freqs[1]
magnitude = 1 / freqs**(alpha / 2)
phase = np.exp(1j * 2 * np.pi * np.random.rand(len(freqs)))
spectrum = magnitude * phase
noise = np.fft.irfft(spectrum, n=N)
return (noise - np.mean(noise)) / np.std(noise)
pink_noise = generate_1f_noise(N) * 0.05
x = 0.01 * white_noise + pink_noise # total input noise
# -----------------------------
# FIR Decimation Filter Design
# -----------------------------
numtaps = 64
cutoff_hz = fs_dec / 2 # Nyquist of decimated system
fir_coeff = firwin(numtaps, cutoff_hz / (fs / 2)) # normalize cutoff
# Frequency response
w_fir, h_fir = freqz(fir_coeff, worN=1024, fs=fs)
# Filtered signal
x_filt = lfilter(fir_coeff, 1.0, x)
# Decimation
x_dec = x_filt[::OSR]
# Time vector for decimated signal
t_dec = t[::OSR]
# -----------------------------
# Plot FIR filter frequency response
# -----------------------------
plt.figure(figsize=(10, 4))
plt.plot(w_fir, 20 * np.log10(np.abs(h_fir)), label='FIR Magnitude Response')
plt.axvline(fs_dec / 2, color='red', linestyle='--', label=f'Decimated Nyquist = {fs_dec/2/1e3:.1f} kHz')
plt.title('FIR Decimation Filter Frequency Response')
plt.xlabel('Frequency (Hz)')
plt.ylabel('Magnitude (dB)')
plt.grid(True)
plt.legend()
plt.tight_layout()
plt.show()
# -----------------------------
# Plot time-domain signal before and after decimation
# -----------------------------
plt.figure(figsize=(10, 4))
plt.plot(t[:2000], x[:2000], alpha=0.5, label='Original Oversampled')
plt.plot(t_dec[:250], x_dec[:250], label='After FIR + Decimation')
plt.title('Time-Domain Signal Before and After FIR Decimation')
plt.xlabel('Time (s)')
plt.ylabel('Amplitude')
plt.legend()
plt.grid(True)
plt.tight_layout()
plt.show()
# -----------------------------
# Return FIR coefficients and basic decimation result
# -----------------------------
import pandas as pd
import ace_tools as tools
df = pd.DataFrame({
"FIR Coefficients": fir_coeff
})
tools.display_dataframe_to_user(name="FIR Filter Coefficients", dataframe=df)
FIR Coefficients
0 -0.000158
1 -0.000478
2 -0.000802
3 -0.001103
4 -0.001322
https://www./cn/resources/technical-books/data-conversion-handbook.html
https://www./media/en/training-seminars/design-handbooks/Data-Conversion-Handbook/Chapter3.pdf
https://www./cn/parametricsearch/11249#/
https://www./cn/products/ad7768-4.html
https://www./media/en/technical-documentation/data-sheets/250032fb.pdf
https://blog.csdn.net/Gou_Hailong/article/details/105761432