量子化雑音とSNR — 6.02N+1.76 dB はどこから来るのか

音楽CDの規格は「16ビット・44.1 kHz」です。このうち「16ビット」が何を意味するのかと問われたら、多くの人は「音の細かさ」と答えるでしょう。もう少し踏み込んだ人なら「ダイナミックレンジが約96 dB」と言うかもしれません。ではその96 dBという数字はどこから出てきたのでしょうか。データシートを開くと、どのADC(AD変換器)のページにも判で押したように

$$ \mathrm{SNR} = 6.02N + 1.76 \ [\mathrm{dB}] $$

という式が載っています。$N$ はビット数です。$16$ を代入すると $98.1$ dB。しかしこの「6.02」と「1.76」という中途半端な数字は、いったい何を根拠にしているのでしょうか。そして、なぜ実際のADCのデータシートには「SNR = 98 dB」ではなく「SNR = 91 dB (typ)」のように、理論値より少し悪い値が書かれているのでしょうか。

この記事では、その2つの数字を1行ずつ導きます。答えを先に言ってしまうと、$6.02$ は $20\log_{10} 2$、$1.76$ は $10\log_{10}(3/2)$ です。前者は「1ビット増えると振幅の刻みが半分になる」ことから、後者は「フルスケールの正弦波の実効値と、量子化誤差の実効値の比」から出てきます。導出そのものは高校数学の範囲で終わりますが、本当に面白いのはその前提のほうです。量子化誤差は本来まったく決定論的な量(入力が決まれば誤差も一意に決まる)なのに、それを「$[-\Delta/2, \Delta/2]$ の一様分布に従う白色雑音」と見なしてよいのはなぜか。この仮定が破れるとき何が起きるのか。破れたときにどう対処するのか。

この話は次の2つの場面で直接効いてきます。ひとつは無線受信機の設計です。受信機のADCが何ビット必要かは、受けたい信号の最小レベルと、同時に入ってくる強い妨害波のレベル差(ダイナミックレンジ)で決まります。ここで「オーバーサンプリングすれば実効ビット数が稼げる」という事実を知っているかどうかで、選ぶADCの値段が一桁変わります。もうひとつは計測器・センサの信号処理です。微小な直流変化を長時間平均して読み取るとき、素朴に平均しても分解能が上がらないことがあります。原因は量子化誤差が入力と相関して固定パターンになっているからで、これを解くのが「ディザ」というわざと雑音を足す技法です。オーディオでも、16ビットにビット落としするときにディザを入れないと、小音量部でざらついた歪みが出ます。

本記事の内容

  • 量子化誤差を一様分布としてモデル化できる条件(と、破れる具体例)
  • 量子化雑音パワー $\Delta^2/12$ の導出
  • $\mathrm{SNR} = 6.02N + 1.76$ dB の1行ずつの導出、および波形が正弦波でない場合の一般形
  • 量子化雑音のスペクトルとFFTの処理利得(ノイズフロアとSNRは別物)
  • オーバーサンプリングによる $10\log_{10}(\mathrm{OSR})$ dB の利得
  • ディザ(RPDF/TPDF)がスプリアスとノイズ変調を消す仕組み
  • コヒーレントサンプリングと窓関数によるSNDR/ENOB/SFDRの実測
  • 実機が理論値に届かない理由(INL、ジッタ、熱雑音)

前提知識

この記事を読む前に、以下の記事を読んでおくと理解が深まります。

量子化とは — 「丸める」ことが雑音を生む

連続なアナログ信号を有限の段に丸め、その差が量子化誤差になる過程を3段階で示した概念図

この図が本記事の全体像です。左のなめらかな信号(値はいくらでも細かく取れる)を、中央のように等間隔の段に丸めます。段の幅が $\Delta$(1 LSB)で、丸めた結果と元の値の差が右の赤い波形、すなわち量子化誤差です。誤差は必ず $\pm\Delta/2$ の帯の中に収まり、しかも入力が決まれば一意に決まる決定論的な量ですが、この帯の中をせわしなく往復する様子は「雑音」に見えます。この見立てをどこまで信用してよいのかが、以下の議論の主題になります。

体重計に乗ると「62.4 kg」と表示されます。この表示は0.1 kg刻みなので、本当の体重が62.43 kgでも62.37 kgでも、同じ「62.4」になります。つまり表示された数字には、最大 $\pm 0.05$ kgの「読み取れない部分」が必ず残ります。これが量子化です。目盛りを細かくすれば読み取れない部分は小さくなりますが、有限の桁数で表す限り、ゼロにはできません。

ADCがやっていることも同じです。連続的な電圧を、有限個の段(レベル)のどれかに割り当てます。$N$ ビットのADCなら段は $2^N$ 個。入力電圧が動ける全範囲(フルスケール範囲)を $V_{FS}$ とすると、1段あたりの幅は

$$ \begin{equation} \Delta = \frac{V_{FS}}{2^N} \end{equation} $$

です。この $\Delta$ を LSB(Least Significant Bit、最下位ビット)と呼びます。12ビットで $V_{FS} = 2\ \mathrm{V}_{pp}$($\pm 1$ V)なら $\Delta = 2/4096 = 488.3\ \mu\mathrm{V}$、16ビットなら $30.5\ \mu\mathrm{V}$ です。ビットが4つ増えると刻みは16分の1になります。

段への割り当て方には流儀が2つあります。ミッドライズ型は、出力レベルを $\pm\Delta/2, \pm3\Delta/2, \dots$ に置きます。0 Vという出力レベルが存在せず、0 V入力に対しては $+\Delta/2$ か $-\Delta/2$ のどちらかが出ます。一方ミッドトレッド型は出力レベルを $0, \pm\Delta, \pm2\Delta, \dots$ に置き、0 V入力に対して0が出ます。本記事ではミッドライズ型を使います。その量子化則は

$$ \begin{equation} Q(x) = \Delta\left(\left\lfloor \frac{x}{\Delta} \right\rfloor + \frac{1}{2}\right) \end{equation} $$

と書けます。

4ビット量子化器の入出力階段特性、ミッドライズ型とミッドトレッド型の原点付近の違い、量子化誤差がのこぎり波になる様子の3枚組

左は4ビット量子化器の入出力特性で、量子化とは「階段で直線を近似する」処理だということが一目でわかります。中央は原点付近の拡大で、ミッドライズ型(青)には $0$ V という出力段が存在せず、$0$ V 入力に対して $\pm\Delta/2$ のどちらかが出るのに対し、ミッドトレッド型(橙)には $0$ の段があります。右が本質的で、量子化誤差は入力に対する完全なのこぎり波、つまり決定論的で周期的な関数です。ランダムな要素はどこにもないのに $-\Delta/2$ から $+\Delta/2$ を隈なく往復している、という点をよく見ておいてください。

$\lfloor \cdot \rfloor$ は床関数です。この定義だと、入力 $x$ に対する出力は必ず $x$ から $\Delta/2$ 以内にあります。実際、$\lfloor x/\Delta \rfloor \le x/\Delta < \lfloor x/\Delta \rfloor + 1$ なので、$Q(x) - x = \Delta(\lfloor x/\Delta \rfloor + 1/2) - x$ は $-\Delta/2$ 以上 $\Delta/2$ 未満に収まります。

この差を量子化誤差と呼び、$e$ で表します。

$$ \begin{equation} e = Q(x) – x, \qquad -\frac{\Delta}{2} \le e < \frac{\Delta}{2} \end{equation} $$

ここで重要な注意点がひとつあります。この不等式が成り立つのは、入力がフルスケールの内側にあるときだけです。入力がフルスケールを超えると、出力は最大レベルに張り付き(クリップ)、誤差は $\Delta/2$ よりずっと大きくなります。実機で「入力を大きくしていったらある点から急にSNRが崩れる」のはこのクリップが原因です。以下では入力はフルスケール内にあるものとします。

さて、$e$ は $x$ が決まれば一意に決まる、完全に決定論的な量です。ランダムな要素はどこにもありません。にもかかわらず、私たちはこれを「雑音」と呼びます。次はその正当化から始めましょう。

量子化誤差を「雑音」とみなすモデル

なぜ決定論的な誤差を確率変数として扱ってよいのでしょうか。直感的な説明はこうです。入力信号が大きく振れていて、しかも隣り合うサンプル同士が量子化の格子に対して「ばらばらな位置」に落ちるなら、誤差 $e$ の値は $[-\Delta/2, \Delta/2]$ の中をほとんどでたらめに動き回ります。長い時間で見ると、どの値も同じくらいの頻度で現れる。これは一様分布と統計的に区別がつきません。円周率の小数点以下の数字が決定論的に決まっているのに、統計的には乱数のように振る舞うのと同じ理屈です。

この直感を数学的に整えたのがWidrowの量子化定理です。入力の確率密度関数のフーリエ変換(特性関数)が $|\omega| \ge 2\pi/\Delta$ で0になるなら、量子化誤差は入力と独立な $[-\Delta/2, \Delta/2]$ の一様分布に厳密に従う、という主張です。ざっくり言えば「入力の分布が $\Delta$ に比べて十分になめらかに広がっていれば成り立つ」ということで、実務的には次の3条件にまとめられます。

  1. 振幅が十分大きい — 少なくとも数LSB、できれば数十LSB以上振れていること
  2. 信号が十分に「動く」 — 波形が量子化格子と単純な同期関係を持たないこと
  3. サンプリング周波数と信号周波数が単純な有理比でない — $f_{in}/f_s$ が $1/4$ のような簡単な分数でないこと

この3つ目が意外と効きます。極端な例として、信号周波数がちょうど $f_s/4$ の正弦波を考えてみましょう。サンプル点は1周期あたり4点しかなく、位相は $0, \pi/2, \pi, 3\pi/2$ の4通りだけ。つまり信号はたかだか4つの値しか取らず、誤差も同じく数個の値しか取りません。これは一様分布とはほど遠く、誤差は周期4で完全に繰り返す、すなわち信号の高調波として現れます。実際に初期位相 $0$ で12ビット量子化すると、誤差が取る値は $+\Delta/2$ と $-\Delta/2$ の2つだけで、誤差パワーは $\Delta^2/12$ のちょうど3倍になります。「雑音」どころか、きれいな線スペクトルです。

f_s/4サンプリングではサンプル点が4通りの位相しか踏まないこと、誤差が周期4で繰り返すこと、誤差の値が2種類しかないことを示す3枚組

左の図で、$f_{in} = f_s/4$ のサンプル点(赤丸)が毎周期まったく同じ3つの高さにしか落ちていないことを確認してください。その結果として中央のように誤差は周期4で機械的に繰り返し、右のヒストグラムでは誤差が両端の2値に集中します(赤)。これに対し $f_{in}/f_s = 331/8192$ と互いに素な比を選んだ場合(緑)は、誤差がほぼ一様に散らばり、誤差パワーも $\Delta^2/12$ の $1.02$ 倍とモデルどおりになります。同じ量子化器・同じ振幅でも、周波数比の選び方ひとつで誤差パワーが3倍も変わるということです。

もう少し実用的な破れ方が、波形の種類による違いです。フルスケールの正弦波・三角波・のこぎり波を12ビットで量子化して誤差パワーを測ると、次のようになります(後のPython実装で再現します)。

波形 誤差パワー $/(\Delta^2/12)$ 実測SNR 一様モデルの予測
正弦波 1.05 73.79 dB 74.01 dB
三角波 1.81 69.67 dB 72.25 dB
のこぎり波 0.037 86.54 dB 72.25 dB

正弦波・三角波・のこぎり波それぞれの量子化誤差の時系列と、誤差パワーの比較棒グラフ

左の3段を見ると、同じ12ビット量子化器を通しているのに誤差の姿がまるで違うことがわかります。正弦波(上)の誤差は $\pm\Delta/2$ の帯をでたらめに埋めていますが、三角波(中)の誤差は $\pm 0.4$ LSB 付近を行き来する矩形波のような固定パターンになり、のこぎり波(下)にいたっては誤差がほぼ一定値に張り付いて全く動きません。右の棒グラフはその結果で、破線(一様分布モデルの予測 $=1.0$)に対して三角波は上に、のこぎり波は下に大きく外れています。

正弦波はモデルどおりです。三角波は誤差が1.8倍に膨らみ、のこぎり波にいたっては誤差パワーがモデルの27分の1しかありません。のこぎり波は一定の傾きで直線的に上がる波形なので、サンプル点は量子化格子に対して規則正しく並び、誤差が特定の値の周辺に集中します。たまたま格子とよく合ったのです。逆に言えば、少し条件を変えれば誤差は跳ね上がります。

つまり $\Delta^2/12$ という値は「典型的にはそうなる」目安であって、どんな入力に対しても成り立つ普遍法則ではありません。ADCの性能を測るときに正弦波を使い、しかも周波数を慎重に選ぶのは、この統計モデルが成り立つ土俵に持ち込むためです。

以上の但し書きを踏まえた上で、モデルが成り立つ場合の誤差パワーを計算しましょう。

量子化雑音パワー $\Delta^2/12$ の導出

一様分布の確率密度関数と、e^2 p(e) の面積が Δ^2/12 になることを示す2枚組

これから行う計算を先に絵にしておきます。左が仮定する確率密度で、幅 $\Delta$・高さ $1/\Delta$ の長方形です。面積が1になっているので確率密度として正しく、原点対称なので平均はゼロ、つまり量子化誤差には直流オフセットが乗りません。右は $e^2 p(e)$ のグラフで、この赤い領域の面積が求めたい雑音パワーです。中央付近では $e^2$ が小さいのでほとんど寄与せず、両端の $|e| \approx \Delta/2$ の付近が面積の大半を稼いでいることが見て取れます。

$e$ が $[-\Delta/2, \Delta/2]$ の一様分布に従うとします。一様分布の確率密度関数は、区間の幅の逆数で一定です。

$$ p(e) = \begin{cases} \dfrac{1}{\Delta} & -\dfrac{\Delta}{2} \le e \le \dfrac{\Delta}{2} \\[6pt] 0 & \text{それ以外} \end{cases} $$

区間の幅は $\Delta$ なので、全区間で積分すると $\Delta \times (1/\Delta) = 1$ になり、確率密度として正しく規格化されています。

まず平均を確認します。被積分関数 $e \cdot p(e)$ は原点対称な区間上の奇関数なので、積分はゼロです。

$$ \mathbb{E}[e] = \int_{-\Delta/2}^{\Delta/2} e \cdot \frac{1}{\Delta}\, de = \frac{1}{\Delta}\left[\frac{e^2}{2}\right]_{-\Delta/2}^{\Delta/2} = \frac{1}{\Delta}\left(\frac{\Delta^2}{8} – \frac{\Delta^2}{8}\right) = 0 $$

平均がゼロということは、量子化誤差には直流オフセットが乗らない、つまり「平均的には正しい値を出す」ということです。ここは重要で、後で述べる平均化・オーバーサンプリングによる精度向上は、この不偏性があってはじめて意味を持ちます。

次に分散(=平均がゼロなので二乗平均そのもの)を求めます。これが「雑音パワー」です。定義どおり $e^2$ に密度を掛けて積分します。

$$ \sigma_e^2 = \mathbb{E}[e^2] = \int_{-\Delta/2}^{\Delta/2} e^2 \cdot \frac{1}{\Delta}\, de $$

$1/\Delta$ は定数なので積分の外に出せます。

$$ \sigma_e^2 = \frac{1}{\Delta}\int_{-\Delta/2}^{\Delta/2} e^2 \, de $$

$e^2$ の原始関数は $e^3/3$ です。これを代入して上端と下端の差を取ると

$$ \sigma_e^2 = \frac{1}{\Delta}\left[\frac{e^3}{3}\right]_{-\Delta/2}^{\Delta/2} = \frac{1}{\Delta}\left(\frac{1}{3}\cdot\frac{\Delta^3}{8} – \frac{1}{3}\cdot\left(-\frac{\Delta^3}{8}\right)\right) $$

括弧の中は同じ量の足し算になるので、$2 \times \Delta^3/24 = \Delta^3/12$ です。これを $\Delta$ で割って

$$ \begin{equation} \sigma_e^2 = \frac{1}{\Delta}\cdot\frac{\Delta^3}{12} = \frac{\Delta^2}{12} \end{equation} $$

が得られます。分母の12は、一様分布の分散公式 $\sigma^2 = (b-a)^2/12$ の12そのものです。幅 $\Delta$ の一様分布の分散が $\Delta^2/12$ である、というだけの話でした。

実効値(RMS)は平方根を取って

$$ e_{rms} = \frac{\Delta}{\sqrt{12}} \approx 0.2887\,\Delta $$

です。数値を入れてみましょう。$V_{FS} = 2\ \mathrm{V}_{pp}$ の12ビットADCなら $\Delta = 488.3\ \mu\mathrm{V}$ なので $e_{rms} = 141.0\ \mu\mathrm{V}$、16ビットなら $\Delta = 30.5\ \mu\mathrm{V}$ で $e_{rms} = 8.81\ \mu\mathrm{V}$ です。16ビットADCの量子化雑音は10 $\mu$V を切っており、この時点で基板の熱雑音やグラウンドノイズと同じオーダーになります。「ビット数を増やせばいくらでも精度が上がる」わけではない理由の片鱗がここに見えます。

雑音パワーが求まったので、あとは信号パワーと比を取るだけです。

SNR = 6.02N + 1.76 dB の導出

SNRは信号パワーと雑音パワーの比です。雑音パワーは前節で $\Delta^2/12$ と決まりました。あとは「信号パワー」をどう決めるかですが、ここが $1.76$ という数字の出どころになります。ADCの性能を最大限に発揮させる入力、つまりフルスケールいっぱいに振れる正弦波を基準にする、というのが業界の約束です。

$N$ ビットのADCのフルスケール範囲は $V_{FS} = 2^N \Delta$ で、その中央を中心に $\pm V_{FS}/2$ まで振れます。したがって基準となる正弦波の振幅は

$$ A = \frac{V_{FS}}{2} = \frac{2^N \Delta}{2} = 2^{N-1}\Delta $$

です。振幅 $A$ の正弦波 $x(t) = A\sin(2\pi f t)$ のパワー(時間平均二乗値)は、$\sin^2$ の平均が $1/2$ であることから

$$ P_s = \overline{x^2} = A^2 \overline{\sin^2(2\pi f t)} = \frac{A^2}{2} $$

です。ここに $A = 2^{N-1}\Delta$ を代入します。$(2^{N-1})^2 = 2^{2N-2} = 2^{2N}/4$ を使うと

$$ \begin{equation} P_s = \frac{(2^{N-1}\Delta)^2}{2} = \frac{2^{2N}\Delta^2}{4 \cdot 2} = \frac{2^{2N}\Delta^2}{8} = \frac{(2^N\Delta)^2}{8} \end{equation} $$

分母の8は「振幅の半分($A = V_{FS}/2$)の二乗で4分の1、正弦波の実効値で更に2分の1」の積です。

これで信号パワーと雑音パワーが揃いました。比を取ります。分数の割り算なので、分母を逆さまにして掛けます。

$$ \mathrm{SNR} = \frac{P_s}{\sigma_e^2} = \frac{2^{2N}\Delta^2/8}{\Delta^2/12} = \frac{2^{2N}\Delta^2}{8}\cdot\frac{12}{\Delta^2} $$

$\Delta^2$ が約分で消えます。ここが本質的なポイントで、SNRは $\Delta$ の絶対値(=ADCの電圧レンジ)に依存しないということです。1 V振れるADCでも1 mV振れるADCでも、ビット数が同じならSNRは同じになります。

$$ \begin{equation} \mathrm{SNR} = \frac{12}{8}\cdot 2^{2N} = \frac{3}{2}\cdot 2^{2N} \end{equation} $$

これを対数(デシベル)で表します。パワー比なので $10\log_{10}$ です。積は対数の和になるので、2つの項に分かれます。

$$ \mathrm{SNR_{dB}} = 10\log_{10}\left(\frac{3}{2}\cdot 2^{2N}\right) = 10\log_{10}\frac{3}{2} + 10\log_{10} 2^{2N} $$

第2項は指数を前に出して $10 \cdot 2N \log_{10} 2 = 20N\log_{10}2$ です。数値を入れると $\log_{10}2 = 0.30103$ なので $20\log_{10}2 = 6.0206$。第1項は $10\log_{10}1.5 = 1.7609$。したがって

$$ \begin{equation} \boxed{\ \mathrm{SNR_{dB}} = 6.02\,N + 1.76 \ [\mathrm{dB}]\ } \end{equation} $$

これがデータシートの式の正体です。2つの数字の意味をあらためて確認しておきましょう。

  • $6.02 = 20\log_{10}2$ — 1ビット増えると $\Delta$ が半分になり、雑音の実効値も半分になります。振幅比が $1/2$ なのでデシベルでは $20\log_{10}2 = 6.02$ dB。「1ビット = 6 dB」という有名な換算はここから来ます。
  • $1.76 = 10\log_{10}(3/2)$ — フルスケール正弦波の実効値 $V_{FS}/(2\sqrt{2})$ と、量子化雑音の実効値 $\Delta/\sqrt{12}$ の比に由来する、波形依存の定数です。正弦波を使う限り常に1.76ですが、波形が変わればこの数字は変わります。

数値例を挙げておきます。$N = 8$ で $49.9$ dB、$N = 12$ で $74.0$ dB、$N = 16$ で $98.1$ dB、$N = 24$ で $146.2$ dB。冒頭のCDの「約96 dB」は、実は98.1 dBを丸めた(あるいはディザを入れた実効値を言った)ものだったわけです。

波形とレベルが変わったらどうなるか

$1.76$ が正弦波専用の定数だという点は、実務で頻繁に効いてきます。一般の波形に対する式を作っておきましょう。実効値 $x_{rms}$ の信号を $N$ ビットで量子化すると

$$ \mathrm{SNR_{dB}} = 10\log_{10}\frac{x_{rms}^2}{\Delta^2/12} = 20\log_{10}\frac{x_{rms}}{\Delta} + 10\log_{10}12 $$

ここで $\Delta = V_{FS}/2^N$ を代入します。$1/\Delta = 2^N/V_{FS}$ なので

$$ \begin{equation} \mathrm{SNR_{dB}} = 6.02\,N + 10.79 + 20\log_{10}\frac{x_{rms}}{V_{FS}} \end{equation} $$

$10\log_{10}12 = 10.79$ です。最後の項は「実効値がフルスケール範囲の何分の1か」を表しており、これは波高率(クレストファクタ) の逆数の対数にほかなりません。代表的な波形について計算すると次のようになります。

入力 $x_{rms}/V_{FS}$ 補正項 SNR
フルスケール正弦波 $1/(2\sqrt{2})$ $-9.03$ dB $6.02N + 1.76$
フルスケール三角波 $1/(2\sqrt{3})$ $-10.79$ dB $6.02N + 0.00$
ガウス雑音($4\sigma$ でフルスケール) $1/8$ $-18.06$ dB $6.02N – 7.27$
$-6$ dBFS 正弦波 $1/(4\sqrt{2})$ $-15.05$ dB $6.02N – 4.26$

波形ごとの補正項を示す棒グラフと、入力レベル(dBFS)に対してSNRが直線的に落ちることを示す2枚組

左の棒グラフは、$6.02N$ に足される定数が波形によってどう変わるかを表しています。基準となるフルスケール正弦波の $+1.76$ dB(破線)に対し、三角波はちょうど $0$、$-6$ dBFS の正弦波は $-4.26$、波高率4のガウス雑音は $-7.27$ dB と、9 dB もの幅で散らばります。右の図はレベル依存性で、3本の直線が傾き1で並んでいることが要点です。12ビットADCでも $-20$ dBFS で使えばSNRは $54.0$ dB しかなく、これは10ビット弱の性能でしかありません。ADCの前段にAGC(自動利得制御)を置いて信号をフルスケール近くまで振らせるのは、この直線を右端まで登るためです。

三角波では $1.76$ の項がきれいに消えて $6.02N$ ちょうどになります。ガウス雑音のように波高率の大きい信号(OFDMやマルチキャリア信号がまさにこれです)では7 dB以上損をします。無線受信機で「ADCの前段のAGCでどこまで信号を振らせるか」が設計上の勘所になるのは、この補正項を稼ぐためです。

そして最後の行が実務で一番大事です。入力振幅がフルスケールより $k$ dB小さければ、SNRも素直に $k$ dB悪化します。「12ビットADCだからSNRは74 dB」は、フルスケールで使ってはじめて言える話で、$-20$ dBFSで使えば54 dBしかありません。

ここまでで「トータルの雑音パワー」の話は終わりました。しかし実際にADCの性能を測るときは、必ずFFTでスペクトルを見ます。すると、雑音がどの周波数にどう分布しているかが問題になります。次はその話です。

量子化雑音のスペクトルと処理利得

前節までで求めたのは、雑音の総パワー $\Delta^2/12$ でした。では、そのパワーは周波数軸上でどう配られているのでしょうか。

誤差 $e[n]$ が白色(隣り合うサンプル同士が無相関)だと仮定すると、そのパワーは $0$ から $f_s/2$ までのナイキスト帯域に一様に広がります。片側パワースペクトル密度は、総パワーを帯域幅で割って

$$ \begin{equation} S_e(f) = \frac{\Delta^2/12}{f_s/2} \qquad (0 \le f \le f_s/2) \end{equation} $$

となります。この式は見た目より重要です。$f_s$ が分母にいるということは、サンプリング周波数を上げると、雑音の総量は変わらないまま、密度が薄くなるということです。この事実が次節のオーバーサンプリングの根拠になります。

白色性は本当でしょうか。実際に12ビットで大振幅の正弦波を量子化し、誤差の自己相関を測ると

$$ r[0] = 1.000,\quad r[1] = -0.018,\quad r[2] = -0.014,\quad r[3] = 0.019, \ \dots $$

とほぼ $\delta$ 関数で、白色と見なして差し支えありません。一方、振幅を3 LSBまで落とすと

$$ r[0] = 1.000,\quad r[1] = 0.223,\quad r[2] = 0.017,\quad r[3] = 0.092, \ \dots $$

と隣接サンプル間に無視できない相関が現れます。小振幅では白色仮定が崩れる、という前節の議論がここでも一貫しています。

ノイズフロアとSNRを混同しない

FFT解析で最も間違えやすいのがここです。$M$ 点のFFT(片側スペクトルは $M/2$ ビン)を取ると、総雑音パワー $\Delta^2/12$ は $M/2$ 本のビンに山分けされます。したがって1ビンあたりの雑音は総雑音より $10\log_{10}(M/2)$ dB小さく見えます。これをFFTの処理利得(プロセスゲイン) と呼びます。

$$ \begin{equation} \text{ノイズフロア}_{\mathrm{dBc}} = \mathrm{SNR_{dB}} + 10\log_{10}\frac{M}{2} \end{equation} $$

12ビットADC(SNR $= 74.0$ dB)を $M = 8192$ 点でFFTすると、処理利得は $10\log_{10}4096 = 36.1$ dBなので、画面上のノイズフロアは信号ピークから $110.1$ dBc下に見えます。実測でも $110.0$ dBcとぴたり一致します。$M$ を変えると

FFT点数 $M$ 処理利得 見かけのノイズフロア 実測
1024 27.1 dB 101.1 dBc 100.9 dBc
8192 36.1 dB 110.1 dBc 110.0 dBc
65536 45.2 dB 119.2 dBc 119.1 dBc

サンプリング周波数を上げると雑音の電力密度が薄くなる様子と、FFT点数を変えてもSNDRが変わらないことを示す2枚組

左は $S_e(f) = (\Delta^2/12)/(f_s/2)$ を絵にしたものです。青($f_s$)と橙($4f_s$)は高さが4倍違いますが、横に4倍広がっているので面積(=総雑音パワー)は同じです。右がノイズフロアと実力の違いで、$M$ を $1024 \to 65536$ と64倍にすると見かけのノイズフロア(青)は $100.9 \to 119.1$ dBc と18 dB深くなり、黒の三角(理論 $\mathrm{SNR}+10\log_{10}(M/2)$)にぴたりと乗ります。一方、赤のSNDRは $73.8, 73.9, 74.0$ dB とまったく動きません。ADCの実力を表しているのは赤のほうです。

となり、FFT点数を増やすだけでノイズフロアはいくらでも下がって見えます。しかしADCが良くなったわけではありません。SNRは74.0 dBのままです。スペクトルアナライザの画面で「ノイズフロアが低いから高性能」と判断してはいけない、というのはこの意味です。SNRを知りたければ、ビンひとつを見るのではなく、信号ビン以外の全ビンのパワーを足し上げる必要があります。

雑音の分布のしかたがわかったので、いよいよ「帯域を絞ればSNRが上がる」というオーバーサンプリングの話に進めます。

オーバーサンプリングによる処理利得

必要な信号帯域が $B$(たとえばオーディオなら20 kHz)だとします。サンプリング定理が要求する最低のサンプリング周波数は $2B$ ですが、実際には $f_s = 2B \cdot \mathrm{OSR}$ と、$\mathrm{OSR}$ 倍だけ速く回すことがよくあります。この $\mathrm{OSR}$(Oversampling Ratio、オーバーサンプリング比)を上げると何が起きるでしょうか。

直感はこうです。量子化雑音の総量はサンプリング周波数によらず $\Delta^2/12$ で一定です(1サンプルあたりの丸め誤差は $f_s$ と無関係だからです)。ところが、その一定量の雑音が広がる帯域は $f_s/2 = B \cdot \mathrm{OSR}$ と広くなります。バターを同じ量だけ、より大きなパンに塗り広げるようなものです。塗る面積が増えれば、厚みは薄くなる。そして受信側では、必要な帯域 $B$ だけをディジタルフィルタで切り出します。すると、切り出した部分に含まれる雑音は総量の $1/\mathrm{OSR}$ しかありません。信号は帯域内にすべて入っているので、SNRが $\mathrm{OSR}$ 倍改善します。

OSR=1とOSR=8の雑音電力密度を比べ、信号帯域内に残る雑音が1/OSRになることを示す2枚組

左の $\mathrm{OSR} = 1$ では雑音(赤)が信号帯域 $B$ の中にすべて詰まっていますが、右の $\mathrm{OSR} = 8$ では同じ総量が8倍の幅に広がり、高さが $1/8$ に下がっています。緑の破線がディジタルフィルタの遮断位置で、その右側の灰色部分は捨てられます。結果として帯域内に残るのは全体の $12.5\ \%$ だけ、つまり $10\log_{10}8 = 9.03$ dB の改善です。信号は帯域内にすべて入ったままなので、この分がそっくりSNRの利得になります。

式で書きましょう。帯域内の雑音パワーは、パワースペクトル密度を $0$ から $B$ まで積分して

$$ \sigma_{e,\text{in-band}}^2 = \int_0^{B} S_e(f)\, df = \frac{\Delta^2/12}{f_s/2}\cdot B $$

$f_s = 2B\cdot\mathrm{OSR}$ を代入すると $f_s/2 = B\cdot\mathrm{OSR}$ なので

$$ \sigma_{e,\text{in-band}}^2 = \frac{\Delta^2/12}{B\cdot\mathrm{OSR}}\cdot B = \frac{1}{\mathrm{OSR}}\cdot\frac{\Delta^2}{12} $$

きれいに $B$ が消えました。したがって帯域内SNRは

$$ \begin{equation} \mathrm{SNR_{dB}} = 6.02\,N + 1.76 + 10\log_{10}(\mathrm{OSR}) \end{equation} $$

となります。ここで注目すべきは、$\mathrm{OSR}$ が入る項の係数が $10\log_{10}$ である点です。ビット数の項が $20\log_{10}2 \cdot N$(振幅比)だったのに対し、こちらはパワー比なので $10\log_{10}$ です。$\mathrm{OSR}$ を2倍にすると $3.01$ dB、4倍にすると $6.02$ dB改善します。つまり

$$ \text{OSRを4倍 = 1ビット分(6.02 dB)の改善} $$

という換算になります。$\mathrm{OSR} = 256$ なら $10\log_{10}256 = 24.1$ dB、ちょうど4ビット分です。10ビットADCを256倍オーバーサンプリングすると、実効的に14ビット相当まで届く計算になります。

実測でもこの関係はよく成り立ちます。10ビット量子化器にフルスケール正弦波を入れ、32通りの初期位相で平均した結果は次のとおりです。

OSR 実測SNR 理論値
1 61.90 dB 61.96 dB $-0.06$
2 64.92 dB 64.97 dB $-0.05$
4 67.78 dB 67.98 dB $-0.20$
8 70.80 dB 70.99 dB $-0.19$
16 73.84 dB 74.00 dB $-0.16$
32 76.91 dB 77.01 dB $-0.10$
64 80.26 dB 80.02 dB $+0.24$

差はすべて0.25 dB以内で、$10\log_{10}(\mathrm{OSR})$ の法則が実際に効いていることがわかります。ちなみに位相を平均せず1回だけ測ると、高いOSRでは $\pm 2$ dB程度ばらつきます。理由は、量子化誤差が完全な白色ではなく信号の高調波にわずかなスプリアスを持つためで、そのスプリアスが狭い帯域内に落ちるか落ちないかで運不運が出るのです。ここでもやはり、モデルは「平均的には正しい」ということになります。

ただし注意すべきコストがあります。$\mathrm{OSR} = 256$ を得るには、ADCを256倍速く回さなければなりません。1ビット稼ぐのに4倍の消費電力(と4倍のデータ量)を払う勘定で、素直なオーバーサンプリングは効率が悪いのです。この効率を劇的に改善するのがノイズシェーピング、すなわち $\Delta\Sigma$ 変調です。雑音を帯域内から帯域外へ「押し出す」ことで、1次変調器なら $\mathrm{OSR}$ 倍あたり $9$ dB(1.5ビット)、2次なら $15$ dB(2.5ビット)と、桁違いに効率良く分解能を稼げます。この仕組みはAD/DA変換の記事で詳しく扱っています。

さて、ここまでは「入力が十分大きい」ことを前提にしてきました。次は、その前提が崩れる小振幅の世界を見ていきます。ここで量子化は「雑音」ではなく「歪み」の顔を見せます。

小振幅の悪夢とディザ

大振幅なら誤差は一様に散らばりました。では振幅を数LSBまで落とすとどうなるでしょうか。極端な場合を考えるのが早道です。振幅 $0.4\,\Delta$ の正弦波を量子化すると、出力は $+\Delta/2$ と $-\Delta/2$ の2値をパタパタ切り替えるだけの矩形波になります。もはや正弦波ではありません。誤差は「正弦波と矩形波の差」という、完全に信号と相関した決定論的な波形です。

振幅0.4Δの正弦波が矩形波に化ける様子、誤差が信号と相関した決定論的波形になること、振幅3LSBのスペクトルに奇数次高調波が櫛状に立つことを示す3枚組

左の図が「小振幅の悪夢」そのものです。青い入力(振幅 $0.4\Delta$)に対して、赤い出力は $+\Delta/2$ と $-\Delta/2$ をパタパタ切り替えるだけの矩形波になっており、正弦波の面影はありません。中央がその差、すなわち量子化誤差で、雑音どころか信号と同期したなめらかな決定論的波形です。相関係数も $+0.610$ と強く相関しています。右は振幅を3 LSBまで戻したときのスペクトルで、信号の $23.4$ dB 下に3次高調波が立ち、5次・7次・9次・11次と奇数次だけの櫛が並びます(偶数次は正弦波の対称性のため厳密にゼロです)。

これを周波数領域で見ると、誤差は信号の奇数次高調波として集中して現れます。実測してみましょう。12ビット量子化器に振幅3 LSBの正弦波を入れると

  • SNDR(信号対雑音歪み比): $16.97$ dB
  • SFDR(スプリアスフリーダイナミックレンジ): $23.40$ dBc(最大スプリアスは3次高調波)

信号のわずか23 dB下に3次高調波が立っています。ここまで大きなスプリアスは、通信でも計測でもオーディオでも許容できません。「小さい信号ほどSNRが悪い」だけならまだしも、離散的な線スペクトルとして出てくるのが厄介なのです。連続的な雑音なら平均化やフィルタで薄められますが、線スペクトルは薄まりません。

もう一つの症状がノイズ変調です。誤差の分散が入力レベルによって変わってしまう現象で、直流入力を0から1 LSBまで掃引しながら誤差パワーを測ると、その様子がはっきり見えます(単位は $\Delta^2/12$)。

直流入力 ディザなし RPDF TPDF
0.0 LSB 3.000 3.000 3.000
0.2 LSB 1.080 2.497 3.009
0.3 LSB 0.480 1.928 3.019
0.5 LSB 0.000 0.000 3.007
0.7 LSB 0.480 1.900 2.995
1.0 LSB 3.000 3.000 3.000

ディザなしの列を見てください。入力がちょうど量子化レベル上($0.5$ LSB)にあるときは誤差が完全にゼロ、レベルの真ん中($0$ や $1$ LSB)では誤差が最大になります。入力がわずかに動くだけで雑音の大きさがガラリと変わる。オーディオでこれが起きると、音量に応じて背景ノイズの質が変化し、耳障りな「ざらつき」として知覚されます。計測では、直流レベルによって精度が変わるという不快な非線形性になります。

ディザ — わざと雑音を足して線形化する

解決策は逆説的です。量子化する前に、わざとランダムな雑音を加えるのです。これをディザと呼びます。

なぜこれで良くなるのか。直感的には、ディザによって入力が量子化格子に対してランダムに揺さぶられるため、誤差が特定の値に固定されなくなります。誤差は入力と無相関なランダム量になり、線スペクトルは消えて平坦な雑音に変わります。「歪みを雑音に変換する」のがディザの本質です。歪みより雑音のほうがずっと扱いやすい(平均やフィルタで減らせる、耳につきにくい)ので、これは大きな進歩です。

ディザには分布の種類があります。

  • RPDF(Rectangular PDF)— $[-\Delta/2, \Delta/2]$ の一様分布。振幅1 LSB p-p。
  • TPDF(Triangular PDF)— $[-\Delta, \Delta]$ の三角分布。振幅2 LSB p-p。独立な2つのRPDFの和として作れます。

理論的な根拠はSchuchmanの条件として整理されており、要点は「ディザの特性関数が量子化周波数の整数倍でゼロになれば、誤差のモーメントが入力に依存しなくなる」ということです。RPDFは1次モーメント(平均)まで、TPDFは2次モーメント(分散)まで入力から独立にできます。上の表がまさにその通りの結果を示しています。RPDFの列は入力によって分散が $1.9$〜$3.0$ と揺れ、$0.5$ LSBではゼロになってしまうのに対し、TPDFの列は掃引した全51点で $2.95$〜$3.03$ に収まり、実質的に一定です。ノイズ変調を完全に断つにはTPDFが必要だ、というのがLipshitzらの結論です。

ではコストはどれくらいでしょうか。TPDFディザの分散は、独立な2つのRPDF(各 $\Delta^2/12$)の和なので $2\Delta^2/12$ です。これが量子化誤差 $\Delta^2/12$ に加わって、総誤差パワーは

$$ \sigma_{total}^2 = \frac{\Delta^2}{12} + 2\cdot\frac{\Delta^2}{12} = 3\cdot\frac{\Delta^2}{12} = \frac{\Delta^2}{4} $$

つまり3倍、デシベルで $10\log_{10}3 = 4.77$ dBの悪化です。実測でも誤差パワーは入力レベルによらず $2.95$〜$3.03$ 倍と、理論の3.0倍によく一致します(RPDFなら2倍、$3.01$ dBの悪化)。

トレードオフは明快です。先ほどの振幅3 LSBの例にTPDFディザを加えると

SNDR SFDR 最大スプリアス
ディザなし 16.97 dB 23.40 dBc 3次高調波
RPDF 14.02 dB 39.57 dBc 高調波でない
TPDF 12.51 dB 38.69 dBc 高調波でない

SNDRは4.5 dB悪化しますが、SFDRは15 dB以上改善し、しかも最大スプリアスの位置が高調波でなくなります(=単なる雑音のピークになった=スプリアスが消えた)。誤差と信号の相関係数も、ディザなしでは $-0.476$(振幅3 LSB時)だったものが、TPDFでは $-0.004$ とほぼゼロになります。

「SNDRが悪化するなら意味がないのでは」と思うかもしれませんが、そうではありません。ディザ後の誤差は白色雑音なので、後段でオーバーサンプリング+デシメーションをすれば $10\log_{10}(\mathrm{OSR})$ dBで素直に減らせます。ディザなしのスプリアスは、いくら平均してもそこに居座り続けます。減らせる雑音減らせない歪みの交換だと思えば、ディザは明らかに得なのです。

なお、ディザ信号をADC後にディジタル側で差し引くサブトラクティブディザという方式もあります。これができれば4.77 dBのペナルティを払わずに完全な線形化が実現しますが、アナログ側で加えた雑音とビット単位で同一の値をディジタル側で再現する必要があり、実装は簡単ではありません。多くの実機では非サブトラクティブ(引き算しない)ディザが使われます。

理論とその破れ方が一通り見えたので、次は「では実際どうやって測るのか」に進みます。

実測の作法 — コヒーレントサンプリングとFFT

ADCの性能を測る標準的な手順は、きれいな正弦波を入れてFFTを取り、スペクトルから各種指標を計算するというものです。ただし、ここには落とし穴があります。

スペクトル漏れという敵

FFTは「取り込んだ $M$ 点が周期的に無限に繰り返している」と仮定して計算します。信号の周期がこの窓長にぴったり収まっていないと、窓の端で波形が不連続になり、その不連続が全周波数に漏れ出します。これがスペクトル漏れです。

整数周期なら窓の継ぎ目がなめらかにつながり、非整数周期だと段差ができることを示す2枚組

左右とも、灰色の帯が「実際に取り込んだ $M$ 点」で、その右側はFFTが勝手に仮定している繰り返しです。左のコヒーレントな場合($M_c/M = 5/64$、窓にちょうど5周期)は、継ぎ目(黒破線)で波形がなめらかにつながっており、FFTが見ている無限長の信号は本物の正弦波と区別がつきません。右の非整数周期($5.4/64$)では継ぎ目に段差ができ、この不連続が全周波数にエネルギーをばらまきます。窓関数とは、この段差を強制的にゼロに押し下げるための重み付けにほかなりません。

どれくらい深刻か、実測で見てみましょう。12ビット(理論SNR $= 74.0$ dB)の量子化器に、窓長のなかに $331.37$ 周期入る正弦波(非整数周期)を入れてFFTすると、矩形窓(=窓なし)でのSNDRは $8.82$ dB にしかなりません。65 dBもの誤差です。量子化雑音を測っているつもりが、実際にはスペクトル漏れを測っていたわけです。

対処法は2つあります。

方法1: コヒーレントサンプリング — 窓長にちょうど整数周期入るように、信号周波数を選びます。

$$ \begin{equation} \frac{f_{in}}{f_s} = \frac{M_c}{M}, \qquad \gcd(M_c, M) = 1 \end{equation} $$

$M$ はFFT点数、$M_c$ は窓内の周期数です。$M_c$ と $M$ が互いに素であることが重要で、これを外すと同じサンプル点を何度も踏むことになり、実効的なサンプル数が減って統計が偏ります。$M = 2^k$(2のべき)を使うなら、$M_c$ は奇数にすれば自動的に互いに素になります。素数を選ぶのが安全で、$M = 8192$ に対して $M_c = 331$ のような選び方をします。

条件を満たせば窓関数は不要(矩形窓でよい)で、信号は1本のビンに完全に収まります。実際、$M_c = 331$、$M = 8192$、12ビットで測ると SNDR $= 73.92$ dB。理論値74.00 dBとの差はわずか0.08 dBです。

方法2: 窓関数 — 信号源の周波数を精密に制御できない場合は、窓関数で端の不連続をなだらかにします。ただし、どの窓でもよいわけではありません。同じ非整数周期の信号に対して各種窓で測ると

窓関数 サイドローブ 測定SNDR
矩形(窓なし) $-13$ dB 8.82 dB
ハミング $-43$ dB 32.48 dB
ハン $-31$ dB 42.84 dB
ブラックマン $-58$ dB 58.70 dB
Blackman-Harris (4項) $-92$ dB 73.92 dB
フラットトップ $-93$ dB 73.19 dB

理論値74.00 dBに到達するには、Blackman-Harrisクラスの窓が必要です。興味深いのは、サイドローブが $-43$ dBのハミング窓が、$-31$ dBのハン窓より悪いという点です。これはサイドローブの「高さ」だけでなく「減衰の速さ(ロールオフ)」が効くためで、ハン窓は $-18$ dB/oct で急速に落ちるのに対し、ハミング窓は $-6$ dB/oct とゆっくりしか落ちません。遠方のサイドローブの積算が効くADC測定では、ロールオフの速い窓が有利になります。窓関数のこうした性質は窓関数の記事スペクトル漏れと窓選択の記事で詳しく解説しています。

要するに「測定値が理論より悪いとき、それがADCのせいなのか測定のせいなのか」を切り分ける必要がある、ということです。コヒーレントサンプリングが使えるなら、それが最も確実です。

性能指標の定義

FFTスペクトルが正しく得られたら、次の指標を計算します。$P_{sig}$ を信号ビンのパワー、$P_{h,k}$ を第 $k$ 次高調波のパワー、$P_{noise}$ を信号・DC・高調波を除く全ビンのパワー和とします。

SNR(信号対雑音比)— 高調波を除いた雑音だけとの比。

$$ \mathrm{SNR} = 10\log_{10}\frac{P_{sig}}{P_{noise}} $$

THD(全高調波歪み)— 高調波成分の合計との比。通常は2〜10次程度まで数えます。

$$ \mathrm{THD} = 10\log_{10}\frac{\sum_{k=2}^{K} P_{h,k}}{P_{sig}} $$

SNDR / SINAD(信号対雑音歪み比)— 信号以外のすべて(雑音+高調波)との比。実用上いちばん重要な指標です。

$$ \begin{equation} \mathrm{SNDR} = 10\log_{10}\frac{P_{sig}}{P_{noise} + \sum_{k\ge2} P_{h,k}} \end{equation} $$

ENOB(有効ビット数)— SNDRを $6.02N + 1.76$ の式に逆代入して得られる「実効的なビット数」。

$$ \begin{equation} \mathrm{ENOB} = \frac{\mathrm{SNDR_{dB}} – 1.76}{6.02} \end{equation} $$

SFDR(スプリアスフリーダイナミックレンジ)— 信号と、それ以外の最大の単一スペクトル線との比。

$$ \mathrm{SFDR} = 10\log_{10}\frac{P_{sig}}{\max_{k \ne sig} P_k} $$

ENOBの意味を押さえておきましょう。ENOBは「そのADCが理想的な何ビットADCと同じ性能か」を表します。12ビットADCのSNDRが $68.5$ dBなら $\mathrm{ENOB} = (68.5-1.76)/6.02 = 11.09$ ビット。カタログの12ビットに対して0.9ビット分の目減りです。「ビット数」はパッケージのラベル、ENOBは中身、と考えるとよいでしょう。

SNDRがSNRと分けて定義されているのは、雑音と歪みが本質的に違うものだからです。たとえばSNRが $74$ dBで歪みが $-80$ dBcのADCのSNDRは

$$ \mathrm{SNDR} = -10\log_{10}\left(10^{-74/10} + 10^{-80/10}\right) = 73.03\ \mathrm{dB} $$

と $1$ dB弱の劣化で済みますが、歪みが $-70$ dBcまで悪化すると

$$ \mathrm{SNDR} = -10\log_{10}\left(10^{-74.0/10} + 10^{-70/10}\right) = 68.54\ \mathrm{dB} $$

と $5.5$ dB(ENOBで0.9ビット)も落ちます。歪みが支配的になると、いくら雑音を減らしても意味がありません。SNRとSFDRを両方見る必要があるのはこのためです。

指標の定義が固まったので、実際にPythonで量子化器を作り、これらを測定してみましょう。

Pythonでの実装

量子化器と誤差の分布

まず量子化器を実装し、誤差の波形と分布を確認します。$\Delta^2/12$ という理論値が実測と合うかを見るのが目的です。

import numpy as np
import matplotlib, matplotlib.pyplot as plt

for cand in ["Hiragino Sans", "Yu Gothic", "Noto Sans CJK JP", "IPAexGothic", "Meiryo"]:
    if any(cand == f.name for f in matplotlib.font_manager.fontManager.ttflist):
        plt.rcParams["font.family"] = cand
        break
plt.rcParams["axes.unicode_minus"] = False

FS = 2.0  # フルスケール範囲 [V] (= ±1V)

def quantize(x, N, FS=2.0):
    """ミッドライズ型 N ビット量子化器。戻り値: (量子化出力, ステップ幅Δ)"""
    delta = FS / (2 ** N)
    q = delta * (np.floor(x / delta) + 0.5)          # 式(2)
    vmax = FS / 2 - delta / 2                        # 最上位レベル
    return np.clip(q, -vmax, vmax), delta

# 見やすさのため 4 ビットで階段状の入出力特性を描く
x = np.linspace(-1, 1, 4001)
q4, d4 = quantize(x, 4, FS)

fig, ax = plt.subplots(1, 2, figsize=(12, 4.5))
ax[0].plot(x, x, "k--", lw=1, label="理想(誤差ゼロ)")
ax[0].step(x, q4, where="mid", lw=1.8, label="4ビット量子化出力")
ax[0].set_xlabel("入力電圧 [V]"); ax[0].set_ylabel("出力電圧 [V]")
ax[0].set_title("量子化器の入出力特性(4ビット)"); ax[0].legend(); ax[0].grid(alpha=0.3)

ax[1].plot(x, (q4 - x) / d4, lw=1.2)
ax[1].axhline(0.5, color="r", ls=":", label="$\\pm\\Delta/2$ の境界")
ax[1].axhline(-0.5, color="r", ls=":")
ax[1].set_xlabel("入力電圧 [V]"); ax[1].set_ylabel("量子化誤差 [LSB]")
ax[1].set_title("量子化誤差はのこぎり波(決定論的)"); ax[1].legend(); ax[1].grid(alpha=0.3)
plt.tight_layout(); plt.show()

左のグラフから、量子化が「階段で直線を近似する」処理であることが一目でわかります。右のグラフが本質的で、量子化誤差は入力に対するのこぎり波、つまり完全に決定論的で周期的な関数です。ランダムな要素はどこにもありません。それでも $-\Delta/2$ から $+\Delta/2$ を隈なく往復しているので、入力が格子に対して不規則に動けば、誤差の出現頻度は一様分布に近づく — これが「雑音とみなす」根拠を視覚化したものです。

なお、以降のコードブロックはこの quantize()(および後で定義する analyze())をそのまま使い回します。順に実行していけば動くようにしてあります。

次に、その分布を実際にヒストグラムで確かめます。

import numpy as np
import matplotlib.pyplot as plt

FS, N = 2.0, 12
NFFT, Mc = 8192, 331          # コヒーレント条件: 331 は奇素数
n = np.arange(NFFT)
A = FS / 2                    # フルスケール振幅
x_big = A * np.sin(2 * np.pi * Mc * n / NFFT + 0.1234)
xq, delta = quantize(x_big, N, FS)
e_big = xq - x_big

# 小振幅(3 LSB)との比較
x_sml = 3 * delta * np.sin(2 * np.pi * Mc * n / NFFT + 0.1234)
e_sml = quantize(x_sml, N, FS)[0] - x_sml

print(f"Δ = {delta*1e6:.1f} μV,  Δ/√12 = {delta/np.sqrt(12)*1e6:.1f} μV")
print(f"大振幅: 誤差パワー/(Δ²/12) = {np.mean(e_big**2)/(delta**2/12):.3f}")
print(f"小振幅: 誤差パワー/(Δ²/12) = {np.mean(e_sml**2)/(delta**2/12):.3f}")
print(f"大振幅 corr(x,e) = {np.corrcoef(x_big, e_big)[0,1]:+.4f}")
print(f"小振幅 corr(x,e) = {np.corrcoef(x_sml, e_sml)[0,1]:+.4f}")

fig, ax = plt.subplots(1, 2, figsize=(12, 4.2))
for a, e, ttl in [(ax[0], e_big, "大振幅(フルスケール)"), (ax[1], e_sml, "小振幅(3 LSB)")]:
    a.hist(e / delta, bins=60, range=(-0.5, 0.5), density=True,
           color="steelblue", edgecolor="w")
    a.axhline(1.0, color="r", ls="--", lw=2, label="一様分布の理論値 $1/\\Delta$")
    a.set_xlabel("量子化誤差 [LSB]"); a.set_ylabel("確率密度 [1/LSB]")
    a.set_title(f"誤差のヒストグラム: {ttl}"); a.legend(); a.grid(alpha=0.3)
plt.tight_layout(); plt.show()

大振幅と小振幅の量子化誤差ヒストグラム、および小振幅では出力が数段を往復するだけになる様子を示す3枚組

出力は $\Delta = 488.3\ \mu\mathrm{V}$、$\Delta/\sqrt{12} = 141.0\ \mu\mathrm{V}$、大振幅の誤差パワー比 $1.022$、小振幅で $1.211$、相関係数は大振幅 $-0.022$ に対し小振幅 $-0.476$ となります。ヒストグラムを見ると、大振幅では誤差が $[-\Delta/2, \Delta/2]$ にほぼ平坦に分布し、赤い破線(理論値 $1/\Delta$)とよく一致します。一方、小振幅では分布に凹凸が現れ、平坦から明らかにずれています。相関係数にいたっては20倍以上の開きがあり、大振幅では誤差が信号と無相関な雑音、小振幅では信号に紐づいた歪みという性格の違いがはっきり出ています。右端の時間波形を見ると、振幅3 LSBの正弦波に対して出力がわずか6段ほどの階段を往復しているだけだとわかり、この「段の粗さ」が歪みの正体であることが直感的につかめます。

SNRとビット数の関係を実測する

いよいよ $6.02N + 1.76$ を検証します。ビット数を変えながら時間領域でSNRを測り、理論直線と重ねます。

import numpy as np
import matplotlib.pyplot as plt

FS, NFFT, Mc = 2.0, 8192, 331
n = np.arange(NFFT); A = FS / 2
x = A * np.sin(2 * np.pi * Mc * n / NFFT + 0.1234)

bits = np.arange(4, 19)
snr_meas = []
for N in bits:
    xq, d = quantize(x, N, FS)
    e = xq - x
    snr_meas.append(10 * np.log10(np.mean(x**2) / np.mean(e**2)))
snr_meas = np.array(snr_meas)
snr_theo = 6.02 * bits + 1.76

for N, m, t in zip(bits[::2], snr_meas[::2], snr_theo[::2]):
    print(f"N={N:2d}  実測 {m:6.2f} dB / 理論 {t:6.2f} dB  (差 {m-t:+.2f} dB)")

plt.figure(figsize=(8, 5))
plt.plot(bits, snr_theo, "k--", lw=2, label="理論 $6.02N + 1.76$ dB")
plt.plot(bits, snr_meas, "o-", color="crimson", label="実測(フルスケール正弦波)")
plt.xlabel("ビット数 $N$"); plt.ylabel("SNR [dB]")
plt.title("量子化SNRとビット数の関係(1ビット = 6.02 dB)")
plt.legend(); plt.grid(alpha=0.3); plt.tight_layout(); plt.show()

ビット数4〜18に対する実測SNRと理論直線6.02N+1.76の重ね描き、および理論からのずれの棒グラフ

実測値は理論直線とほぼ重なります。差は $N \ge 8$ で $0.15$ dB以内、$N = 4$ という極端に粗い量子化でも $0.53$ dBです。低ビット側でわずかに理論より悪いのは、粗い格子では誤差と信号の相関が残り一様分布の仮定が緩むためで、ここでもモデルの適用範囲が見えます。それでも傾きが $6.02$ dB/bit であること、切片が $1.76$ dB であることは目で確認できます。ここで大事なのは、これが「たまたま合った」のではなく、フルスケール正弦波というモデルの仮定が満たされる条件で測ったから合っている、という点です。次のコードでその裏返しを見ます。

import numpy as np
from scipy import signal as sg

FS, N = 2.0, 12
NFFT = 8192 * 8; n = np.arange(NFFT); Mc = 331 * 7 + 3
ph = 0.1234
waves = {
    "正弦波":    np.sin(2 * np.pi * Mc * n / NFFT + ph),
    "三角波":    sg.sawtooth(2 * np.pi * Mc * n / NFFT + ph, 0.5),
    "のこぎり波": sg.sawtooth(2 * np.pi * Mc * n / NFFT + ph),
}
print("波形       誤差パワー/(Δ²/12)   実測SNR   一様モデルの予測")
for name, x in waves.items():
    xq, d = quantize(x, N, FS)
    e = xq - x
    ratio = np.mean(e**2) / (d**2 / 12)
    snr = 10 * np.log10(np.mean(x**2) / np.mean(e**2))
    pred = 6.0206 * N + (1.76 if name == "正弦波" else 0.0)
    print(f"{name:8s}  {ratio:8.3f}          {snr:6.2f} dB   {pred:6.2f} dB")

出力は正弦波が $1.053$ 倍(実測73.79 dB / 予測74.01 dB)、三角波が $1.812$ 倍(69.67 dB / 72.25 dB)、のこぎり波が $0.037$ 倍(86.54 dB / 72.25 dB)です。三角波では誤差が予測の1.8倍に膨らみ、のこぎり波では逆に27分の1しかありません。のこぎり波は一定の傾きで直線的に変化するため、サンプル点が量子化格子と規則正しい関係になり、誤差が偶然ほぼ打ち消し合ったのです。$\Delta^2/12$ は普遍法則ではなく、条件付きの近似であることが、これで具体的に確認できました。

FFTによるSNDR・ENOB・SFDRの測定

ここからは、実機のADC評価と同じ手順を再現します。コヒーレントサンプリングした正弦波をFFTし、スペクトルから指標を計算します。

import numpy as np

def analyze(xq, Mc, n_harm=10):
    """コヒーレントサンプリング前提のスペクトル解析(矩形窓)"""
    M = len(xq)
    P = np.abs(np.fft.rfft(xq)) ** 2
    P[0] = 0.0                              # DC を除去
    Psig = P[Mc]
    # 高調波(エイリアシング折り返しを考慮)
    hbins = []
    for k in range(2, n_harm + 1):
        b = (k * Mc) % M
        if b > M // 2:
            b = M - b                       # 折り返し
        if 0 < b <= M // 2:
            hbins.append(b)
    hbins = sorted(set(hbins) - {Mc})
    Ph = P[hbins].sum()
    Pn = P.sum() - Psig - Ph                # 雑音(信号・DC・高調波を除く)
    mask = np.ones_like(P, bool); mask[[0, Mc]] = False   # SFDR は高調波も対象
    spur = int(np.argmax(np.where(mask, P, 0.0)))
    return dict(SNR=10*np.log10(Psig/Pn), THD=10*np.log10(Ph/Psig),
                SNDR=10*np.log10(Psig/(Pn+Ph)),
                ENOB=(10*np.log10(Psig/(Pn+Ph))-1.76)/6.02,
                SFDR=10*np.log10(Psig/P[spur]), P=P, Psig=Psig)

FS, NFFT, Mc = 2.0, 8192, 331
n = np.arange(NFFT); A = FS / 2
for N in [8, 10, 12, 14, 16]:
    xq, d = quantize(A*np.sin(2*np.pi*Mc*n/NFFT + 0.1234), N, FS)
    r = analyze(xq, Mc)
    print(f"N={N:2d}  理論SNR={6.02*N+1.76:6.2f}  SNDR={r['SNDR']:6.2f}  "
          f"ENOB={r['ENOB']:5.2f}  SFDR={r['SFDR']:6.2f} dBc")

出力は $N=8$ でSNDR $49.82$ dB / ENOB $7.98$、$N=12$ で $73.92$ dB / $11.99$、$N=16$ で $98.07$ dB / $16.00$ となります。ENOBが公称ビット数とほぼ一致するのは、この量子化器が理想的(INL/DNL誤差ゼロ、ジッタゼロ)だからです。実機ではここが必ず目減りします。SFDRは $N=12$ で $97.98$ dBc、SNDRより24 dBも良い値です。これは量子化雑音が多数のビンに薄く散らばっているため、単一のビンの高さはSNDRよりずっと低くなるからで、SFDRとSNDRを混同してはいけない理由でもあります。

スペクトルを描いてみましょう。

import numpy as np
import matplotlib.pyplot as plt

FS, NFFT, Mc = 2.0, 8192, 331
n = np.arange(NFFT); A = FS / 2
fig, ax = plt.subplots(1, 2, figsize=(13, 4.5))
for a, N in zip(ax, [8, 12]):
    xq, d = quantize(A*np.sin(2*np.pi*Mc*n/NFFT + 0.1234), N, FS)
    r = analyze(xq, Mc)
    dbc = 10*np.log10(np.maximum(r["P"], 1e-30) / r["Psig"])
    f = np.arange(len(dbc)) / NFFT           # 正規化周波数 f/fs
    a.plot(f, dbc, lw=0.7, color="steelblue")
    a.axhline(-(6.02*N+1.76+10*np.log10(NFFT/2)), color="r", ls="--", lw=1.5,
              label="理論ノイズフロア(処理利得込み)")
    a.set_ylim(-150, 5); a.set_xlim(0, 0.5)
    a.set_xlabel("正規化周波数 $f/f_s$"); a.set_ylabel("信号からの相対レベル [dBc]")
    a.set_title(f"{N}ビット量子化のスペクトル\nSNDR={r['SNDR']:.1f} dB, "
                f"ENOB={r['ENOB']:.2f}, SFDR={r['SFDR']:.1f} dBc")
    a.legend(fontsize=9); a.grid(alpha=0.3)
plt.tight_layout(); plt.show()

8ビットと12ビットの量子化スペクトル、理論ノイズフロアと最大スプリアスの位置

2つのスペクトルを比べると、8ビットではノイズフロアが $-86$ dBc付近、12ビットでは $-110$ dBc付近にあり、ちょうど4ビット分($24$ dB)下がっています。赤い破線($\mathrm{SNR} + 10\log_{10}(M/2)$ の理論ノイズフロア)とよく一致しており、処理利得の式が正しいことが確認できます。また8ビットのスペクトルには、平均フロア($-85.9$ dBc)から $18.9$ dBも突き出たスプリアスが点在します(最大スプリアスは $-67.1$ dBc)。これが量子化誤差のうち信号と相関した成分で、ビット数が少ないほど相関が強くなるため目立つようになります。

FFT点数を変えるとノイズフロアだけが動く

「ノイズフロアはSNRではない」という前節の主張を確かめます。

import numpy as np

FS, N = 2.0, 12
print("FFT点数   平均ノイズフロア   理論(SNR+10log10(M/2))   SNDR")
for M, mc in [(1024, 41), (8192, 331), (65536, 2647)]:
    n = np.arange(M)
    xq, d = quantize(1.0*np.sin(2*np.pi*mc*n/M + 0.1234), N, FS)
    P = np.abs(np.fft.rfft(xq))**2; P[0] = 0
    Psig = P[mc]
    mask = np.ones_like(P, bool); mask[[0, mc]] = False
    floor = 10*np.log10(Psig / P[mask].mean())
    sndr = 10*np.log10(Psig / (P.sum() - Psig))
    print(f"{M:7d}   {floor:8.2f} dBc   {74.0+10*np.log10(M/2):12.2f} dBc   {sndr:6.2f} dB")

出力は $M=1024$ で $100.86$ dBc、$M=8192$ で $110.04$ dBc、$M=65536$ で $119.13$ dBcとなり、FFT点数を64倍にするとノイズフロアは18 dB下がって見えます。しかし右端のSNDRは $73.78, 73.92, 73.97$ dBと、理論値74.0 dBのまわりでほとんど変わりません。ノイズフロアの高さは測定条件(FFT点数)の関数であって、ADCの性能ではないことがこれではっきりします。データシートの「Noise Floor」を比較するときは、必ずFFT点数を揃えなければなりません。

ディザの効果

小振幅信号のスプリアスがディザで消える様子を見ます。

import numpy as np
import matplotlib.pyplot as plt

FS, N, NFFT, Mc = 2.0, 12, 8192, 331
n = np.arange(NFFT); delta = FS / 2**N
x = 3 * delta * np.sin(2 * np.pi * Mc * n / NFFT + 0.1234)   # 振幅 3 LSB
rng = np.random.default_rng(1)
rpdf = rng.uniform(-0.5, 0.5, NFFT) * delta                   # 1 LSB p-p
tpdf = (rng.uniform(-0.5, 0.5, NFFT) + rng.uniform(-0.5, 0.5, NFFT)) * delta  # 2 LSB p-p

cases = [("ディザなし", np.zeros(NFFT)), ("RPDF(1 LSB p-p)", rpdf),
         ("TPDF(2 LSB p-p)", tpdf)]
fig, ax = plt.subplots(1, 3, figsize=(15, 4.2), sharey=True)
for a, (name, dth) in zip(ax, cases):
    xq, _ = quantize(x + dth, N, FS)
    e = xq - x
    r = analyze(xq, Mc)
    dbc = 10*np.log10(np.maximum(r["P"], 1e-30) / r["Psig"])
    a.plot(np.arange(len(dbc))/NFFT, dbc, lw=0.7)
    a.set_ylim(-90, 5); a.set_xlim(0, 0.5)
    a.set_xlabel("正規化周波数 $f/f_s$")
    a.set_title(f"{name}\nSNDR={r['SNDR']:.1f} dB, SFDR={r['SFDR']:.1f} dBc\n"
                f"誤差パワー={np.mean(e**2)/(delta**2/12):.2f}×(Δ²/12)")
    a.grid(alpha=0.3)
ax[0].set_ylabel("信号からの相対レベル [dBc]")
plt.tight_layout(); plt.show()

ディザなし・RPDF・TPDFの3条件でのスペクトル比較。高調波の櫛が消えて平坦な雑音に変わる様子

左のディザなしのスペクトルには、信号の $23.4$ dB下に3次高調波が鋭く立ち、5次($-26.8$ dBc)、9次($-32.6$ dBc)、11次($-33.6$ dBc)と奇数次だけの櫛が並びます(偶数次は正弦波の対称性のため厳密にゼロです)。RPDF・TPDFを加えるとこれらの線がきれいに消え、最大スプリアスの位置も高調波ではない($7.4$ 次・$10.4$ 次という中途半端な場所の)単なる雑音のピークに変わります。数値では SFDR が $23.4 \to 39.6$ dBc(RPDF)/$38.7$ dBc(TPDF)と15 dB以上改善する一方、SNDR は $17.0 \to 14.0 \to 12.5$ dB と悪化し、誤差パワーは $1.21 \to 2.14 \to 3.02$ 倍($3.01$ dB/$4.77$ dB)に増えます。相関係数も $-0.476 \to -0.002 \to -0.004$ とほぼゼロになります。歪みを雑音に交換するというディザの本質が、この3枚のスペクトルに凝縮されています。

TPDFがRPDFより優れる点、すなわちノイズ変調の除去を確認します。

import numpy as np
import matplotlib.pyplot as plt

FS, N, L = 2.0, 12, 40000
delta = FS / 2**N
rng = np.random.default_rng(11)
dcs = np.linspace(0, 1.0, 51)
res = {"ディザなし": [], "RPDF": [], "TPDF": []}
for dc in dcs:
    x = np.full(L, dc * delta)
    u = rng.uniform(-0.5, 0.5, L) * delta
    t = (rng.uniform(-0.5, 0.5, L) + rng.uniform(-0.5, 0.5, L)) * delta
    for key, dth in [("ディザなし", 0.0), ("RPDF", u), ("TPDF", t)]:
        e = quantize(x + dth, N, FS)[0] - x
        res[key].append(np.mean(e**2) / (delta**2 / 12))

plt.figure(figsize=(9, 5))
for key, style in [("ディザなし", "o-"), ("RPDF", "s-"), ("TPDF", "^-")]:
    plt.plot(dcs, res[key], style, ms=3, lw=1.5, label=key)
plt.axhline(3.0, color="k", ls=":", label="TPDF の理論値 $3\\times\\Delta^2/12$")
plt.xlabel("直流入力レベル [LSB]"); plt.ylabel("誤差パワー / $(\\Delta^2/12)$")
plt.title("ノイズ変調: 入力レベルによる雑音量の変動")
plt.legend(); plt.grid(alpha=0.3); plt.tight_layout(); plt.show()

直流入力レベルを0〜1LSBで掃引したときの誤差パワー。ディザなしとRPDFは大きく変動するがTPDFだけが平坦

ディザなしの曲線は、入力が量子化レベル上($0.5$ LSB)でゼロ、レベル中間($0$ と $1$ LSB)で $3$ と、入力レベルに応じて雑音量が激しく上下します。RPDFでも $0.5$ LSBでゼロに落ち込むディップが残っています。これに対しTPDFの曲線だけが $3.0$ で完全に平坦です。これがLipshitzらの結論「2次モーメントまで入力から独立にするにはTPDFが必要」の実証です。オーディオのビット落とし処理でTPDFディザが標準的に使われるのは、この平坦さ(=背景ノイズの質が音量によって変わらないこと)のためです。

オーバーサンプリングによるSNR改善

最後に $10\log_{10}(\mathrm{OSR})$ の法則を確かめます。前述のとおり単発測定はばらつくので、初期位相を変えて平均します。

import numpy as np
import matplotlib.pyplot as plt

FS, N = 2.0, 10
NF, mc = 2**15, 97
nn = np.arange(NF); A = FS / 2
osrs = np.array([1, 2, 4, 8, 16, 32, 64])
rng = np.random.default_rng(5)
acc = {o: [] for o in osrs}
for _ in range(32):                       # 32通りの初期位相で平均
    ph = rng.uniform(0, 2*np.pi)
    xq, d = quantize(A*np.sin(2*np.pi*mc*nn/NF + ph), N, FS)
    P = np.abs(np.fft.rfft(xq))**2; P[0] = 0
    Psig = P[mc]
    for o in osrs:
        band = P[:int((NF//2)/o) + 1].copy(); band[mc] = 0
        acc[o].append(Psig / band.sum())
meas = np.array([10*np.log10(np.mean(acc[o])) for o in osrs])
theo = 6.02*N + 1.76 + 10*np.log10(osrs)
for o, m, t in zip(osrs, meas, theo):
    print(f"OSR={o:3d}  実測 {m:6.2f} dB / 理論 {t:6.2f} dB (差 {m-t:+.2f})")

plt.figure(figsize=(8, 5))
plt.semilogx(osrs, theo, "k--", lw=2, base=2,
             label="理論 $6.02N+1.76+10\\log_{10}(\\mathrm{OSR})$")
plt.semilogx(osrs, meas, "o-", color="darkorange", base=2, label="実測(32位相平均)")
plt.xlabel("オーバーサンプリング比 OSR"); plt.ylabel("帯域内SNR [dB]")
plt.title("オーバーサンプリングの処理利得(10ビット量子化器)")
plt.legend(); plt.grid(alpha=0.3, which="both"); plt.tight_layout(); plt.show()

OSRを1から64まで変えたときの帯域内SNRの実測値と理論直線の一致

出力は OSR=1 で $61.90$ dB、OSR=4 で $67.78$ dB、OSR=16 で $73.84$ dB、OSR=64 で $80.26$ dBとなり、すべて理論値との差が $0.25$ dB以内に収まります。対数横軸で見ると実測点が理論直線にきれいに乗り、OSRを4倍にするごとに $6$ dB(1ビット分)ずつ改善していく様子が確認できます。10ビットADCが、OSR=64で16ビット相当の帯域内SNRに達しているわけです。ただし前述のとおり、この方式は「4倍の速度で1ビット」という効率の悪い交換であり、実用的な高分解能ADCが $\Delta\Sigma$ 変調によるノイズシェーピングを併用する理由でもあります。

窓関数の必要性

コヒーレントサンプリングできない場合に何が起きるか、そして窓の選び方を確認します。

import numpy as np
from scipy.signal import windows

FS, N, NFFT = 2.0, 12, 8192
n = np.arange(NFFT); A = FS / 2
Mnc = 331.37                                  # わざと非整数周期にする
xq, d = quantize(A * np.sin(2*np.pi*Mnc*n/NFFT + 0.1234), N, FS)

print("窓関数                   測定SNDR   (理論 74.00 dB)")
for name, w, nb in [("矩形(窓なし)", np.ones(NFFT), 1),
                    ("ハミング", windows.hamming(NFFT, sym=False), 3),
                    ("ハン", windows.hann(NFFT, sym=False), 3),
                    ("ブラックマン", windows.blackman(NFFT, sym=False), 4),
                    ("Blackman-Harris", windows.blackmanharris(NFFT, sym=False), 5),
                    ("フラットトップ", windows.flattop(NFFT, sym=False), 6)]:
    P = np.abs(np.fft.rfft(xq * w))**2
    c = int(round(Mnc))
    Psig = P[c-nb:c+nb+1].sum()                # 主ローブが広がるので複数ビンを信号扱い
    Pn = P.sum() - Psig - P[:nb+1].sum()       # DC 近傍も除外
    print(f"{name:22s} {10*np.log10(Psig/Pn):7.2f} dB")

非整数周期の信号のスペクトルにおける窓ごとの裾の広がりと、窓の種類別に測定したSNDRの棒グラフ

左の図で、矩形窓(青)の裾が $-50$ dB 付近までしか下がらず全周波数に広がっているのに対し、ハン窓(橙)とBlackman-Harris窓(緑)は主ローブから離れると急速に $-100$ dB以下まで落ちていることを確認してください。右の棒グラフはその帰結で、同じ信号・同じ12ビット量子化器なのに、窓の選び方だけで測定SNDRが $8.82$ dB から $73.92$ dB まで65 dBも動きます。緑の2本(Blackman-Harrisとフラットトップ)だけが理論値74.00 dB(破線)に到達しています。

出力は矩形窓が $8.82$ dB、ハミングが $32.48$ dB、ハンが $42.84$ dB、ブラックマンが $58.70$ dB、Blackman-Harrisが $73.92$ dB、フラットトップが $73.19$ dBです。窓なしでは理論値から65 dBもずれ、量子化雑音ではなくスペクトル漏れを測ってしまっています。またサイドローブが $-43$ dBのハミング窓が $-31$ dBのハン窓より悪いのは、ハミング窓のロールオフが遅く遠方のサイドローブが積算されるためです。12ビット(74 dB)の測定にはBlackman-Harris級の窓が必要という結論は、そのままより高分解能なADCの評価にも当てはまります(16ビットならさらに厳しい窓が要ります)。

実装と実測が一通り済んだので、最後に「実機が理論に届かない理由」を整理しておきます。

実機が 6.02N + 1.76 dB に届かない理由

理想量子化器のENOBは公称ビット数とほぼ一致しました。実機ではそうなりません。12ビットADCのENOBが11.0、16ビットADCのENOBが13.5といった値になるのが普通です。その差はどこから来るのでしょうか。主な要因は4つあります。

1. 微分/積分非線形性(DNL / INL) — 理想量子化器では全ての段の幅がぴったり $\Delta$ ですが、実際の抵抗やコンデンサには製造ばらつきがあり、段の幅が場所によって異なります。段幅の理想値からのずれがDNL、入出力特性の直線からのずれがINLです。INLは入力レベルに依存する系統的な誤差なので、雑音ではなく高調波歪みとして現れます。前述のとおり歪みはSNDRを一気に悪化させるので、高分解能ADCではINLの管理が最重要課題になります。DNLが $-1$ LSBを下回ると、その段がまったく出力されない「ミッシングコード」になります。

2. クロックジッタ(アパーチャジッタ) — サンプリングの瞬間が $t_j$ だけ揺らぐと、入力信号が急峻に変化しているところではその揺らぎが電圧誤差に化けます。入力を $A\sin(2\pi f_{in}t)$ とすると、時間誤差 $\delta t$ による電圧誤差は微分係数を使って $\delta v \approx 2\pi f_{in} A \cos(2\pi f_{in}t)\,\delta t$ です。実効値を取ると $\delta v_{rms} = 2\pi f_{in}\, t_j \cdot A/\sqrt{2}$、信号実効値 $A/\sqrt{2}$ との比から

$$ \begin{equation} \mathrm{SNR_{jitter}} = -20\log_{10}\left(2\pi f_{in} t_j\right)\ [\mathrm{dB}] \end{equation} $$

が得られます。ビット数がまったく入っていないことに注目してください。ジッタによる限界は入力周波数だけで決まるのです。$f_{in} = 10$ MHz、$t_j = 1$ psなら $84.0$ dB(ENOB $13.7$)ですが、$f_{in} = 100$ MHzになると $64.0$ dB(ENOB $10.3$)まで落ちます。高速ADCで高い入力周波数を扱うとき、いくらビット数を増やしてもクロックの質が悪ければ無意味だ、というのがこの式の教えるところです。逆に $1$ GHzの入力で $10$ ビット相当を確保したければ、ジッタは $0.1$ ps以下でなければなりません。

ジッタで決まるSNRを入力周波数の関数として描いた図と、実機の12ビットADCで各誤差要因が積み上がりENOBが目減りする試算

左の図の3本の直線は、ジッタ $t_j$ が $0.1$、$1$、$10$ ps のときの上限SNRです。どの線も右下がりで、しかも量子化SNRを表す横の破線・点線(12ビット・16ビット)と必ず交差します。交点より右側では「クロックが天井を決める」領域で、ビット数を増やしても無駄です。$t_j = 1$ ps の青線は $10$ MHz で 16ビットの天井をわずかに下回り、$100$ MHz では12ビット相当も割り込みます。右は12ビットADCの誤差予算の試算例で、量子化雑音のみなら $74.00$ dB(ENOB 12.00)だったものが、INL歪み・ジッタ・熱雑音のパワーが順に加算されて $68.10$ dB(ENOB 11.02)まで落ちる様子を示しています。カタログの「12ビット」に対して実力が11ビットというのは、こういう積み上がりの結果です。

3. 熱雑音 — 前段のアンプ、サンプルホールド回路のスイッチ抵抗($kT/C$ 雑音)、基準電圧源などが、量子化雑音とは別に雑音を持ち込みます。16ビットADCの量子化雑音は $8.8\ \mu\mathrm{V}$ 程度でしたから、これと同オーダーの熱雑音は容易に発生します。総雑音は各寄与のパワー和なので

$$ \sigma_{total}^2 = \frac{\Delta^2}{12} + \sigma_{thermal}^2 + \sigma_{jitter}^2 + \cdots $$

となり、どれか1つが支配的になるとビット数を増やしても効果がなくなります。高分解能ADCでは、$\Delta^2/12$ よりも熱雑音のほうが大きいことが珍しくありません。「24ビットADCのENOBが20ビット」といった仕様は、この熱雑音の壁を表しています。

4. コンパレータの準安定とメタステーブル誤差 — 高速ADCで比較器の判定時間が足りないとき、出力が定まらないまま次段に渡され、大きなコードエラーになります。発生確率は低いものの、レーダーや科学計測のように稀な大誤差が致命的になる用途では無視できません。

これらを踏まえると、ADC選定の実務は次のようになります。「必要なSNRは何dBか」を先に決め、$\mathrm{ENOB} = (\mathrm{SNR}+1.76)/6.02$ で必要ENOBを算出し、公称ビット数ではなくデータシートのSNDR/ENOB曲線を、実際に使う入力周波数のところで読む。ENOBは入力周波数が上がるほど(ジッタと歪みのために)低下するので、DC付近の値だけを見て選ぶと痛い目に遭います。

理想の理論値、その適用条件、破れ方、そして実機での目減り要因まで一通り見てきました。最後に全体を整理しておきます。

まとめ

本記事では、AD変換における量子化雑音とSNRの理論を、導出と実測の両面から解説しました。

  • 量子化誤差は決定論的だが、条件が揃えば $[-\Delta/2, \Delta/2]$ の一様分布として扱える。条件は「振幅が十分大きい」「信号が量子化格子と同期しない」「$f_{in}/f_s$ が単純な有理数でない」の3つ。三角波で1.8倍、のこぎり波で0.037倍と、条件が崩れれば誤差パワーは大きく外れる
  • 雑音パワーは $\sigma_e^2 = \Delta^2/12$。幅 $\Delta$ の一様分布の分散そのもので、実効値は $\Delta/\sqrt{12} \approx 0.289\Delta$
  • $\mathrm{SNR} = 6.02N + 1.76$ dB。$6.02 = 20\log_{10}2$ は「1ビットで刻みが半分」、$1.76 = 10\log_{10}(3/2)$ はフルスケール正弦波という基準に由来する。$\Delta$ が約分で消えるため、SNRは電圧レンジによらない
  • 一般波形では $\mathrm{SNR} = 6.02N + 10.79 + 20\log_{10}(x_{rms}/V_{FS})$。三角波なら $6.02N$、波高率4のガウス雑音なら $6.02N – 7.27$。入力が $-k$ dBFSならSNRも $k$ dB落ちる
  • ノイズフロアとSNRは別物。FFT $M$ 点の処理利得 $10\log_{10}(M/2)$ の分だけノイズフロアは低く見える。$M$ を64倍にするとフロアは18 dB下がるがSNDRは変わらない
  • オーバーサンプリングで $10\log_{10}(\mathrm{OSR})$ dBの利得。OSRを4倍で1ビット分。実測でも理論と0.25 dB以内で一致。ただし効率は悪く、実用的にはノイズシェーピング($\Delta\Sigma$)を併用する
  • 小振幅では誤差が信号と相関し、離散スプリアスとノイズ変調を生む。ディザで歪みを雑音に変換できる。TPDF(2 LSB p-p)なら誤差分散が入力レベルによらず一定になり、代償は $4.77$ dB
  • 測定にはコヒーレントサンプリングか高性能な窓が必須。$f_{in}/f_s = M_c/M$(互いに素)を満たせば矩形窓で理論値と0.1 dB以内。満たせない場合はBlackman-Harris級の窓が要る
  • SNDRとENOBが実力値。$\mathrm{ENOB} = (\mathrm{SNDR}-1.76)/6.02$。実機ではINL歪み、クロックジッタ($-20\log_{10}(2\pi f_{in}t_j)$)、熱雑音で理論値に届かない

量子化雑音は、AD変換という「連続を離散に写す」操作から必然的に生じる、消せない誤差です。しかし完全に消せないだけで、その大きさと分布は設計者がコントロールできます。ビット数を上げる、オーバーサンプリングする、ノイズシェーピングする、ディザで質を変える — 本記事で見てきたのは、この4つの武器の理論的な根拠と限界でした。

次のステップとして、以下の記事も参考にしてください。