PythonでわかるAD変換と二分探索アルゴリズム

##  はじめに

本記事では、逐次比較型ADC（SAR ADC）風のアルゴリズムを用いて、
入力信号（サイン波）に対して 比較 → ビット出力 → 残差生成 → 再帰処理 を行う
Pythonコードについて詳しく解説します。

このコードを使うことで、以下のような処理が視覚的に確認できます：

各ステップでのビット判定（0 or 1）

各ステップで生成される残差電圧の推移

最終的なデジタル出力の再構成

すべてのビット・残差を時間軸に沿ってプロット表示

逐次比較型ADCの学習や可視化、シミュレーションに最適な内容となっております。

##  Pythonコード

```python
import numpy as np
import matplotlib.pyplot as plt

def comparator_and_residue(Vin, Vref):
    """
    コンパレータと残差出力
    Comparator and residue output
    """
    if Vin > Vref:
        Bit = 1
        Vout = 2 * (Vin - Vref)
    else:
        Bit = 0
        Vout = 2 * Vin
    return Bit, Vout

# ---- 初期設定 / Initialization ----
fs = 1000                           # サンプリング周波数 [Hz]
f = 5                               # サイン波周波数 [Hz]
t = np.arange(0, 1, 1/fs)           # 時間軸
Vin_all = 0.5 * np.sin(2 * np.pi * f * t) + 0.5  # [0,1]範囲のサイン波
Vref = 0.5                          # 比較用基準電圧
n_steps = 8                         # 8ビット比較ステップ

# ---- データ記録用 / Data storage ----
bits_matrix = []
residues_matrix = []
digital_values = []

# ---- SAR処理: ビット生成 + 各残差を記録 ----
for Vin in Vin_all:
    current_Vin = Vin
    bits = []
    residues = []

    for _ in range(n_steps):
        bit, current_Vin = comparator_and_residue(current_Vin, Vref)
        bits.append(bit)
        residues.append(current_Vin)

    bits_matrix.append(bits)
    residues_matrix.append(residues)
    digital = int("".join(map(str, bits)), 2)
    digital_values.append(digital)

# ---- NumPy配列に変換 / Convert to arrays ----
bits_array = np.array(bits_matrix)            # shape: (samples, 8)
residues_array = np.array(residues_matrix)    # shape: (samples, 8)

# ---- プロット / Plot section ----
plt.figure(figsize=(16, 18))

# 元の入力とデジタル値のプロット / Input and reconstructed value
plt.subplot(n_steps + 2, 1, 1)
plt.plot(t, Vin_all, label='Input (sine wave)')
plt.plot(t, np.array(digital_values) / (2**n_steps - 1), label='Reconstructed Digital Value')
plt.title("Input and Reconstructed Digital Output (8-bit)")
plt.ylabel("Voltage")
plt.legend()
plt.grid(True)

# 各残差ステップのプロット / Plot all residues
for i in range(n_steps):
    plt.subplot(n_steps + 2, 1, i + 2)
    plt.plot(t, residues_array[:, i], label=f"Residue after Step {i+1}", color='C' + str(i % 10))
    plt.ylabel(f"Vout {i+1}")
    plt.grid(True)
    plt.legend()

plt.xlabel("Time [s]")
plt.tight_layout()
plt.show()

# ---- 最初の数個の残差を表示 / Print first 10 residues ----
print("First 10 samples (residues per step):")
for i in range(10):
    residue_str = ', '.join(f"{r:.4f}" for r in residues_matrix[i])
    print(f"t = {t[i]:.4f}s, Vin = {Vin_all[i]:.3f}, Residues = [{residue_str}]")

# ---- プロット / Plot section ----
plt.figure(figsize=(14, 14))

# 元の入力とデジタル出力の比較 / Input vs Digital Output
plt.subplot(n_steps + 2, 1, 1)
plt.plot(t, Vin_all, label='Input (sine wave)')
plt.plot(t, np.array(digital_values) / (2**n_steps - 1), label='Reconstructed Digital Value')
plt.title("Input and Reconstructed Digital Output (8-bit)")
plt.ylabel("Voltage")
plt.legend()
plt.grid(True)

# 各ビットのプロット / Bit plot
for i in range(n_steps):
    plt.subplot(n_steps + 2, 1, i + 2)
    plt.plot(t, bits_array[:, i], drawstyle='steps-post')
    plt.ylim(-0.2, 1.2)
    plt.ylabel(f"Bit {n_steps - 1 - i}")
    plt.grid(True)

# 最終段の下に残差の最終値をプロット / Last residue
plt.subplot(n_steps + 2, 1, n_steps + 2)
plt.plot(t, residues_array[:, -1], label='Final Residue', color='purple')
plt.ylabel("Residue (Step 8)")
plt.xlabel("Time [s]")
plt.grid(True)
plt.legend()

plt.tight_layout()
plt.show()


```
結果
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4046871/97533f46-467d-41bc-a0b2-32fadbea6905.png)