デルタシグマADCの仕組み
0 本章の目的
デルタシグマADC(ΔΣ ADC)は、
高いサンプリング周波数と量子化ノイズの周波数特性を利用し、
信号帯域内のノイズを低減することで高分解能を得る
アナログ‐デジタル変換方式である。
本章では、デルタシグマADCの基本的な動作を理解するために、
- 量子化ノイズの性質
- オーバーサンプリングの効果
- ΔΣ変調器の時間領域での動作
- ノイズシェーピングの周波数的意味
- デシメーション処理の役割
について、順に説明する。
1 記号と基本量
本章で用いる主な記号を以下に示す。
fs :サンプリング周波数 [Hz]
Ts :サンプリング周期 [s](Ts = 1 / fs)
BW :信号帯域幅 [Hz]
OSR:オーバーサンプリング比
Δ :量子化ステップ幅
eq[n]:量子化誤差
Vref:参照電圧
オーバーサンプリング比 OSR は、次式で定義される。
OSR = fs / (2 × BW)
2 デルタシグマADCの基本的な考え方
デルタシグマADCは、
一回の変換で高精度を得るのではなく、
多数の粗い量子化結果を用いて平均的な精度を高める方式である。
一般的な逐次比較型ADCでは、
1回の変換動作で多ビットの量子化を行う。
一方、デルタシグマADCでは、
・量子化ビット数を小さくする
・サンプリング周波数を非常に高くする
・量子化誤差の周波数分布を制御する
という方法をとる。
3 量子化ノイズの基本
3.1 量子化誤差のモデル
量子化によって生じる誤差 eq は、
理想的には次の範囲に一様に分布すると考えられる。
-Δ/2 ≦ eq ≦ +Δ/2
このとき、量子化雑音電力 Pq(平均二乗値)は、
Pq = E[eq²] = Δ² / 12
で与えられる。
3.2 周波数分布の近似
量子化ノイズは、
0 〜 fs/2 の周波数範囲に一様に分布する
白色雑音として近似される。
このとき、単位周波数あたりのノイズ密度 N0 は、
N0 = Pq / (fs/2)
となる。
4 オーバーサンプリングの効果
4.1 帯域内ノイズの評価
信号帯域 BW に含まれるノイズ電力 Pnoise は、
Pnoise = N0 × BW
= (Pq / (fs/2)) × BW
と表される。
ここで OSR = fs / (2BW) を用いると、
Pnoise ∝ 1 / OSR
が得られる。
4.2 意味の整理
オーバーサンプリングとは、
量子化ノイズの総量を変えずに、
信号帯域に含まれるノイズのみを減少させる操作である。
ただし、この効果だけでは
分解能の向上は限定的である。
5 ΔΣ変調器の動作原理(時間領域)
5.1 一次ΔΣ変調器の構成
一次ΔΣ変調器は、
・積分器
・量子化器(比較器)
・フィードバックDAC
から構成される。
5.2 差分方程式による表現
入力信号を x[n]、出力信号を y[n] とする。
積分器の内部状態 v[n] は、
v[n] = v[n−1] + x[n] − y[n−1]
で表される。
量子化器は、
y[n] = Q(v[n])
と動作し、Q( ) は量子化操作を表す。
5.3 動作の意味
この構造では、
入力と出力との差が積分され、
誤差が大きくなると出力が反転する。
その結果、
・低周波成分は平均的に正しく表現される
・量子化誤差は高周波成分として現れる
という性質が生じる。
6 ノイズシェーピング(周波数領域)
6.1 基本的性質
ΔΣ変調器は周波数領域で見ると、
・信号成分:低域通過
・量子化ノイズ:高域通過
という伝達特性を持つ。
6.2 一次ΔΣのノイズ特性
一次ΔΣ変調器における量子化ノイズのパワースペクトルは、
N(f) ∝ f²
となる。
これは、
低周波領域ではノイズが抑制され、
高周波領域でノイズが増大することを示している。
6.3 効果の整理
オーバーサンプリングとノイズシェーピングを併用することで、
信号帯域内の量子化ノイズは大きく低減される。
7 デシメーション処理
ΔΣ変調器の出力は、
・高サンプリング周波数
・高周波ノイズを多く含む
という特徴を持つ。
そこで、
- デジタル低域通過フィルタ
- 間引き(デシメーション)
を行い、
信号帯域のみを取り出す。
8 デルタシグマADCの特長
デルタシグマADCは、
・高精度な多ビットDACを必要としない
・高利得増幅回路への依存が小さい
・デジタル処理による精度向上が可能
という特長を持つ。
このため、
高分解能が要求される低〜中速用途に適している。
9 まとめ
デルタシグマADCは、
・高いサンプリング周波数
・量子化ノイズの周波数制御
・デジタル信号処理
を組み合わせることで、
高分解能を実現するアナログ‐デジタル変換方式である。
量子化誤差を時間的・周波数的に制御する点が、
本方式の基本原理である。
参考文献
[1] 和保 孝夫,
『アナログ/デジタル変換入門 ― 原理と回路実装 ―』,コロナ社,2019年.
ISBN: 978-4-339-00918-7
出版社公式ページ
[2] Richard Schreier, Gabor C. Temes,
Understanding Delta-Sigma Data Converters,
IEEE Press / Wiley, 2005.
ISBN: 978-0471465850
Wiley公式ページ
[3] Boris Murmann,
“ADC Performance Survey 1997–2025,”
ISSCC / VLSI 掲載ADC性能データベース(オンライン)
GitHub公式リポジトリ
[4] Analog Devices,
“Sigma-Delta ADCs Tutorial,”
Analog Devices Technical Article.
公式ページ
[5] Texas Instruments,
“How delta-sigma ADCs work, Part 1,”
Application Report, SLYT423.
公式PDF
[6] Texas Instruments,
“How delta-sigma ADCs work, Part 2,”
Application Report, SLYT438.
公式PDF
[7] Texas Instruments,
“Digital Filter Types in Delta-Sigma ADCs,”
Application Report, SBAA230.
公式PDF
[8] Walt Kester,
“ADC Architectures III: Sigma-Delta ADC Basics,”
Analog Devices Tutorial MT-022.
公式ページ
Pythonコード1
# Program Name: delta_sigma_1st_order_demo_with_decimation.py
# Creation Date: 20260207
# Purpose: 1st-order Delta-Sigma modulator simulation + PSD + digital decimation filter reconstruction
import numpy as np
import matplotlib.pyplot as plt
# Optional: SciPy for Welch PSD and FIR design/filtering
try:
from scipy import signal
SCIPY_OK = True
except Exception:
SCIPY_OK = False
# =========================
# PARAM_INIT
# =========================
fs_base = 1000.0 # Nyquist rate (2*BW) [Hz]
OSR = 64 # Oversampling ratio
fs = fs_base * OSR # Sampling frequency [Hz]
T = 0.05 # Simulation time [s]
f_sig = 50.0 # Input frequency [Hz]
A_sig = 0.5 # Input amplitude (<1 recommended)
nperseg = 4096 # Welch segment length
eps_psd = 1e-20 # Avoid log(0)
zoom_samples = 200
delta_q = 2.0 # Quantizer step for +/-1 output
# Decimation / Reconstruction filter settings
BW = fs_base / 2.0 # Signal bandwidth [Hz]
lp_cutoff_hz = 0.45 * BW # Lowpass cutoff [Hz] (keep some margin)
fir_taps = 511 # FIR taps (odd is convenient)
# =========================
# Utility: FIR lowpass (SciPy or NumPy)
# =========================
def lowpass_fir(num_taps, cutoff_hz, fs_hz):
"""
Inputs:
num_taps: int, FIR length
cutoff_hz: float, cutoff frequency [Hz]
fs_hz: float, sampling frequency [Hz]
Outputs:
h: np.ndarray, FIR coefficients
Process:
Windowed-sinc lowpass (Hamming window) if SciPy unavailable.
"""
if SCIPY_OK:
return signal.firwin(num_taps, cutoff_hz, fs=fs_hz, window="hamming", pass_zero=True)
# NumPy fallback: windowed-sinc
M = num_taps - 1
n = np.arange(num_taps)
fc = cutoff_hz / fs_hz # normalized (cycles/sample), 0..0.5
# ideal sinc lowpass: h[n] = 2*fc*sinc(2*fc*(n-M/2))
h_ideal = 2.0 * fc * np.sinc(2.0 * fc * (n - M / 2.0))
w = np.hamming(num_taps)
h = h_ideal * w
# normalize DC gain to 1
h = h / np.sum(h)
return h
def welch_psd(x, fs_hz, nperseg_):
if SCIPY_OK:
f_, p_ = signal.welch(x, fs=fs_hz, nperseg=min(nperseg_, len(x)), scaling="density")
return f_, p_
# fallback: simple periodogram (not Welch)
n = len(x)
w = np.hanning(n)
X = np.fft.rfft((x - np.mean(x)) * w)
p = (np.abs(X) ** 2) / (fs_hz * np.sum(w ** 2))
f = np.fft.rfftfreq(n, d=1.0 / fs_hz)
return f, p
# =========================
# Time & Input
# =========================
t = np.arange(0.0, T, 1.0 / fs)
x = A_sig * np.sin(2.0 * np.pi * f_sig * t)
# =========================
# 1st-order Delta-Sigma Modulator
# =========================
n = len(x)
v_hist = np.zeros(n)
y_bits = np.zeros(n, dtype=np.int8)
y_bipolar = np.zeros(n)
v = 0.0
fb = 0.0
for i in range(n):
v = v + (x[i] - fb)
v_hist[i] = v
if v >= 0.0:
y_bits[i] = 1
y_bipolar[i] = 1.0
else:
y_bits[i] = 0
y_bipolar[i] = -1.0
fb = y_bipolar[i]
# =========================
# PSD (Modulator output)
# =========================
f_out, psd_out = welch_psd(y_bipolar, fs, nperseg)
# =========================
# Noise Theory (1st-order shaping)
# =========================
Pq_total = (delta_q ** 2) / 12.0
white_noise_psd = Pq_total / (fs / 2.0)
f_theory = np.geomspace(1.0, fs / 2.0, 200)
psd_theory = white_noise_psd * (2.0 * np.sin(np.pi * f_theory / fs)) ** 2
# =========================
# Digital Reconstruction: Lowpass + Decimate (OSR)
# =========================
h_lp = lowpass_fir(fir_taps, lp_cutoff_hz, fs)
if SCIPY_OK:
y_lp = signal.lfilter(h_lp, 1.0, y_bipolar)
else:
y_lp = np.convolve(y_bipolar, h_lp, mode="same")
# Group delay compensation (FIR linear phase)
gd = (fir_taps - 1) // 2
y_lp_aligned = np.roll(y_lp, -gd)
y_lp_aligned[-gd:] = y_lp_aligned[-gd-1] # simple tail fill
# Decimate by OSR to fs_base
y_dec = y_lp_aligned[::OSR]
t_dec = t[::OSR]
# Scale (optional): match amplitude using least-squares gain
# y_dec_scaled ≈ x_dec
x_dec = x[::OSR]
gain = np.dot(x_dec, y_dec) / (np.dot(y_dec, y_dec) + 1e-30)
y_dec_scaled = gain * y_dec
# PSD after reconstruction (at fs_base)
f_rec, psd_rec = welch_psd(y_dec_scaled, fs_base, nperseg)
# =========================
# Plot
# =========================
plt.figure(figsize=(12, 18))
# 1) Time domain: input vs bitstream
plt.subplot(5, 1, 1)
plt.plot(t, x, linewidth=2, label="Input x(t)")
plt.step(t, y_bits, where="post", alpha=0.7, label="Bitstream (1/0)")
plt.title("1. Time Domain: Input vs 1-bit Output")
plt.ylabel("Amplitude / Logic")
plt.grid(True)
plt.legend()
# 2) Integrator state
plt.subplot(5, 1, 2)
plt.plot(t, v_hist, label="Integrator v[n]")
plt.axhline(0.0, linestyle="--", alpha=0.5)
plt.title("2. Integrator State")
plt.ylabel("State")
plt.grid(True)
plt.legend()
# 3) Frequency domain: noise shaping
plt.subplot(5, 1, 3)
plt.semilogx(f_out, 10 * np.log10(psd_out + eps_psd), label="DS Output PSD")
plt.semilogx(f_theory, 10 * np.log10(psd_theory + eps_psd), "--", label="Theory (1st-order NTF)")
plt.axhline(10 * np.log10(white_noise_psd + eps_psd), linestyle=":", label="Flat Quantization PSD")
plt.axvline(BW, linewidth=2, label="Signal BW")
plt.title("3. Frequency Domain: Noise Shaping")
plt.xlabel("Frequency [Hz]")
plt.ylabel("PSD [dB/Hz]")
plt.grid(True, which="both")
plt.legend()
# 4) Reconstruction (decimated): time domain
plt.subplot(5, 1, 4)
zN = min(len(t_dec), 300)
plt.plot(t_dec[:zN], x_dec[:zN], linewidth=2, label="Target (Input, decimated)")
plt.plot(t_dec[:zN], y_dec_scaled[:zN], linewidth=2, label="Reconstructed (LPF + Decimate)")
plt.title("4. Reconstruction in Time Domain (After Digital Filter + Decimation)")
plt.xlabel("Time [s]")
plt.ylabel("Amplitude")
plt.grid(True)
plt.legend()
# 5) Reconstruction PSD at fs_base
plt.subplot(5, 1, 5)
plt.semilogx(f_rec, 10 * np.log10(psd_rec + eps_psd), label="Reconstructed PSD (fs=fs_base)")
plt.axvline(BW, linewidth=2, label="Signal BW")
plt.title("5. Reconstruction Frequency Domain (After Decimation)")
plt.xlabel("Frequency [Hz]")
plt.ylabel("PSD [dB/Hz]")
plt.grid(True, which="both")
plt.legend()
plt.tight_layout()
plt.show()
Discussion