ΔΣ ADCの基本
ΔΣ(デルタシグマ)ADCは、従来のADCが抱えるアナログ回路の課題を、高速なサンプリングとデジタル信号処理によって解決し、高分解能を実現するA/D変換器です。その核となる技術は、オーバーサンプリングとノイズシェイピングの2つです。
1. オーバーサンプリング
ΔΣ ADCは、信号帯域幅の2倍であるナイキスト周波数$f_{Nyq}$よりも、はるかに高いサンプリング周波数$f_s$で動作します。このサンプリング周波数とナイキスト周波数の比率を**オーバーサンプリング比(OSR)**と呼びます。
$$OSR = \frac{f_s}{f_{Nyq}} = \frac{f_s}{2f_B}$$
ここで、$f_B$は信号帯域幅です。
量子化ノイズの総パワー$P_{noise}$は一定ですが、オーバーサンプリングによってこのノイズがより広い周波数範囲に拡散されます。信号帯域内のノイズパワー$P_{noise_band}$は、OSRに反比例して減少します。
$$P_{noise_band} = \frac{P_{noise}}{OSR}$$
これにより、信号対雑音比(SNR)は次のように改善されます。
$$SNR = 10 \log_{10} \frac{S}{N/OSR} = 10 \log_{10} \frac{S}{N} + 10 \log_{10} OSR \quad [dB]$$
この式から、OSRを2倍にするごとに、SNRは$10 \log_{10} 2 \approx 3.01$ dB改善することがわかります。これは、単純にサンプリング周波数を上げたことによる効果です。
2. ノイズシェイピング
ΔΣ ADCの性能を飛躍的に向上させるのがノイズシェイピングです。これは、フィードバックループ内の積分器を利用して、量子化ノイズの周波数分布を整形し、低周波領域のノイズを高周波領域に移動させる技術です。
1次ΔΣ変調器の線形化モデルに基づくと、出力$V(z)$は、入力信号成分とノイズ成分に分解できます。
$$V(z) = STF(z) \cdot U(z) + NTF(z) \cdot E(z)$$
- $STF(z)$は信号伝達関数で、1次変調器ではほぼ1(通過)となります。
- $NTF(z)$は雑音伝達関数で、1次変調器では$NTF(z) = 1 - z^{-1}$となります。
この雑音伝達関数を周波数領域で評価すると、振幅スペクトルは次のようになります。
$$|NTF(f)| = |1 - e^{-j2\pi f T_s}| = |e^{-j\pi f T_s}(e^{j\pi f T_s} - e^{-j\pi f T_s})| = 2|\sin(\pi f T_s)|$$
低周波領域($f \ll 1/T_s$)では、$\sin(\pi f T_s) \approx \pi f T_s$となるため、雑音スペクトル密度は周波数に比例して増加します。これは、ノイズパワーが周波数とともに20 dB/dec(10倍の周波数で100倍のパワー)で増加することを意味します。
信号帯域内のノイズパワーは、このノイズスペクトル密度を積分して得られます。その結果、1次ΔΣ変調器のSNRは以下のように表されます。
$$SNR_{1st} \propto OSR^3$$
この関係から、OSRを2倍にするごとに、SNRは$10 \log_{10} 2^3 = 30 \log_{10} 2 \approx 9.03$ dB改善することがわかります。この9dBの改善は、単純なオーバーサンプリングによる3dBの改善を大きく上回り、ノイズシェイピングの絶大な効果を示しています。
ΔΣ ADCの構成と利点
ΔΣ ADCは、アナログ信号をデジタルに変換するΔΣ変調器と、その出力を処理するデシメーションフィルタで構成されます。
-
ΔΣ変調器
- 1ビットコンパレータと**1ビットD/A変換器(DAC)**をフィードバックループ内に持ちます。
- この1ビットDACは、原理的に非線形性がなく、高精度を保てます。
- アナログ入力信号の平均値を、高周波の1ビットデジタルストリーム(ビットストリーム)として出力します。
-
デシメーションフィルタ
- 高周波の1ビット出力から、信号帯域外のノイズを除去し、ナイキストレートまでダウンサンプリングします。
- この際、複数のサンプルを平均化することで、出力のビット幅を増やし、高分解能のデジタル信号を生成します。
この構成により、ΔΣ ADCは以下の利点をもたらします。
- 高い分解能: アナログ回路を簡素化し、多くの機能を高精度なデジタル回路で担うことで、非常に高い分解能を実現します。
- アンチエイリアシングフィルタの簡素化: サンプリング周波数が非常に高いため、エイリアシング(信号の折り返し)が信号帯域から遠く離れた周波数で発生します。このため、フィルタの設計が容易になり、急峻な特性を持つ高価なフィルタが不要になります。
import numpy as np
import matplotlib.pyplot as plt
# ========================
# Parameters / パラメータ設定
# ========================
fs1 = 1.0 # Nyquist-rate sampling frequency (規格化した基準サンプリング周波数)
fs2 = 4.0 # Oversampling frequency (Nyquistの4倍サンプリング)
fB = fs1 / 2 # 信号帯域幅 (Nyquistの場合のfs1/2)
N_points = 500
f_axis = np.linspace(0, fs2/2, N_points) # 横軸は 0 ~ fs2/2 に統一
# ========================
# PSD モデル定義 (理論モデルの簡易版)
# ========================
# (a) Nyquist-rate ADC:
# NyquistレートADCでは、量子化雑音のパワースペクトル密度 (PSD) は
# 信号帯域幅 fB = fs1/2 の範囲でフラット(一定)になる。
psd_nyquist = np.where(f_axis <= fs1/2, 1.0, 0.0)
# (b) Oversampling ADC:
# オーバーサンプリングADCでは、全体の雑音電力は変わらないが
# サンプリング周波数を上げることで雑音が広い帯域に分散される。
# よって PSD の高さが fs1/fs2 に比例して低くなる。
psd_oversample = np.where(f_axis <= fs2/2, fs1/fs2, 0.0)
# (c) Noise-Shaping ADC:
# ノイズシェーピングADCでは、雑音が単純に平坦に広がるのではなく、
# フィルタによって低周波で抑圧され、高周波側に追いやられる。
# 簡易的に「周波数の二乗」で増加する形をモデル化した。
psd_shaped = (f_axis / (fs2/2))**2
psd_shaped /= np.max(psd_shaped) # 最大値を1に正規化(比較しやすくするため)
# ========================
# プロット描画 (Plotting)
# ========================
plt.figure(figsize=(8, 8)) # 図全体のサイズ
# (a) Nyquist-rate
plt.subplot(3, 1, 1) # 3行1列のサブプロット → 1番目
plt.fill_between(f_axis, psd_nyquist, color="gray", alpha=0.7) # 塗りつぶし
plt.axvline(fB, color="k", linestyle="--", label="fB") # 信号帯域幅を縦線で示す
plt.title("(a) Nyquist-rate", fontsize=12)
plt.ylabel("PSD")
plt.xlim(0, fs2/2) # 横軸を統一
plt.ylim(0, 1.1) # 縦軸を統一(0~1に揃えると比較しやすい)
plt.legend()
# (b) Oversampling
plt.subplot(3, 1, 2) # 2番目のサブプロット
plt.fill_between(f_axis, psd_oversample, color="gray", alpha=0.7)
plt.axvline(fB, color="k", linestyle="--")
plt.title("(b) Oversampling", fontsize=12)
plt.ylabel("PSD")
plt.xlim(0, fs2/2)
plt.ylim(0, 1.1) # 縦軸統一
# (c) Noise shaping
plt.subplot(3, 1, 3) # 3番目のサブプロット
plt.fill_between(f_axis, psd_shaped, color="gray", alpha=0.7)
plt.axvline(fB, color="k", linestyle="--")
plt.title("(c) Noise shaping", fontsize=12)
plt.xlabel("Frequency (normalized)")
plt.ylabel("PSD")
plt.xlim(0, fs2/2)
plt.ylim(0, 1.1) # 縦軸統一
plt.tight_layout()
plt.show()