🐡

デルタシグマADCの仕組み

に公開

0 本章の目的

デルタシグマADC(ΔΣ ADC)は、
高いサンプリング周波数と量子化ノイズの周波数特性を利用し、
信号帯域内のノイズを低減することで高分解能を得る
アナログ‐デジタル変換方式である。

本章では、デルタシグマADCの基本的な動作を理解するために、

  1. 量子化ノイズの性質
  2. オーバーサンプリングの効果
  3. ΔΣ変調器の時間領域での動作
  4. ノイズシェーピングの周波数的意味
  5. デシメーション処理の役割

について、順に説明する。


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 デシメーション処理

ΔΣ変調器の出力は、

・高サンプリング周波数
・高周波ノイズを多く含む

という特徴を持つ。

そこで、

  1. デジタル低域通過フィルタ
  2. 間引き(デシメーション)

を行い、
信号帯域のみを取り出す。


8 デルタシグマADCの特長

デルタシグマADCは、

・高精度な多ビットDACを必要としない
・高利得増幅回路への依存が小さい
・デジタル処理による精度向上が可能

という特長を持つ。

このため、
高分解能が要求される低〜中速用途に適している。


9 まとめ

デルタシグマADCは、

・高いサンプリング周波数
・量子化ノイズの周波数制御
・デジタル信号処理

を組み合わせることで、
高分解能を実現するアナログ‐デジタル変換方式である。

量子化誤差を時間的・周波数的に制御する点が、
本方式の基本原理である。


参考文献

[1] 和保 孝夫,
『アナログ/デジタル変換入門 ― 原理と回路実装 ―』,コロナ社,2019年.
ISBN: 978-4-339-00918-7
出版社公式ページ
https://www.coronasha.co.jp/np/isbn/9784339009187/


[2] Richard Schreier, Gabor C. Temes,
Understanding Delta-Sigma Data Converters,
IEEE Press / Wiley, 2005.
ISBN: 978-0471465850
Wiley公式ページ
https://www.wiley.com/en-us/Understanding+Delta+Sigma+Data+Converters-p-9780471465850


[3] Boris Murmann,
“ADC Performance Survey 1997–2025,”
ISSCC / VLSI 掲載ADC性能データベース(オンライン)
GitHub公式リポジトリ
https://github.com/bmurmann/ADC-survey


[4] Analog Devices,
“Sigma-Delta ADCs Tutorial,”
Analog Devices Technical Article.
公式ページ
https://www.analog.com/en/resources/technical-articles/sigmadelta-adcs-tutorial.html


[5] Texas Instruments,
“How delta-sigma ADCs work, Part 1,”
Application Report, SLYT423.
公式PDF
https://www.ti.com/lit/slyt423


[6] Texas Instruments,
“How delta-sigma ADCs work, Part 2,”
Application Report, SLYT438.
公式PDF
https://www.ti.com/lit/slyt438


[7] Texas Instruments,
“Digital Filter Types in Delta-Sigma ADCs,”
Application Report, SBAA230.
公式PDF
https://www.ti.com/lit/sbaa230


[8] Walt Kester,
“ADC Architectures III: Sigma-Delta ADC Basics,”
Analog Devices Tutorial MT-022.
公式ページ
https://www.analog.com/media/en/training-seminars/tutorials/MT-022.pdf


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