はじめに
アナログ・デジタル変換器(ADC: Analog-to-Digital Converter)は、
現実世界の連続的なアナログ信号を、デジタルシステムが理解・処理できる離散的な数値データへ変換する中核的なデバイスである。
本教材では、このADCの原理と動作をPythonによる数値シミュレーションと生成AIによる可視化学習を通じて体系的に理解する。
ADCの基礎
# Program Name: ad_da_sine_separate_plots.py
# Creation Date: 20251105
# Overview: Simulate AD→2進化→DA conversion of a sine wave with separate Matplotlib plots and numeric outputs
# Usage: Run in Python environment (NumPy + Matplotlib) to visualize and calculate SNR, quantization error, etc.
!pip install numpy matplotlib
import numpy as np
import matplotlib.pyplot as plt
# ==== Parameters / パラメータ設定 ====
A = 1.0 # 信号振幅 Amplitude [V]
f_in = 1000 # 入力信号周波数 Input frequency [Hz]
fs = 16000 # サンプリング周波数 Sampling frequency [Hz]
N = 8 # ADC分解能 Resolution [bit]
VFS = 2.0 # フルスケール電圧 Full scale voltage [V]
duration = 0.005 # 表示時間 Signal duration [s]
# ==== Derived values / 派生値 ====
Δ = VFS / (2**N) # 量子化ステップ幅 Quantization step
T = 1 / fs # サンプリング周期 Sampling period
t = np.arange(0, duration, T) # サンプリング時刻 Sampling instants
# ==== 1. 入力アナログ信号 ====
x = A * np.sin(2 * np.pi * f_in * t)
# ==== 2. AD変換(量子化と2進化) ====
D = np.round(x / Δ) # デジタルコード
x_q = D * Δ # 量子化後アナログ値
e_q = x - x_q # 量子化誤差
# ==== 3. D/A変換(ゼロ次ホールド) ====
t_da = np.arange(0, duration, 1/(fs*20)) # 高分解能時間軸 for plotting
y_zoh = np.zeros_like(t_da)
for n in range(len(t)):
idx = (t_da >= n*T) & (t_da < (n+1)*T)
y_zoh[idx] = x_q[n]
# ==== 4. 理想ローパスフィルタ(sinc補間) ====
def sinc_interp(D, T, t_array):
"""sinc補間による理想的DA再構成"""
y_rec = np.zeros_like(t_array)
for n in range(len(D)):
y_rec += D[n] * np.sinc((t_array - n*T)/T)
return y_rec
x_rec = Δ * sinc_interp(D, T, t_da)
# ==== 5. 数値計算 ====
P_signal = np.mean(x**2)
P_noise = np.mean(e_q**2)
SNR = 10 * np.log10(P_signal / P_noise)
SNR_ideal = 6.02 * N + 1.76
Var_eq = np.var(e_q)
RMS_eq = np.sqrt(Var_eq)
print("=== Calculation Results ===")
print(f"Input frequency: {f_in} Hz")
print(f"Sampling frequency: {fs} Hz")
print(f"Resolution: {N} bits")
print(f"Quantization step Δ = {Δ:.6f} V")
print(f"Quantization noise variance = {Var_eq:.6e}")
print(f"Quantization noise RMS = {RMS_eq:.6e}")
print(f"Measured SNR = {SNR:.2f} dB")
print(f"Ideal SNR (6.02N+1.76) = {SNR_ideal:.2f} dB")
# ==== 6. プロット ====
# (1) 入力アナログ波形
plt.figure(figsize=(10,3))
plt.plot(t, x, label="Analog Input x(t)", color='blue')
plt.title("Analog Input Signal")
plt.xlabel("Time [s]")
plt.ylabel("Amplitude [V]")
plt.grid(True)
plt.legend()
plt.tight_layout()
plt.show()
# (2) 量子化出力
plt.figure(figsize=(10,3))
plt.step(t, x_q, where='post', label="Quantized Output (AD)", color='orange')
plt.title("Quantized Output after AD Conversion")
plt.xlabel("Time [s]")
plt.ylabel("Amplitude [V]")
plt.grid(True)
plt.legend()
plt.tight_layout()
plt.show()
# (3) D/A出力(ZOH)
plt.figure(figsize=(10,3))
plt.plot(t_da, y_zoh, label="ZOH Output (DA)", color='green')
plt.title("Zero-Order Hold (DA Output)")
plt.xlabel("Time [s]")
plt.ylabel("Amplitude [V]")
plt.grid(True)
plt.legend()
plt.tight_layout()
plt.show()
# (4) 理想LPF再構成出力
plt.figure(figsize=(10,3))
plt.plot(t_da, x_rec, label="Reconstructed Signal x̂(t)", color='red')
plt.title("Reconstructed Analog Signal via Ideal LPF")
plt.xlabel("Time [s]")
plt.ylabel("Amplitude [V]")
plt.grid(True)
plt.legend()
plt.tight_layout()
plt.show()
# ==========================================================
# CMOS Amplifier Gain and Output Resistance Calculation
# ==========================================================
# This script models small-signal gain and output resistance
# for representative CMOS amplifier topologies.
# Parameters are user-adjustable for design exploration.
# ==========================================================
import numpy as np
import pandas as pd
# --- Parameters (typical CMOS values) ---
gm = 1e-3 # Transconductance [S]
ro = 50e3 # Output resistance [Ω]
Av_stage = [] # List for results
# ----------------------------------------------------------
# 1. Common Source (CS) Amplifier
# Av ≈ -gm * ro
# ----------------------------------------------------------
Av_CS = -gm * ro
Rout_CS = ro
Av_stage.append(["Common Source", Av_CS, Rout_CS])
# ----------------------------------------------------------
# 2. Cascode Amplifier
# Rout ≈ gm2 * ro1 * ro2
# Av ≈ -gm1 * Rout
# ----------------------------------------------------------
gm1, gm2 = 1e-3, 1e-3
ro1, ro2 = 50e3, 50e3
Rout_Cascode = gm2 * ro1 * ro2
Av_Cascode = -gm1 * Rout_Cascode
Av_stage.append(["Cascode", Av_Cascode, Rout_Cascode])
# ----------------------------------------------------------
# 3. Folded Cascode Amplifier
# Simplified model: Av ≈ gm1 * (ro1 || ro2)
# ----------------------------------------------------------
Rout_FC = (ro1 * ro2) / (ro1 + ro2)
Av_FC = gm1 * Rout_FC
Av_stage.append(["Folded Cascode", Av_FC, Rout_FC])
# ----------------------------------------------------------
# 4. Telescopic Op-Amp
# Rout ≈ ro1 || (gm2 * ro2 * ro3)
# Av ≈ gm1 * Rout
# ----------------------------------------------------------
ro3 = 50e3
Rout_Telescopic = 1 / (1/ro1 + 1/(gm2 * ro2 * ro3))
Av_Telescopic = gm1 * Rout_Telescopic
Av_stage.append(["Telescopic", Av_Telescopic, Rout_Telescopic])
# ----------------------------------------------------------
# 5. Two-Stage Op-Amp
# Av_total ≈ (gm1 * ro1) * (gm2 * ro2)
# Rout ≈ ro2
# ----------------------------------------------------------
gm1, gm2 = 1e-3, 1e-3
ro1, ro2 = 50e3, 50e3
Av_TwoStage = (gm1 * ro1) * (gm2 * ro2)
Rout_TwoStage = ro2
Av_stage.append(["Two-Stage", Av_TwoStage, Rout_TwoStage])
# ----------------------------------------------------------
# 6. Source Follower (Common Drain)
# Av ≈ gm * Rs / (1 + gm * Rs)
# Rout ≈ 1 / (gm + gmb)
# ----------------------------------------------------------
Rs = 10e3
gmb = 0.2e-3
Av_SF = (gm * Rs) / (1 + gm * Rs)
Rout_SF = 1 / (gm + gmb)
Av_stage.append(["Source Follower", Av_SF, Rout_SF])
# ----------------------------------------------------------
# 7. Flipped Voltage Follower (FVF)
# Rout ≈ 1 / (gm1 * gm2 * ro1)
# ----------------------------------------------------------
gm1, gm2 = 1e-3, 1e-3
ro1 = 50e3
Rout_FVF = 1 / (gm1 * gm2 * ro1)
Av_FVF = 1 # voltage follower
Av_stage.append(["Flipped Voltage Follower", Av_FVF, Rout_FVF])
# ----------------------------------------------------------
# Output Table
# ----------------------------------------------------------
df = pd.DataFrame(Av_stage, columns=["Amplifier Type", "Voltage Gain (Av)", "Output Resistance (Ω)"])
print(df.to_string(index=False, justify="center"))
# ----------------------------------------------------------
# Example comparison metric (dB gain)
# ----------------------------------------------------------
df["Gain_dB"] = 20 * np.log10(np.abs(df["Voltage Gain (Av)"]))
print("\nVoltage Gain [dB]:")
print(df[["Amplifier Type", "Gain_dB"]])
# ==========================================================
# Quantization Noise Analysis for ADC Simulation
# ==========================================================
# 本コードは、AD変換で生じる「量子化ノイズ」の確率的性質を数値的に確認する。
# 1. 量子化誤差のヒストグラム(確率分布)
# 2. 量子化ノイズの周波数スペクトル(FFT)
# ==========================================================
import numpy as np
import matplotlib.pyplot as plt
# ----------------------------------------------------------
# 1. アナログ入力信号定義(正弦波)
# ----------------------------------------------------------
fs = 1000 # サンプリング周波数 [Hz]
f_sig = 10 # 入力信号周波数 [Hz]
duration = 1.0 # 信号長 [s]
bits = 8 # ADC分解能 [bit]
Vref = 1.0 # 基準電圧 [V]
t = np.arange(0, duration, 1/fs)
v_sample = 0.8 * np.sin(2 * np.pi * f_sig * t) # アナログ信号(振幅0.8V)
# ----------------------------------------------------------
# 2. 量子化処理(Uniform Quantizer)
# ----------------------------------------------------------
levels = 2**bits
Δ = 2*Vref / levels # 量子化ステップ幅
v_quantized = np.round(v_sample / Δ) * Δ # 量子化
error = v_quantized - v_sample # 量子化誤差
# ----------------------------------------------------------
# 3. 量子化誤差ヒストグラム
# ----------------------------------------------------------
plt.figure(figsize=(10, 4))
plt.hist(error, bins=50, color="skyblue", edgecolor="black", density=True)
plt.title("Quantization Error Histogram (Expected: Uniform Distribution)")
plt.xlabel("Error Voltage [V]")
plt.ylabel("Probability Density")
plt.grid(True)
plt.tight_layout()
plt.show()
# ----------------------------------------------------------
# 4. 量子化ノイズの時間波形
# ----------------------------------------------------------
plt.figure(figsize=(10, 3))
plt.plot(t[:200], error[:200], color="orange")
plt.title("Quantization Noise (Time Domain, first 200 samples)")
plt.xlabel("Time [s]")
plt.ylabel("Error Voltage [V]")
plt.grid(True)
plt.tight_layout()
plt.show()
# ----------------------------------------------------------
# 5. 量子化ノイズのスペクトル解析(FFT)
# ----------------------------------------------------------
N = len(error)
f_axis = np.fft.fftfreq(N, d=1/fs)
E_f = np.fft.fft(error)
Pxx = np.abs(E_f[:N//2])**2 / N # Power Spectrum
plt.figure(figsize=(10, 4))
plt.semilogy(f_axis[:N//2], Pxx, color="purple")
plt.title("Power Spectrum of Quantization Noise (Expected: White Spectrum)")
plt.xlabel("Frequency [Hz]")
plt.ylabel("Power Spectrum |E(f)|²")
plt.grid(True)
plt.tight_layout()
plt.show()
# ----------------------------------------------------------
# 6. 数値評価(分散と理論値比較)
# ----------------------------------------------------------
# 理論上の量子化ノイズ分散:σ_q² = Δ² / 12
sigma_q_theory = Δ**2 / 12
sigma_q_measured = np.var(error)
print("=== Quantization Noise Statistics ===")
print(f"ADC bits : {bits} bits")
print(f"Quantization step Δ : {Δ:.6f} V")
print(f"Theoretical variance : {sigma_q_theory:.6e}")
print(f"Measured variance : {sigma_q_measured:.6e}")
print(f"Ratio (meas/theory) : {sigma_q_measured/sigma_q_theory:.3f}")
# ----------------------------------------------------------
# 7. 直角波入力によるノイズ確認(補足)
# ----------------------------------------------------------
v_square = 0.8 * np.sign(np.sin(2*np.pi*f_sig*t)) # 直角波
vq_square = np.round(v_square / Δ) * Δ
error_square = vq_square - v_square
plt.figure(figsize=(10, 3))
plt.plot(t[:200], error_square[:200], color="red")
plt.title("Quantization Error for Square Wave Input")
plt.xlabel("Time [s]")
plt.ylabel("Error Voltage [V]")
plt.grid(True)
plt.tight_layout()
plt.show()
# ==========================================================
# 結論(プレーンテキスト出力)
# ==========================================================
print("\n=== Interpretation ===")
print("1. ヒストグラム → 誤差は一様分布に近い(確率的にランダム)。")
print("2. FFTスペクトル → 広帯域にほぼ一定パワー(ホワイトノイズ特性)。")
print("3. 分散 → 理論値 Δ²/12 と一致。")
print("4. 正弦波入力でも直角波入力でも、量子化誤差の統計的性質は同様。")
# ==========================================================
# 増幅器 × 一次遅れ系 の畳み込み積分シミュレーション
# ==========================================================
# 目的:
# 増幅器ゲイン A と一次遅れ系 G(s) = 1 / (T s + 1) を連続時間で結合し、
# 入力信号との畳み込み積分を数値的に可視化する。
# (応答: y(t) = A * ∫ h(τ) x(t−τ) dτ)
# ==========================================================
import numpy as np
import matplotlib.pyplot as plt
from scipy.signal import lti, step, convolve
# ----------------------------------------------------------
# 1. パラメータ設定
# ----------------------------------------------------------
A = 5.0 # 増幅器ゲイン
T = 0.1 # 時定数 [s]
t = np.linspace(0, 1, 1000)
# 入力信号(矩形波またはステップ入力)
x = np.where(t >= 0.2, 1.0, 0.0)
# ----------------------------------------------------------
# 2. 一次遅れ系のインパルス応答 h(t)
# ----------------------------------------------------------
# 伝達関数: G(s) = 1 / (T s + 1)
h = (1/T) * np.exp(-t/T) # インパルス応答 h(t) = (1/T)e^(-t/T)
# ----------------------------------------------------------
# 3. 畳み込み積分(数値積分近似)
# ----------------------------------------------------------
dt = t[1] - t[0]
y = A * np.convolve(x, h) * dt
t_conv = np.arange(0, len(y)) * dt
# ----------------------------------------------------------
# 4. プロット(時間応答の比較)
# ----------------------------------------------------------
plt.figure(figsize=(10,6))
plt.subplot(3,1,1)
plt.plot(t, x, label="Input x(t)")
plt.ylabel("x(t)")
plt.legend()
plt.grid(True)
plt.subplot(3,1,2)
plt.plot(t, h, label="Impulse Response h(t) = (1/T)e^(-t/T)")
plt.ylabel("h(t)")
plt.legend()
plt.grid(True)
plt.subplot(3,1,3)
plt.plot(t_conv, y, color='purple', label="Output y(t) = A·(x*h)")
plt.ylabel("y(t)")
plt.xlabel("Time [s]")
plt.legend()
plt.grid(True)
plt.tight_layout()
plt.show()
# ----------------------------------------------------------
# 5. 数値検証:最終値
# ----------------------------------------------------------
print("=== Numerical Check ===")
print(f"Gain A = {A}, Time Constant T = {T}")
print(f"Steady-state output ≈ {y[-1]:.3f}")
print("理論値: y(∞) = A × 1 × 1 = ", A)
# ----------------------------------------------------------
# 6. FFTによる周波数応答比較
# ----------------------------------------------------------
freq = np.fft.rfftfreq(len(t), d=dt)
Xf = np.fft.rfft(x)
Hf = 1 / (1 + 1j*2*np.pi*freq*T)
Yf = A * Xf * Hf
plt.figure(figsize=(10,4))
plt.semilogx(freq, 20*np.log10(np.abs(Hf)), label="|G(jω)|")
plt.title("First-Order System Frequency Response (Bode Magnitude)")
plt.xlabel("Frequency [Hz]")
plt.ylabel("Gain [dB]")
plt.grid(True, which="both")
plt.legend()
plt.tight_layout()
plt.show()
# ==========================================================
# 増幅器 × 一次遅れ系 の畳み込み積分 + Bode線図 + Nyquist線図
# ==========================================================
# 内容:
# ・一次遅れ系の時間応答(畳み込み積分)
# ・Bode線図(ゲイン・位相)
# ・Nyquist線図(実軸-虚軸平面)
# ==========================================================
import numpy as np
import matplotlib.pyplot as plt
from scipy.signal import lti, bode, freqresp
# ----------------------------------------------------------
# 1. パラメータ設定
# ----------------------------------------------------------
A = 5.0 # 増幅器ゲイン
T = 0.1 # 時定数 [s]
t = np.linspace(0, 1, 1000)
# 入力信号(ステップ入力)
x = np.where(t >= 0.2, 1.0, 0.0)
# ----------------------------------------------------------
# 2. 一次遅れ系のインパルス応答 h(t)
# ----------------------------------------------------------
h = (1/T) * np.exp(-t/T) # インパルス応答 h(t) = (1/T)e^(-t/T)
# ----------------------------------------------------------
# 3. 畳み込み積分(数値積分)
# ----------------------------------------------------------
dt = t[1] - t[0]
y = A * np.convolve(x, h) * dt
t_conv = np.arange(0, len(y)) * dt
# ----------------------------------------------------------
# 4. 時間応答プロット
# ----------------------------------------------------------
plt.figure(figsize=(10,6))
plt.subplot(3,1,1)
plt.plot(t, x, label="Input x(t)")
plt.ylabel("x(t)")
plt.legend()
plt.grid(True)
plt.subplot(3,1,2)
plt.plot(t, h, label="Impulse Response h(t)")
plt.ylabel("h(t)")
plt.legend()
plt.grid(True)
plt.subplot(3,1,3)
plt.plot(t_conv, y, color='purple', label="Output y(t) = A·(x*h)")
plt.ylabel("y(t)")
plt.xlabel("Time [s]")
plt.legend()
plt.grid(True)
plt.tight_layout()
plt.show()
# ----------------------------------------------------------
# 5. 定常値の確認
# ----------------------------------------------------------
print("=== Steady-State Check ===")
print(f"Gain A = {A}, Time Constant T = {T}")
print(f"Steady-state output ≈ {y[-1]:.3f}")
print("理論値: y(∞) = A × 1 × 1 = ", A)
# ----------------------------------------------------------
# 6. Bode線図の計算
# ----------------------------------------------------------
# G(s) = A / (T s + 1)
num = [A]
den = [T, 1]
system = lti(num, den)
w, mag, phase = bode(system)
plt.figure(figsize=(10,6))
plt.subplot(2,1,1)
plt.semilogx(w, mag)
plt.title("Bode Plot of Amplifier × First-Order System")
plt.ylabel("Gain [dB]")
plt.grid(True, which="both")
plt.subplot(2,1,2)
plt.semilogx(w, phase)
plt.xlabel("Angular Frequency ω [rad/s]")
plt.ylabel("Phase [deg]")
plt.grid(True, which="both")
plt.tight_layout()
plt.show()
# ----------------------------------------------------------
# 7. Nyquist線図
# ----------------------------------------------------------
w = np.logspace(-1, 3, 1000)
_, H = freqresp(system, w)
plt.figure(figsize=(6,6))
plt.plot(H.real, H.imag, label="Nyquist Curve")
plt.plot(H.real, -H.imag, 'r--', alpha=0.5, label="Conjugate Mirror")
plt.axhline(0, color='gray', linewidth=0.8)
plt.axvline(0, color='gray', linewidth=0.8)
plt.xlabel("Re[G(jω)]")
plt.ylabel("Im[G(jω)]")
plt.title("Nyquist Plot of Amplifier × First-Order System")
plt.legend()
plt.axis("equal")
plt.grid(True)
plt.tight_layout()
plt.show()
# ----------------------------------------------------------
# 8. 補足出力
# ----------------------------------------------------------
print("\n=== 解釈 ===")
print("1. Bode線図:低周波ではゲイン≈A、位相≈0°。高周波で-20dB/dec, 位相-90°。")
print("2. Nyquist線図:原点近くを中心に、単純な半円を描く形状。安定な一次遅れ系。")
print("3. 時間応答:指数的に立ち上がり、最終値はゲインA倍。")
# ============================================================
# Program Name: ota_design_calculator.py
# Creation Date: 20251105
# Overview: Compute OTA parameters (A0, GBW, SR, PM) for ADC design
# Usage: Run directly in Python (editable parameters)
# ============================================================
import math
# ==== 共通パラメータ設定 ====
VDD = 1.8 # 電源電圧 [V]
Vpeak = 0.5 # 出力振幅 [V]
fs = 50e6 # サンプリング周波数 [Hz]
CL = 1e-12 # 負荷容量 [F]
Cc = 0.5e-12 # ミラー補償容量 [F]
Ibias = 100e-6 # バイアス電流 [A]
gm1 = 1e-3 # トランスコンダクタンス [S]
ro = 1e5 # 出力抵抗 [Ω]
beta = 0.5 # フィードバック係数
f_signal = 10e6 # 入力信号周波数 [Hz]
Rz = 1 / gm1 # ゼロ補償抵抗
# ==== (1) Folded Cascode OTA ====
A0_fc = gm1 * ro
GBW_fc = gm1 / (2 * math.pi * CL)
SR_fc = Ibias / CL
print("=== (1) Folded Cascode OTA ===")
print(f"DC Gain A0 = {A0_fc:.2e} (→ {20*math.log10(A0_fc):.2f} dB)")
print(f"GBW = {GBW_fc/1e6:.2f} MHz")
print(f"SR = {SR_fc:.2e} V/s ({SR_fc/1e6:.2f} V/µs)")
print(f"Required SR >= {2*math.pi*f_signal*Vpeak:.2e} V/s")
print()
# ==== (2) Telescopic OTA ====
A0_tel = (gm1 * ro)**2
GBW_tel = gm1 / (2 * math.pi * CL)
SR_tel = Ibias / CL
print("=== (2) Telescopic OTA ===")
print(f"DC Gain A0 = {A0_tel:.2e} (→ {20*math.log10(A0_tel):.2f} dB)")
print(f"GBW = {GBW_tel/1e6:.2f} MHz")
print(f"SR = {SR_tel:.2e} V/s ({SR_tel/1e6:.2f} V/µs)")
print()
# ==== (3) Two-Stage OTA (Miller Compensation) ====
A0_2s = (gm1 * ro)**2
GBW_2s = gm1 / (2 * math.pi * Cc)
SR_2s = Ibias / Cc
PM_2s = 60 # 設計目標値
print("=== (3) Two-Stage OTA (Miller) ===")
print(f"DC Gain A0 = {A0_2s:.2e} (→ {20*math.log10(A0_2s):.2f} dB)")
print(f"GBW = {GBW_2s/1e6:.2f} MHz")
print(f"SR = {SR_2s/1e6:.2f} V/µs")
print(f"PM ≈ {PM_2s}° (target)")
print()
# ==== (4) Switched OTA ====
t_sample = 1 / fs
e_settle = math.exp(-t_sample / (A0_fc / (2 * math.pi * GBW_fc)))
GBW_min = math.log(2**12) / (2 * math.pi * beta * t_sample)
print("=== (4) Switched OTA ===")
print(f"Settling error e_settle = {e_settle:.3e}")
print(f"GBW required > {GBW_min/1e6:.2f} MHz")
print(f"Actual GBW = {GBW_fc/1e6:.2f} MHz")
print()
# ==== 参考設計例 ====
print("=== Reference Design Example ===")
print(f"fs = {fs/1e6:.1f} MHz, CL = {CL*1e12:.1f} pF, Vref = {VDD:.1f} V")
print(f"Required A0 > 60 dB, GBW > 10×fs = {10*fs/1e6:.1f} MHz")
print(f"Bias current Ibias = {Ibias*1e6:.1f} µA")
print(f"SR achieved = {SR_fc/1e6:.1f} V/µs")
逐次比較型ADC
# ===============================
# 必要なライブラリをインストール / Install libraries
# ===============================
!pip install numpy matplotlib
import numpy as np
import matplotlib.pyplot as plt
# ===============================
# パラメータ設定 / Global parameters
# ===============================
Vref = 1.0 # 参照電圧 [V] / Reference voltage
Vin = 0.63 # 入力電圧 [V] / Analog input
Nbit = 8 # 分解能 [bit] / Resolution
C_total = 1e-12 # キャパシタ合計 [F] / Total capacitance
fs = 1e6 # サンプリング周波数 [Hz]
t_comp = 1e-6 # 比較器応答時間 [s]
debug = True # ステップ表示ON/OFF
# ===============================
# SAR動作シミュレーション関数 / SAR ADC simulation
# ===============================
def sar_adc(Vin, Vref, Nbit):
"""逐次比較型ADCの二分探索動作を模擬 / Binary search simulation of SAR ADC"""
digital = 0
dac_history = []
for bit in range(Nbit - 1, -1, -1):
trial = digital | (1 << bit)
Vdac = Vref * trial / (2 ** Nbit)
dac_history.append(Vdac)
if debug:
print(f"Step {Nbit - bit}: trial={trial:0{Nbit}b}, Vdac={Vdac:.4f} V, ", end="")
if Vin >= Vdac:
digital = trial
if debug:
print("Comparator=1 (Vin>=Vdac)")
else:
if debug:
print("Comparator=0 (Vin<Vdac)")
code = digital
Vout = Vref * code / (2 ** Nbit)
return code, Vout, dac_history
# ===============================
# 実行 / Run simulation
# ===============================
code, Vout, dac_trace = sar_adc(Vin, Vref, Nbit)
print(f"\nResult: Vin={Vin:.3f} V → Code={code:0{Nbit}b} ({code}) → Vout={Vout:.4f} V")
# ===============================
# 可視化 / Plot DAC output steps
# ===============================
plt.figure(figsize=(6,4))
plt.step(range(1, Nbit+1), dac_trace, where='post', label='DAC output')
plt.axhline(Vin, color='r', linestyle='--', label='Vin')
plt.title("SAR ADC Binary Search Process")
plt.xlabel("Comparison Step")
plt.ylabel("Voltage [V]")
plt.legend()
plt.grid(True)
plt.show()
# ===============================
# エネルギー計算 / Energy estimation
# ===============================
E_conv = 0.5 * C_total * (Vref ** 2)
print(f"Approx. conversion energy: {E_conv:.2e} J/conv")
# ===============================
# kT/C 熱ノイズの評価
# ===============================
!pip install numpy
import numpy as np
# ===============================
# パラメータ設定 / Parameters
# ===============================
k = 1.38e-23 # ボルツマン定数 [J/K]
T = 300 # 絶対温度 [K] (27°C)
Vref = 1.0 # 参照電圧 [V]
Nbit = 12 # ADC分解能 [bit]
SNR_ideal = 6.02 * Nbit + 1.76
# ===============================
# SAR ADCのコンデンサ総容量とノイズの評価
# ===============================
# 1. 理想SNR達成に必要なVrms
q = Vref / (2**Nbit)
Vn_quant_rms = q / np.sqrt(12) # 量子化ノイズ RMS
# 理想SNRを達成するためには、熱ノイズ Vrms が量子化ノイズよりも十分小さい必要。
# ここでは、熱ノイズを量子化ノイズの 1/5 に抑えると仮定する。
Vn_thermal_max = Vn_quant_rms / 5
print(f"許容される熱ノイズ RMS: {Vn_thermal_max:.3e} Vrms")
# 2. 許容Vrmsを実現するために必要なサンプリング容量 Cs
# Cs = kT / (Vn_thermal_max)^2
Cs_required = k * T / (Vn_thermal_max**2)
# ===============================
# 結果出力 / Results
# ===============================
print("\n--- kT/C Thermal Noise Analysis ---")
print(f"ADC 分解能 Nbit : {Nbit} bit")
print(f"理想 SNR : {SNR_ideal:.2f} dB")
print(f"量子化ノイズ RMS : {Vn_quant_rms:.3e} Vrms")
print("-" * 39)
print(f"要求されるサンプリング容量 Cs: {Cs_required*1e12:.2f} pF")
print(f"→ 高分解能ADCの設計では、kT/Cノイズを抑えるため大きなキャパシタが必要となることが分かります。")
# ============================================================
# Successive Approximation Register (SAR) ADC Simulation
# ------------------------------------------------------------
# This model demonstrates:
# • Sample-and-Hold operation
# • Capacitor DAC (binary-weighted)
# • Comparator and SAR logic
# • Step-by-step voltage comparison visualization
# ============================================================
import numpy as np
import matplotlib.pyplot as plt
# -------------------------------
# SAR ADC model function
# -------------------------------
def sar_adc(vin, vref=1.0, nbit=4, verbose=True):
code = 0
history = [] # (step, v_dac, decision)
for i in range(nbit - 1, -1, -1):
trial = code | (1 << i)
v_dac = vref * (trial / (2 ** nbit))
if vin >= v_dac:
code = trial
decision = "1"
else:
decision = "0"
history.append((nbit - i, v_dac, decision))
if verbose:
print(f"Step {nbit - i}: Vdac={v_dac:.3f} V, Vin={vin:.3f} V → bit={decision}")
return code, history
# -------------------------------
# Parameter settings
# -------------------------------
nbit = 4
vref = 1.0
vin = 0.65 # Input voltage
fs = 50e3 # Sampling frequency
chold = 10e-12 # Sample-and-hold capacitor [F]
# -------------------------------
# Sample & Hold operation
# -------------------------------
t = np.linspace(0, 1/fs, 1000)
vin_signal = vin + 0.02 * np.sin(2 * np.pi * 1000 * t) # Small ripple
v_hold = np.ones_like(t) * vin # Held voltage
plt.figure(figsize=(8, 3))
plt.plot(t * 1e6, vin_signal, label="Input Signal Vin(t)")
plt.plot(t * 1e6, v_hold, 'r--', label="Held Voltage (Hold Phase)")
plt.xlabel("Time [µs]")
plt.ylabel("Voltage [V]")
plt.title("Sample-and-Hold Operation")
plt.legend()
plt.grid(True)
plt.show()
# -------------------------------
# Run SAR successive comparison process
# -------------------------------
code, hist = sar_adc(vin, vref, nbit)
print(f"\nFinal Output Code: {code:0{nbit}b} ({code})")
# -------------------------------
# Visualize DAC voltage per comparison step
# -------------------------------
steps = [h[0] for h in hist]
v_dac_values = [h[1] for h in hist]
plt.figure(figsize=(8, 4))
plt.step(steps, v_dac_values, where='mid', label="DAC Output (Vdac)", color='orange')
plt.hlines(vin, 0, nbit + 1, colors='blue', linestyles='dashed', label=f"Vin = {vin:.2f} V")
plt.xlabel("Comparison Step")
plt.ylabel("Voltage [V]")
plt.title("SAR ADC Step-by-Step Comparison (Capacitor DAC Output)")
plt.legend()
plt.grid(True)
plt.show()
# -------------------------------
# DAC ideal transfer characteristic
# -------------------------------
codes = np.arange(2 ** nbit)
v_dac_curve = vref * codes / (2 ** nbit)
plt.figure(figsize=(7, 3))
plt.step(codes, v_dac_curve, where='mid', color='purple')
plt.xlabel("Digital Code")
plt.ylabel("Vdac [V]")
plt.title("Ideal DAC Transfer Characteristic (4-bit)")
plt.grid(True)
plt.show()
デルタシグマ型ADC
# ===============================
# 必要なライブラリをインストール / Install libraries
# ===============================
!pip install numpy matplotlib
import numpy as np
import matplotlib.pyplot as plt
# ===============================
# パラメータ設定 / Global parameters
# ===============================
fs = 1e5 # サンプリング周波数 [Hz] / Sampling frequency
fin = 1e3 # 入力信号周波数 [Hz] / Input frequency
N = 4096 # サンプル数 / Number of samples
Vref = 1.0 # 参照電圧 [V] / Reference voltage
Ain = 0.8 # 入力振幅 [V] / Input amplitude
OSR = 64 # オーバーサンプリング比 / Oversampling ratio
# ===============================
# 入力信号生成 / Generate analog input
# ===============================
t = np.arange(N) / fs
x = Ain * np.sin(2 * np.pi * fin * t)
# ===============================
# ΔΣ変調器 1次ループ / First-order ΔΣ modulator
# ===============================
v = 0 # 積分器の状態変数 / Integrator state
y = np.zeros(N) # 出力ビット列 / Output bitstream
e = np.zeros(N) # 量子化誤差 / Quantization error
for n in range(N):
v += x[n] - y[n-1] * Vref if n > 0 else x[n] # 積分器 (Integrator)
if v >= 0:
y[n] = 1.0 # 量子化器出力 +Vref
else:
y[n] = -1.0 # 量子化器出力 -Vref
e[n] = v - y[n] # 誤差蓄積 (noise shaping)
# ===============================
# デシメーション / Decimation (単純平均)
# ===============================
decim = int(OSR)
M = N // decim
y_dec = np.array([np.mean(y[i*decim:(i+1)*decim]) for i in range(M)])
# ===============================
# 結果表示 / Display results
# ===============================
plt.figure(figsize=(10,6))
# 入力信号とビットストリーム / Input vs Output bitstream
plt.subplot(2,1,1)
plt.plot(t[:500], x[:500], label="Input Signal (x)")
plt.step(t[:500], y[:500], where='mid', label="ΔΣ Bitstream (y)")
plt.title("First-Order Delta-Sigma Modulator")
plt.xlabel("Time [s]")
plt.ylabel("Amplitude [V]")
plt.legend()
plt.grid(True)
# パワースペクトル密度 / Power Spectral Density (PSD)
Yf = np.fft.fft(y * np.hanning(N))
freq = np.fft.fftfreq(N, 1/fs)
PSD = 20 * np.log10(np.abs(Yf[:N//2]) / np.max(np.abs(Yf)))
plt.subplot(2,1,2)
plt.plot(freq[:N//2], PSD)
plt.title("Noise Shaping Effect (1st-Order ΔΣ ADC)")
plt.xlabel("Frequency [Hz]")
plt.ylabel("Normalized Magnitude [dB]")
plt.grid(True)
plt.tight_layout()
plt.show()
# ===============================
# 出力情報 / Output summary
# ===============================
print(f"Input amplitude = {Ain} V, Sampling freq = {fs/1e3:.1f} kHz, OSR = {OSR}")
print(f"Decimated output samples = {M}")
# ================== 必要ライブラリ ==================
!pip install numpy matplotlib scipy
import numpy as np
import matplotlib.pyplot as plt
from scipy.signal import welch
# ================== パラメータ設定 ==================
fs = 48000 # サンプリング周波数 [Hz] / Sampling frequency
fin = 1000 # 入力サイン波周波数 [Hz] / Input sine frequency
A = 0.5 # 入力振幅 / Input amplitude
N = 2**14 # サンプル数 / Number of samples
t = np.arange(N) / fs # 時間軸 / Time vector
# ================== 入力信号生成 ==================
Vin = A * np.sin(2 * np.pi * fin * t)
# ================== ノイズモデル ==================
np.random.seed(0)
Q = (np.random.rand(N) - 0.5) * (2**-10) # 量子化誤差 / Quantization noise
Vn = np.random.randn(N) * 1e-3 # 熱ノイズ / Thermal noise
# ================== ノイズシェーピング処理 ==================
noise = Q + Vn
shaped_noise = np.zeros_like(noise)
for n in range(1, N):
shaped_noise[n] = noise[n] - noise[n-1] # 一次差分 = 1st-order noise shaping
# 出力信号 (Vin + shaped_noise)
Dout = Vin + shaped_noise
# ================== PSD計算(Welch法) ==================
f_in, Pxx_in = welch(Vin, fs=fs, nperseg=2048)
f_out, Pxx_out = welch(Dout, fs=fs, nperseg=2048)
# 入力の最大値を基準に正規化
ref = np.max(Pxx_in)
Pxx_in_dB = 10 * np.log10(Pxx_in / ref + 1e-20)
Pxx_out_dB = 10 * np.log10(Pxx_out / ref + 1e-20)
# ================== 時間波形(入力) ==================
plt.figure(figsize=(12,4))
plt.plot(t[:500], Vin[:500], label="Input Vin (sine)")
plt.xlabel("Time [s]")
plt.ylabel("Amplitude [V]")
plt.title("Input Signal (time domain, first 500 samples)")
plt.legend()
plt.grid(True)
plt.show()
# ================== 入力信号スペクトル ==================
plt.figure(figsize=(12,6))
plt.semilogx(f_in, Pxx_in_dB, label="Input Vin (normalized to 0 dB)")
plt.xlabel("Frequency [Hz]")
plt.ylabel("PSD [dB]")
plt.title("Input Signal Spectrum")
plt.grid(True, which="both")
plt.legend()
plt.show()
# ================== 時間波形(出力) ==================
plt.figure(figsize=(12,4))
plt.plot(t[:500], Dout[:500], label="Output Dout (with noise shaping)")
plt.xlabel("Time [s]")
plt.ylabel("Amplitude [V]")
plt.title("Output Signal (time domain, first 500 samples)")
plt.legend()
plt.grid(True)
plt.show()
# ================== 出力スペクトル ==================
plt.figure(figsize=(12,6))
plt.semilogx(f_out, Pxx_out_dB, label="Output Dout (Noise-Shaped)")
plt.xlabel("Frequency [Hz]")
plt.ylabel("PSD [dB]")
plt.title("Output Signal Spectrum (Noise-Shaped by 1st-order ΔΣ)")
plt.grid(True, which="both")
plt.legend()
plt.show()
# ================== 要約 ==================
print("ΔΣ ADC 1次ノイズシェーピングモデル実行完了")
print(f"Sampling frequency: {fs} Hz, Input frequency: {fin} Hz, Samples: {N}")
!pip install numpy matplotlib
import numpy as np
import matplotlib.pyplot as plt
# ==============================
# パラメータ設定 / Parameter setup
# ==============================
fs = 1000 # サンプリング周波数 [Hz] / Sampling frequency
f_in = 10 # 入力信号周波数 [Hz] / Input signal frequency
A_in = 0.8 # 入力信号振幅 / Input amplitude (0〜1推奨)
N = 500 # サンプル数 / Number of samples
Vref = 1.0 # 量子化器基準電圧 / Quantizer reference voltage
# ==============================
# 入力信号生成 / Generate input signal
# ==============================
t = np.arange(N) / fs
x_in = A_in * np.sin(2 * np.pi * f_in * t)
# ==============================
# ΔΣ変調の主要構造 / Delta-Sigma modulation
# ==============================
integ = 0.0
y_out = np.zeros(N)
quant = np.zeros(N)
err = np.zeros(N)
for n in range(N):
integ += x_in[n] - quant[n-1] if n > 0 else x_in[n] # 積分器 / Integrator
# 1bit 量子化器(コンパレータ動作)/ 1-bit quantizer
quant[n] = Vref if integ >= 0 else -Vref
y_out[n] = quant[n]
err[n] = x_in[n] - y_out[n]
# ==============================
# 結果のプロット / Plot results
# ==============================
plt.figure(figsize=(10,6))
plt.subplot(3,1,1)
plt.plot(t, x_in, label='Input signal (x_in)')
plt.title('ΔΣ ADC Simulation: Input, Integrator, Quantizer')
plt.ylabel('Amplitude [V]')
plt.legend()
plt.subplot(3,1,2)
plt.plot(t, np.cumsum(x_in - y_out)/fs, label='Integrator output')
plt.ylabel('Integrator')
plt.legend()
plt.subplot(3,1,3)
plt.step(t, y_out, label='Quantized output (1-bit)', where='post')
plt.xlabel('Time [s]')
plt.ylabel('Output [V]')
plt.legend()
plt.tight_layout()
plt.show()
# ==========================================================
# 2次 ΔΣ(デルタシグマ)ADC ノイズシェーピング解析
# ==========================================================
# 目的:
# ・2次ΔΣモジュレータのノイズシェーピング特性をFFTで確認
# ・量子化ノイズが高次で強く高周波へ押し上げられることを示す
# ==========================================================
import numpy as np
import matplotlib.pyplot as plt
# ----------------------------------------------------------
# 1. パラメータ設定
# ----------------------------------------------------------
fs = 1_000_000 # サンプリング周波数 [Hz]
fin = 1000 # 入力信号周波数 [Hz]
OSR = 64 # オーバーサンプリング比
duration = 0.01 # シミュレーション時間 [s]
N = int(fs * duration)
t = np.arange(N) / fs
x_in = 0.5 * np.sin(2 * np.pi * fin * t) # 入力信号(-1〜+1の範囲)
# ----------------------------------------------------------
# 2. 2次ΔΣ変調器の動作モデル
# ----------------------------------------------------------
# 2つの積分器+1ビット量子化器による離散シミュレーション
v1 = np.zeros(N)
v2 = np.zeros(N)
y = np.zeros(N)
for n in range(1, N):
v1[n] = v1[n-1] + (x_in[n] - y[n-1]) # 第1積分器
v2[n] = v2[n-1] + (v1[n] - y[n-1]) # 第2積分器
y[n] = 1.0 if v2[n] >= 0 else -1.0 # 1ビット量子化器
# ----------------------------------------------------------
# 3. 出力波形(時間領域)
# ----------------------------------------------------------
plt.figure(figsize=(10, 3))
plt.plot(t[:2000]*1e3, x_in[:2000], label="Input (analog)")
plt.step(t[:2000]*1e3, y[:2000], where='mid', label="ΔΣ Output (1-bit)")
plt.xlabel("Time [ms]")
plt.ylabel("Amplitude")
plt.title("2nd-Order ΔΣ Modulator Time-Domain Waveform")
plt.legend()
plt.grid(True)
plt.tight_layout()
plt.show()
# ----------------------------------------------------------
# 4. FFTスペクトル解析
# ----------------------------------------------------------
# 窓関数を適用(Hanning)
window = np.hanning(N)
y_win = y * window
Y = np.fft.fft(y_win)
Y_mag = np.abs(Y[:N//2]) / np.sum(window/2)
f_axis = np.fft.fftfreq(N, 1/fs)[:N//2]
# dBスケール変換
Y_db = 20 * np.log10(Y_mag / np.max(Y_mag))
plt.figure(figsize=(10, 4))
plt.plot(f_axis/1000, Y_db, color='purple')
plt.title("2nd-Order ΔΣ Modulator Spectrum (Noise Shaping)")
plt.xlabel("Frequency [kHz]")
plt.ylabel("Magnitude [dBFS]")
plt.grid(True)
plt.xlim(0, fs/2/1000)
plt.ylim(-140, 10)
plt.tight_layout()
plt.show()
# ----------------------------------------------------------
# 5. ベースバンドSNRの計算
# ----------------------------------------------------------
# 信号帯域:DC〜fs/(2*OSR)
bw_index = int(N/(2*OSR))
signal_power = np.sum(Y_mag[:bw_index]**2)
noise_power = np.sum(Y_mag[bw_index:]**2)
SNR = 10 * np.log10(signal_power / noise_power)
print("=== 2nd-Order ΔΣ Modulator SNR Analysis ===")
print(f"Sampling Rate fs : {fs/1e3:.1f} kHz")
print(f"Input Frequency : {fin:.1f} Hz")
print(f"Oversampling : {OSR}")
print(f"Estimated SNR : {SNR:.2f} dB")
# ----------------------------------------------------------
# 6. 理論的SNR比較
# ----------------------------------------------------------
# ΔΣ ADCの理論SNR(n次, OSR倍)
# SNR_theory ≈ 6.02N + 1.76 + 30*(2n + 1)*log10(OSR)
n = 2 # 2次モジュレータ
SNR_theory = 6.02*1 + 1.76 + 30*(2*n + 1)*np.log10(OSR)
print(f"Theoretical SNR : {SNR_theory:.2f} dB")
print(f"SNR Ratio (exp/theory) : {SNR/SNR_theory:.3f}")
# ----------------------------------------------------------
# 7. 解釈(テキスト出力)
# ----------------------------------------------------------
print("\n=== Interpretation ===")
print("1. FFTスペクトルで高周波側にノイズが集中 → 高次ノイズシェーピング。")
print("2. 1次ΔΣよりも低周波ノイズが大幅に低減。")
print("3. オーバーサンプリング比を上げると、さらにSNR向上。")
print("4. 理論式 SNR ≈ 6.02 + 1.76 + 30*(2n+1)*log10(OSR) と整合。")
パイプライン型ADC
!pip install numpy matplotlib
import numpy as np
import matplotlib.pyplot as plt
# ===============================
# パラメータ設定 / Parameters
# ===============================
Vref = 1.0
N_stage = 8
N_point = 200
Ain = 0.9 * Vref / 2
fs = 1e6
fin = fs / 40
t = np.arange(N_point) / fs
Vin_all = Ain * np.sin(2 * np.pi * fin * t)
# ===============================
# ステージ関数 / 1.5-bit stage model
# ===============================
def stage(Vin, Vref):
D = np.zeros_like(Vin)
Vdac = np.zeros_like(Vin)
Vres = np.zeros_like(Vin)
for i in range(len(Vin)):
if Vin[i] > 0.25 * Vref:
D[i] = +1
elif Vin[i] < -0.25 * Vref:
D[i] = -1
else:
D[i] = 0
Vdac[i] = D[i] * 0.5 * Vref
Vres[i] = 2 * (Vin[i] - Vdac[i])
return D, Vres
# ===============================
# 残差伝搬 / Residue propagation
# ===============================
Vin_stage = [Vin_all]
Dout_stage = []
for k in range(N_stage):
Dk, Vres = stage(Vin_stage[-1], Vref)
Dout_stage.append(Dk)
Vin_stage.append(Vres)
# ===============================
# 各ステージを個別プロット / Plot each stage separately
# ===============================
for k in range(N_stage):
plt.figure(figsize=(10,3))
plt.plot(t, Vin_stage[k], label=f"Stage {k+1} Input (V_in{k+1})")
plt.xlabel("Time [s]")
plt.ylabel("Voltage [V]")
plt.title(f"Pipeline ADC Stage {k+1} Residue Input")
plt.grid(True)
plt.legend()
plt.tight_layout()
plt.show()
# ===============================
# デジタルコード確認 / Check one sample
# ===============================
sample_index = 50
codes = [int(Dout_stage[k][sample_index]) for k in range(N_stage)]
digital_out = sum([(codes[k] + 1) * (2 ** (N_stage - k - 1)) for k in range(N_stage)])
print(f"Sample {sample_index}: Input={Vin_all[sample_index]:.3f} V → Digital Code={digital_out}")
# ==========================================================
# Simple Digital Reconstruction for a 3-stage Pipeline ADC
# Each stage outputs 1 bit (simplified)
# ==========================================================
import numpy as np
# Example stage outputs (Stage1 outputs appear earliest)
stage1_bits = np.array([1, 0, 1, 1]) # MSB
stage2_bits = np.array([1, 1, 0, 1])
stage3_bits = np.array([0, 1, 1, 0]) # LSB
# Align delays: Stage1 delayed by 2 clocks, Stage2 by 1 clock
delay1 = np.pad(stage1_bits, (2, 0))[:-2]
delay2 = np.pad(stage2_bits, (1, 0))[:-1]
delay3 = stage3_bits
# Weighted sum (bit-shift addition)
digital_out = (delay1 << 2) + (delay2 << 1) + delay3
print("Stage1 delayed:", delay1)
print("Stage2 delayed:", delay2)
print("Stage3:", delay3)
print("Final digital output:", digital_out)
# ==========================================================
# Half Adder and Full Adder Implementation in Python
# ==========================================================
# Half Adder
def half_adder(a, b):
"""半加算器: 入力 a,b → 出力 sum, carry"""
sum_bit = a ^ b # XOR 演算:和
carry_bit = a & b # AND 演算:桁上がり
return sum_bit, carry_bit
# Full Adder
def full_adder(a, b, cin):
"""全加算器: 入力 a,b,carry_in → 出力 sum, carry_out"""
sum1, carry1 = half_adder(a, b)
sum2, carry2 = half_adder(sum1, cin)
carry_out = carry1 | carry2 # OR演算で最終キャリー
return sum2, carry_out
# ==== 実行例 ====
bits = [0, 1]
print("=== Half Adder Truth Table ===")
for a in bits:
for b in bits:
s, c = half_adder(a, b)
print(f"a={a}, b={b} → sum={s}, carry={c}")
print("\n=== Full Adder Truth Table ===")
for a in bits:
for b in bits:
for cin in bits:
s, c = full_adder(a, b, cin)
print(f"a={a}, b={b}, cin={cin} → sum={s}, carry={c}")
出力例
=== Half Adder Truth Table ===
a=0, b=0 → sum=0, carry=0
a=0, b=1 → sum=1, carry=0
a=1, b=0 → sum=1, carry=0
a=1, b=1 → sum=0, carry=1
=== Full Adder Truth Table ===
a=0, b=0, cin=0 → sum=0, carry=0
a=0, b=0, cin=1 → sum=1, carry=0
a=0, b=1, cin=0 → sum=1, carry=0
a=0, b=1, cin=1 → sum=0, carry=1
a=1, b=0, cin=0 → sum=1, carry=0
a=1, b=0, cin=1 → sum=0, carry=1
a=1, b=1, cin=0 → sum=0, carry=1
a=1, b=1, cin=1 → sum=1, carry=1
論理式:
-
半加算器
Sum = A ⊕ B Carry = A · B -
全加算器
Sum = A ⊕ B ⊕ Cin Carry = (A·B) + (B·Cin) + (A·Cin)
# ==========================================================
# 1.5-bit Pipeline ADC Simulation (3 stages, ramp input)
# Each stage performs ±1 / 0 decision and 2× residue amplification
# ==========================================================
import numpy as np
import matplotlib.pyplot as plt
# ==== パラメータ設定 ====
Vref = 1.0 # 基準電圧
Nstage = 3 # ステージ数
Nsamp = 32 # サンプリング点数
vin = np.linspace(-Vref/2, Vref/2, Nsamp) # 入力ランプ波
# ==== ステージ動作関数 ====
def pipeline_stage(vin):
"""1.5ビットステージ:3値判定と残差生成"""
if vin > Vref/4:
d = +1
elif vin < -Vref/4:
d = -1
else:
d = 0
v_dac = (d/2) * Vref
v_res = 2 * (vin - v_dac)
return d, v_res
# ==== 各ステージの結果を記録 ====
d1, d2, d3 = np.zeros(Nsamp), np.zeros(Nsamp), np.zeros(Nsamp)
v1, v2 = np.zeros(Nsamp), np.zeros(Nsamp)
for i in range(Nsamp):
d1[i], v1[i] = pipeline_stage(vin[i]) # Stage1
d2[i], v2[i] = pipeline_stage(v1[i]) # Stage2
d3[i], _ = pipeline_stage(v2[i]) # Stage3
# ==== デジタル出力(重み付き和) ====
# ステージ重み:Stage1=4, Stage2=2, Stage3=1
D = (d1 * 4) + (d2 * 2) + (d3 * 1)
# ==== デジタル出力を0〜7スケールにマッピング ====
D_shifted = D - np.min(D)
D_norm = D_shifted / np.max(D_shifted) * (2**3 - 1)
D_code = np.round(D_norm).astype(int)
# ==== 数値結果を出力 ====
print("=== 1.5-bit Pipeline ADC Simulation Results ===")
print(f"{'Vin(V)':>7} | {'d1':>3} {'d2':>3} {'d3':>3} | {'Digital Code':>12}")
print("-"*40)
for i in range(Nsamp):
print(f"{vin[i]:7.3f} | {int(d1[i]):3d} {int(d2[i]):3d} {int(d3[i]):3d} | {int(D_code[i]):12d}")
# ==== プロット ====
plt.figure(figsize=(9,5))
plt.plot(vin, D_code, 'o-', label="Digital output code")
plt.plot(vin, vin/Vref*(2**3 - 1) + (2**3 - 1)/2, '--', label="Ideal ramp (scaled)")
plt.xlabel("Input Voltage [V]")
plt.ylabel("Digital Code")
plt.title("1.5-bit Pipeline ADC Output (3 stages)")
plt.legend()
plt.grid(True)
plt.show()
出力(例)
=== 1.5-bit Pipeline ADC Simulation Results ===
Vin(V) | d1 d2 d3 | Digital Code
----------------------------------------
-0.500 | -1 -1 -1 | 0
-0.468 | -1 -1 0 | 1
-0.437 | -1 -1 1 | 2
-0.406 | -1 0 -1 | 3
-0.375 | -1 0 0 | 4
...
0.406 | 1 0 1 | 13
0.437 | 1 1 -1 | 14
0.468 | 1 1 0 | 15
0.500 | 1 1 1 | 15
フラッシュ型ADC
# ===============================
# 必要ライブラリ / Required libraries
# ===============================
!pip install numpy matplotlib
import numpy as np
import matplotlib.pyplot as plt
# ===============================
# パラメータ設定 / Parameters
# ===============================
Nbit = 8 # 分解能 [bit]
Vref = 1.0 # 参照電圧 [V]
N_comp = 2**Nbit - 1 # 比較器数 = 255
N_point = 400 # 入力サンプル数
t = np.linspace(0, 1, N_point)
# 入力信号(0〜Vref範囲の正弦波) / Input analog sine wave
Vin = 0.5 * Vref * (1 + np.sin(2 * np.pi * 2 * t))
# ===============================
# 比較器群 / Comparator array
# ===============================
Vref_levels = np.linspace(0, Vref, N_comp + 1)[1:] # Vref_i = i/2^N * Vref
C = np.zeros((N_point, N_comp)) # サーモコード配列 / Thermometer code
for i in range(N_point):
for j in range(N_comp):
C[i, j] = 1 if Vin[i] >= Vref_levels[j] else 0
# ===============================
# サーモコード→デジタル出力変換 / Thermometer to binary
# ===============================
D = np.sum(C, axis=1) # D = Σ C_i
Vout = D * (Vref / (2**Nbit)) # Vout = D × Δ
LSB = Vref / (2**Nbit) # 1LSB
# ===============================
# 結果表示 / Plot results
# ===============================
plt.figure(figsize=(10,4))
plt.plot(t, Vin, label="Analog Input Vin")
plt.step(t, Vout, where='mid', label=f"Flash ADC 8-bit Output (Quantized)")
plt.xlabel("Time [s]")
plt.ylabel("Voltage [V]")
plt.title(f"8-bit Flash ADC Simulation (255 Comparators, 1 LSB = {LSB:.5f} V)")
plt.legend()
plt.grid(True)
plt.tight_layout()
plt.show()
# ===============================
# サーモコード確認(例: 1サンプル) / Example of thermometer code
# ===============================
sample_idx = 120
print(f"Sample {sample_idx}: Vin = {Vin[sample_idx]:.4f} V")
print(f"Digital output D = {int(D[sample_idx])}, Vout = {Vout[sample_idx]:.4f} V")
print(f"Thermometer code (first 32 of 255 comparators):")
print(C[sample_idx][:32])
サイクリック型ADC
!pip install numpy matplotlib
import numpy as np
import matplotlib.pyplot as plt
# =======================================
# --- Parameter Block ---
# =======================================
Vref = 1.0 # Reference voltage [V]
Nbit = 8 # ADC resolution
A = 2000 # Amplifier finite gain
beta_true = 1.85 # Actual internal β used in ADC core
Nsamp = 80
Vin = np.linspace(0, Vref, Nsamp)
Vin_centered = Vin - Vref/2
# =======================================
# --- Function: β-expansion ADC model ---
# =======================================
def beta_expansion_adc(vin, Vref, Nbit, A, beta):
gain_eff = A / (A + 1)
bits = np.zeros((len(vin), Nbit))
vres = vin.copy()
for k in range(Nbit):
Dk = (vres >= 0).astype(int)
bits[:, k] = Dk
Vdac = (2*Dk - 1)*(Vref/2)
vres = gain_eff * beta * (vres - Vdac)
return bits
# =======================================
# --- Step 1: Generate two sequences for β estimation ---
# =======================================
# Fixed DC input (Vin=Vref/4) with MSB forced 0 and 1
def simulate_forced(vin, Vref, Nbit, A, beta, msb_force):
gain_eff = A / (A + 1)
vres = np.ones(len(vin))*vin
bits = np.zeros((len(vin), Nbit))
for k in range(Nbit):
if k == 0:
Dk = np.ones(len(vin))*msb_force
else:
Dk = (vres >= 0).astype(int)
bits[:, k] = Dk
Vdac = (2*Dk - 1)*(Vref/2)
vres = gain_eff * beta * (vres - Vdac)
return bits[0] # return bit sequence for 1 sample
Vin_dc = np.array([0.25])
X0 = simulate_forced(Vin_dc, Vref, Nbit, A, beta_true, 0)
X1 = simulate_forced(Vin_dc, Vref, Nbit, A, beta_true, 1)
# =======================================
# --- Step 2: Dynamic β estimation algorithm ---
# =======================================
def reconstruct_x(bits, beta):
d = bits - 0.5
pow_inv = beta ** (-np.arange(1, len(bits)+1))
return 0.5 + np.sum(d * pow_inv)
def estimate_beta(X0, X1, beta_lo=1.5, beta_hi=2.1, iters=40):
phi = (np.sqrt(5)-1)/2
a, b = beta_lo, beta_hi
c = b - phi*(b-a); d = a + phi*(b-a)
for _ in range(iters):
e_c = abs(reconstruct_x(X0, c) - reconstruct_x(X1, c))
e_d = abs(reconstruct_x(X0, d) - reconstruct_x(X1, d))
if e_c > e_d:
a = c; c = d; d = a + phi*(b-a)
else:
b = d; d = c; c = b - phi*(b-a)
return 0.5*(a+b)
beta_est = estimate_beta(X0, X1)
print(f"Estimated β value = {beta_est:.4f} (true={beta_true:.2f})")
# =======================================
# --- Step 3: Conversion β→Binary ---
# =======================================
def beta_to_binary(bits, beta_eff, Nbit):
S = 0.0
invb = 1.0 / beta_eff
for d in bits:
S = invb * (S + (d - 0.5))
xhat = 0.5 + S
xhat = min(max(xhat, 0.0), 1.0)
D_bin = int(np.clip(np.round((2**Nbit) * xhat), 0, 2**Nbit - 1))
return D_bin, xhat
# =======================================
# --- Step 4: Simulate β-expansion ADC and compare with binary ADC ---
# =======================================
bits_beta = beta_expansion_adc(Vin_centered, Vref, Nbit, A, beta_est)
codes_beta = np.array([beta_to_binary(bits_beta[i], beta_est, Nbit)[0] for i in range(Nsamp)])
Vout_beta = Vref * codes_beta / (2**Nbit)
# Ideal binary for comparison
bits_bin = beta_expansion_adc(Vin_centered, Vref, Nbit, A, 2.0)
codes_bin = np.array([beta_to_binary(bits_bin[i], 2.0, Nbit)[0] for i in range(Nsamp)])
Vout_bin = Vref * codes_bin / (2**Nbit)
# =======================================
# --- Step 5: Plot results ---
# =======================================
plt.figure(figsize=(10,6))
plt.plot(Vin, Vout_bin, label="Binary (β=2.0)")
plt.plot(Vin, Vout_beta, label=f"β-expansion (β_est={beta_est:.2f})")
plt.plot(Vin, Vin, 'k--', label="Ideal")
plt.title("β-expansion ADC vs Binary ADC")
plt.xlabel("Input Voltage [V]")
plt.ylabel("Output Voltage [V]")
plt.legend()
plt.grid(True)
plt.show()
# =======================================
# --- Step 6: Print example bit sequences ---
# =======================================
print("\nExample bit sequences (MSB→LSB):")
for i in range(0, Nsamp, Nsamp//6):
seq_bet = ''.join(str(int(x)) for x in bits_beta[i])
seq_bin = ''.join(str(int(x)) for x in bits_bin[i])
print(f"Vin={Vin[i]:.3f} | β-seq={seq_bet} → {codes_beta[i]:3d} | 2^seq={seq_bin} → {codes_bin[i]:3d}")
NS-SAR型ADC
# ===============================
# ライブラリ導入 / Import libraries
# ===============================
!pip install numpy matplotlib
import numpy as np
import matplotlib.pyplot as plt
# ===============================
# パラメータ設定 / Global parameters
# ===============================
fs = 1e5 # サンプリング周波数 [Hz]
fin = 1000 # 入力信号周波数 [Hz]
N = 8192 # サンプル数
Vref = 1.0 # 参照電圧 [V]
Ain = 0.6 # 入力振幅 [V]
Nbit = 8 # SAR分解能 [bit]
alpha = 1.0 # 積分ゲイン
beta = 1.0 # フィードバック係数
# ===============================
# 入力信号生成 / Input sine signal
# ===============================
t = np.arange(N) / fs
x = Ain * np.sin(2 * np.pi * fin * t)
# ===============================
# NS-SARループシミュレーション / NS-SAR discrete-time loop
# ===============================
v = 0.0
y = np.zeros(N)
for n in range(N):
# 逐次比較(SAR量子化)+積分フィードバック
y[n] = np.round((x[n] + beta * v) * (2**Nbit - 1)) / (2**Nbit - 1)
v = v + alpha * (x[n] - y[n]) # 誤差を積分(ノイズ整形)
# ===============================
# FFT解析 / FFT spectrum analysis
# ===============================
Yf = np.fft.fft(y * np.hanning(N))
freq = np.fft.fftfreq(N, 1/fs)
PSD = 20 * np.log10(np.abs(Yf[:N//2]) / np.max(np.abs(Yf)))
# ===============================
# 可視化 / Visualization
# ===============================
plt.figure(figsize=(10,5))
plt.semilogx(freq[:N//2], PSD, label="Output Spectrum (NS-SAR ADC)")
plt.xlabel("Frequency [Hz]")
plt.ylabel("Normalized Magnitude [dB]")
plt.title("Noise-Shaping SAR ADC Spectrum (FFT method)")
plt.grid(True, which="both")
plt.legend()
plt.tight_layout()
plt.show()
# ===============================
# 比較:通常SAR ADC(ノイズシェーピングなし)
# ===============================
y_sar = np.round(x * (2**Nbit - 1)) / (2**Nbit - 1)
Yf_sar = np.fft.fft(y_sar * np.hanning(N))
PSD_sar = 20 * np.log10(np.abs(Yf_sar[:N//2]) / np.max(np.abs(Yf_sar)))
plt.figure(figsize=(10,5))
plt.semilogx(freq[:N//2], PSD_sar, label="Conventional SAR ADC")
plt.semilogx(freq[:N//2], PSD, label="Noise-Shaping SAR ADC (1st-order)")
plt.xlabel("Frequency [Hz]")
plt.ylabel("Normalized Magnitude [dB]")
plt.title("FFT Comparison: SAR vs Noise-Shaping SAR ADC")
plt.legend()
plt.grid(True, which="both")
plt.tight_layout()
plt.show()
# ===============================
# 結果要約 / Summary
# ===============================
print("Simulation complete.")
print("✓ Noise-Shaping SAR ADC shows 1st-order shaping (20 dB/dec slope).")
print("✓ Quantization noise suppressed in low-frequency band.")
print("✓ Compare with conventional SAR → flat noise floor (no shaping).")
FFTとサイン波
!pip install numpy matplotlib scipy
import numpy as np
import matplotlib.pyplot as plt
# -------------------- Function Definitions --------------------
def freq_search(N: int, fs: float, finRate=8):
"""整合サンプリング用入力周波数を計算 / Calculate input frequency for coherent sampling"""
freq = []
FFT_points = 2**N
for n in range(2, N):
prime = np.exp(n * np.log(2)) - 1
fin_per_fs = prime / FFT_points
freq.append(fin_per_fs)
array = np.array(freq)
index = (np.abs(array - (1 / finRate))).argmin()
fin = freq[index] * fs
return fin
def ideal_adc(input_voltage, n_bits: int, v_min: float, v_max: float):
"""理想ADC変換 / Ideal ADC conversion"""
LSB = (v_max - v_min) / (2**n_bits)
digital_level = ((input_voltage - v_min) / LSB).astype(int)
digital_level = np.clip(digital_level, 0, 2**n_bits - 1)
return digital_level
def sine_wave_generator(N: int, f_signal: float, fs: float, amplitude: float, offset: float):
"""正弦波信号生成 / Generate sine wave"""
t_sampling = 1 / fs
duration = N * t_sampling
t = np.arange(0, duration, t_sampling)
signal = amplitude * np.sin(2 * np.pi * f_signal * t) + offset
return signal
def AC_evaluation(bit: int, data, N: int, fs: float, f_in: float, power_W=1e-3):
"""FFT解析でSNDR, ENOB, SFDR, FoM算出 / FFT-based AC evaluation"""
f = np.divide(data, 2 ** (bit - 1))
dt = 1 / fs
t = np.arange(0, N * dt, dt)
freq = np.fft.fftfreq(N, dt)
F = np.fft.fft(f * np.hanning(N)) / (N/2)
Power = np.abs(F) ** 2
Pow_dB = 10 * np.log10(Power / np.max(Power))
signal_index = np.argmax(Power[1:int(N/2)]) + 1
Signal = Power[signal_index]
Noise = np.sum(Power[1:int(N/2)]) - Signal
SNDR = 10 * np.log10(Signal / Noise)
ENOB = (SNDR - 1.76) / 6.02
spurious = [np.max(Pow_dB[(signal_index * i) - 2: signal_index * i + 2]) for i in range(2, 5)]
SFDR = Pow_dB[signal_index] - np.max(spurious)
# FoM計算 / Figure of Merit
FoM_W = power_W / (2**ENOB * fs)
FoM_S = SNDR + 10 * np.log10(fs / power_W)
# --- 結果表示 / Print results ---
print("===== ADC AC Evaluation Results =====")
print(f"SNR or SNDR = {SNDR:.2f} dB")
print(f"ENOB = {ENOB:.2f} bit")
print(f"SFDR = {SFDR:.2f} dB")
print(f"FoM_W = {FoM_W:.2e} J/step")
print(f"FoM_S = {FoM_S:.1f} dB")
print("=====================================")
# --- FFTスペクトル表示 / Plot spectrum ---
freq_norm = freq / fs
plt.figure(figsize=(8,4))
plt.plot(freq_norm[1:int(N/2)], Pow_dB[1:int(N/2)])
plt.title("FFT Spectrum and Performance Metrics")
plt.xlabel("Normalized Frequency (f/fs)")
plt.ylabel("Power [dBFS]")
plt.grid(True)
plt.text(0.3, -5, f"SNDR={SNDR:.2f} dB")
plt.text(0.3, -10, f"ENOB={ENOB:.2f} bit")
plt.text(0.3, -15, f"SFDR={SFDR:.2f} dB")
plt.tight_layout()
plt.show()
# -------------------- Main Execution --------------------
N = 12 # FFT exponent → FFT points = 2^N
fs = 1_000_000 # Sampling frequency [Hz]
bit = 8 # ADC resolution [bit]
fin_rate = 8 # fs/fin ratio for coherent sampling
v_amplitude = 2.5 # Amplitude [V]
v_offset = 2.5 # DC offset [V]
v_min = 0 # ADC minimum voltage [V]
v_max = 5 # ADC maximum voltage [V]
noise = 0.0 # Noise amplitude [V]
# 入力周波数計算 / Calculate coherent input frequency
f_in = freq_search(N, fs, fin_rate)
print(f"Input frequency f_in = {f_in:.2f} Hz")
# 信号生成 / Generate input signal
v_signal = sine_wave_generator(2**N, f_in, fs, v_amplitude, v_offset)
v_signal += noise * np.random.randn(2**N)
# ADC変換 / Ideal ADC conversion
data = ideal_adc(v_signal, bit, v_min, v_max)
# AC特性評価 / Evaluate ADC AC performance
AC_evaluation(bit, data, 2**N, fs, f_in)
# 時間波形プロット / Plot input signal and ADC code
t = np.arange(0, 2**N) / fs
plt.figure(figsize=(10,4))
plt.plot(t[:500], v_signal[:500], label="Analog Input")
plt.step(t[:500], data[:500], where='post', label="ADC Code")
plt.title("ADC Input and Quantized Output (First 500 samples)")
plt.xlabel("Time [s]")
plt.ylabel("Voltage / Code")
plt.legend()
plt.grid(True)
plt.tight_layout()
plt.show()
ADCに必要なパラメータ
import numpy as np
# ===============================
# パラメータ設定 / Global parameters
# ===============================
N = 12 # 分解能 [bit]
Vref_p = 1.0 # 正基準電圧 [V]
Vref_n = 0.0 # 負基準電圧 [V]
fs = 1e6 # サンプリング周波数 [Hz]
fclk = 10e6 # クロック周波数 [Hz](SAR用)
P = 5e-3 # 消費電力 [W]
Idd = 2e-3 # 電源電流 [A]
Vdd = 2.5 # 電源電圧 [V]
fB = 20e3 # 信号帯域幅 [Hz]
OSR = fs / (2 * fB) # オーバーサンプリング比
A_area = 10e-12 # キャパシタ面積 [m²](例)
# ===============================
# 基本パラメータ
# ===============================
Vfs = Vref_p - Vref_n
q = Vfs / (2**N)
eq_max = q / 2
# ===============================
# 時間関連
# ===============================
Ts = 1 / fs
fN = fs / 2
Tconv = N / fclk
# ===============================
# 性能関連
# ===============================
SNR_ideal = 6.02 * N + 1.76
Pn = q**2 / 12
Vn_rms = q / np.sqrt(12)
# テスト値設定(実測やシミュレーションの仮値)
SINAD = SNR_ideal - 3.0 # 実際は理想より低下
ENOB = (SINAD - 1.76) / 6.02
SFDR = 70.0 # 仮定値 [dB]
THD = -80.0 # 仮定値 [dB]
# ===============================
# 電力効率・FoM
# ===============================
FoM_W = P / (2**N * fs)
FoM_S = SNR_ideal + 10 * np.log10(fs / P)
# ===============================
# エラー・誤差要素
# ===============================
E_FS = Vfs - Vfs # 理想的には0
E_off = 0.0
E_gain = 0.0
sigmaC_C = 1 / np.sqrt(A_area / 1e-12) # 面積1µm²基準
# ===============================
# 出力プリント / Print summary
# ===============================
print("===============================================")
print(" ADC Parameter Summary")
print("===============================================")
print(f"Resolution (N) : {N} bit")
print(f"Full-scale Voltage (Vfs) : {Vfs:.4f} V")
print(f"Quantization Step (q) : {q:.6e} V/LSB")
print(f"Max Quantization Error : {eq_max:.6e} V")
print("-----------------------------------------------")
print(f"Sampling Freq (fs) : {fs:.3e} Hz")
print(f"Nyquist Freq (fN) : {fN:.3e} Hz")
print(f"Sampling Period (Ts) : {Ts:.3e} s")
print(f"Conversion Time (SAR) : {Tconv:.3e} s")
print("-----------------------------------------------")
print(f"SNR (ideal) : {SNR_ideal:.2f} dB")
print(f"SINAD (assumed) : {SINAD:.2f} dB")
print(f"ENOB : {ENOB:.2f} bit")
print(f"Noise Power (Pn) : {Pn:.3e} V^2")
print(f"Input-Referred Noise : {Vn_rms:.3e} Vrms")
print(f"THD (assumed) : {THD:.2f} dB")
print(f"SFDR (assumed) : {SFDR:.2f} dB")
print("-----------------------------------------------")
print(f"Power Consumption (P) : {P:.3e} W")
print(f"Vdd, Idd : {Vdd:.2f} V, {Idd:.3e} A")
print(f"Walden FoM (J/step) : {FoM_W:.3e}")
print(f"Schreier FoM (dB) : {FoM_S:.2f} dB")
print("-----------------------------------------------")
print(f"Oversampling Ratio (OSR) : {OSR:.2f}")
print(f"Noise Shaping Gain (1st) : {10*np.log10(OSR**3/3):.2f} dB")
print("-----------------------------------------------")
print(f"Capacitor Matching σC/C : {sigmaC_C:.2f} % (for A={A_area*1e12:.1f} µm²)")
print("===============================================")
print(" All formulas implemented from ADC performance definitions.")
ADCのサンプルホールド
import numpy as np
import matplotlib.pyplot as plt
# ----- Parameters -----
Vref = 1.0 # Reference voltage
bits = 4 # Number of bits in ADC
Ceq = 2.0 # Equivalent total capacitance (arbitrary)
fs = 1000 # Sampling frequency (Hz)
t = np.linspace(0, 1, fs)
Vin = 0.6 * np.sin(2 * np.pi * 3 * t) + 0.6 # Input analog signal
# ----- Sample and Hold -----
# Sample at discrete times
Ts = 0.05 # Sampling period
sample_times = np.arange(0, 1, Ts)
samples = np.interp(sample_times, t, Vin) # Sample values
# Hold: keep each value constant until next sample
Vhold = np.repeat(samples, int(Ts * fs))
Vhold = np.append(Vhold, np.full(fs - len(Vhold), samples[-1])) # pad
# ----- SAR Conversion Simulation -----
def sar_adc(vin, bits=4, Vref=1.0):
"""Simulate charge-redistribution SAR ADC conversion."""
Vc = -vin # hold voltage from sample
D = np.zeros(bits)
for k in range(bits):
# Tentatively set bit = 1
D[k] = 1
Vtest = -vin + np.sum(D * Vref / 2**np.arange(1, bits+1))
if Vtest < 0:
# comparator output high -> keep bit = 1
pass
else:
# comparator output low -> reset bit to 0
D[k] = 0
code = np.sum(D * 2**np.arange(bits-1, -1, -1))
return D, code
codes = []
for vin in samples:
_, code = sar_adc(vin, bits, Vref)
codes.append(code)
Vout = np.array(codes) * (Vref / (2**bits - 1)) # digital-to-analog reconstructed value
# ----- Plot Results -----
plt.figure(figsize=(10, 6))
plt.plot(t, Vin, label="Analog Input (Vin)", linewidth=1.5)
plt.step(sample_times, Vout, where="post", label="Quantized Output (SAR Result)")
plt.step(t, Vhold, where="post", label="Sample & Hold Output", alpha=0.6)
plt.title("Sample-Hold and Charge Redistribution SAR ADC Simulation", fontsize=13)
plt.xlabel("Time [s]")
plt.ylabel("Voltage [V]")
plt.grid(True)
plt.legend()
plt.tight_layout()
plt.show()
二部探索アルゴリズムでつかうβ推定
# --- 比較用のデータ ---
# βコード列 (L=10)
beta_code = "0101101000"
# β変換プロセスを経て得られた通常の10ビット2進コード (D=385)
conventional_binary_code = "0110000001"
# 10進数コード D (確認用)
D_int = 385
print("--- β展開ADC結果と通常の2進数の比較 ---")
print(f"基数 β = 1.83 (特殊な基数)")
print(f"ビット長 N = 10")
print("-" * 35)
print(f"1. 元の βコード列 b_k: {beta_code}")
print(f"2. 通常の10進数コード D: {D_int}")
print(f"3. 変換後の通常の2進コード: {conventional_binary_code}")
print("-" * 35)
# ビットごとの比較
print("\n--- ビットごとの差異 ---")
print(" k | βコード | 2進コード | 差異")
print("----|---------|-----------|-----")
diff_count = 0
for k in range(10):
b_bit = beta_code[k]
c_bit = conventional_binary_code[k]
# 差異があるかチェック
diff = "❌" if b_bit != c_bit else "✅"
if b_bit != c_bit:
diff_count += 1
# kはMSB側から1から数える
print(f" {k+1:2} | {b_bit} | {c_bit} | {diff}")
print("-" * 35)
print(f"総ビット数: 10")
print(f"差異のあるビット数: {diff_count}")
print(f"→ βコードは通常の2進コードとは異なる形式であることが確認できます。")
import numpy as np
import matplotlib.pyplot as plt
import os
from scipy.fft import fft
from scipy.signal import find_peaks
# --- 1. 設定とデータ読み込み ---
# ADCの出力データファイルパス (適切なパスに修正してください)
# path = r"20240221_512_fs735k_1stfixed.csv_out.csv"
# 仮のデータ生成 (実際のデータに合わせて修正してください)
N = 512 # サンプリング点数 (FFTpoints)
fs = 735294.117647 # サンプリング周波数 [Hz]
fn_ideal = 15 # 入力信号のサイクル数 (M)
fin = fn_ideal * fs / N # 理想的な入力周波数 [Hz]
# βopt推定の探索範囲
beta_sweep_min = 1.85
beta_sweep_max = 1.95
beta_sweep_nums = 1000 # 探索回数
# サンプリングデータ (仮のデータ: 理想的なCyclic ADC出力に近い冗長コード)
# 実際のファイルからロードする代わりに、ここではテストデータを生成します
# ----------------------------------------------------------------------
# 【重要】テストデータを、FFTに必要な N個の最終ADC出力として生成し直します。
# ----------------------------------------------------------------------
def generate_test_data(N, beta_true, fin, fs):
t = np.arange(N) / fs
Vin_norm = np.sin(2 * np.pi * fin * t) # -1から1の範囲の入力信号を想定
# 理想的なADC出力を生成(Nビット換算)
# 簡略化のため、ここではVin_normを直接量子化(疑似的な最終出力)
# 実際のデータでは、各サンプルが既にデジタル校正前のコード系列です
# N=512のFFT点数に合わせた配列を生成
ideal_output = np.round(Vin_norm * 2**(10) / 2**(10)) # 10bit程度の分解能を想定
# ノイズと非線形性を加えることで、β探索が意味を持つようにする
noise = np.random.normal(0, 0.05, N)
raw_codes_approx = Vin_norm * (2**10) + noise * 10
# 実際のADC出力は、Nサンプル分の最終冗長コード配列であると仮定
return raw_codes_approx
# 実際のADC出力データ(校正前の最終コード系列)をロードする行に置き換えてください
# raw_codes = np.loadtxt(path, delimiter=',')
raw_codes = generate_test_data(N, 1.90, fin, fs)
# N点のデータ配列であることが必須
if raw_codes.ndim != 1 or len(raw_codes) != N:
print("ERROR: raw_codesはFFTポイント数Nと同じ長さの一次元配列でなければなりません。")
# ここではテストデータをそのまま使用し続けますが、実際のコードでは終了すべきです
B = len(raw_codes)
# --- 2. β最適値の探索とデジタル校正関数 ---
def digital_calibration(raw_codes, beta_est):
"""
【修正】
raw_codesはN点の時系列データ配列であり、
Cyclic ADCのデジタル校正は、各サンプル(N点)に対してではなく、
アナログビット重み補正係数 (1/β)^i を用いて一括で校正済みコードを計算します。
ここでは、raw_codesが「校正前の最終ビット系列」であり、
beta_estは単に「校正係数」として乗じるという**簡略化**された動作をシミュレートします。
*もしraw_codesが[Nサンプル, Bビット]の行列であれば、関数は大幅に複雑化します*
ここでは、FFTを実行するために、raw_codesを配列として返し、
β探索が意味を持つように、各サンプルに対して校正係数を乗じる簡略化されたロジックを適用します。
"""
# 探索の目的がSNDR最大化であるため、この簡略化された校正係数の適用で探索は可能
# 厳密な校正ロジックは、raw_codesがビット系列である場合に必要です。
D_binary = raw_codes * (beta_est / 2.0)
return D_binary
def calculate_sndr(D_binary, fs, fin):
"""デジタル校正後のデータでSNDRを計算する (簡略版)"""
# 1. FFTを実行
# D_binaryが配列であることを確認 (修正後のコードでは配列になっている)
if np.ndim(D_binary) == 0:
raise ValueError("FFTの入力は一次元配列である必要があります。")
Y = fft(D_binary)
P_total = np.abs(Y)**2 / N # 電力スペクトル
# 2. 信号ピークとノイズ/歪みを分離
# 正規化周波数 [0, fs/2] に相当するインデックスを取得
P_half = P_total[1:int(N/2)]
# 信号ピークのインデックスを特定
# 簡略化のため、最も高いピークを信号と仮定します
idx_signal = np.argmax(P_half) + 1
P_signal = P_total[idx_signal]
# DC成分 (P_total[0]) と信号成分を除く全電力
P_noise_dist = np.sum(P_total) - P_total[0] - P_signal
# SNDRを計算
if P_noise_dist > 0:
SNDR_val = 10 * np.log10(P_signal / P_noise_dist)
else:
SNDR_val = -200.0 # エラー値または非常に高い値
return SNDR_val, P_total, idx_signal
# --- 3. βoptの探索 ---
beta_sweep_range = np.linspace(beta_sweep_min, beta_sweep_max, beta_sweep_nums)
sndr_results = []
print(f"INFO: βoptの探索範囲: {beta_sweep_min:.4f} から {beta_sweep_max:.4f}")
for beta_est in beta_sweep_range:
# 1. デジタル校正
D_calibrated = digital_calibration(raw_codes, beta_est)
# 2. SNDRを評価
try:
sndr_val, _, _ = calculate_sndr(D_calibrated, fs, fin)
sndr_results.append(sndr_val)
except ValueError as e:
# calculate_sndr内で発生したValueErrorを捕捉
print(f"ERROR during SNDR calculation for beta={beta_est:.4f}: {e}")
sndr_results.append(-300.0) # 失敗を示す値
# βoptの決定
sndr_results = np.array(sndr_results)
idx_opt = np.argmax(sndr_results)
beta_opt = beta_sweep_range[idx_opt]
SNDR_opt = sndr_results[idx_opt]
ENOB_opt = (SNDR_opt - 1.76) / 6.02
# --- 4. 最終結果の表示とプロット ---
print(f"====================================")
print(f"最適 β (beta_opt): {beta_opt:.6f}")
print(f"最大 SNDR: {SNDR_opt:.2f} [dB]")
print(f"対応 ENOB: {ENOB_opt:.2f} [bit]")
print(f"====================================")
# 最適βで再校正とFFT
D_calibrated_opt = digital_calibration(raw_codes, beta_opt)
SNDR, P_total, idx_signal = calculate_sndr(D_calibrated_opt, fs, fin)
P_dB = 10 * np.log10(P_total / np.max(P_total)) # dBFS
# パワースペクトルのプロット
fig, ax = plt.subplots(figsize=(10, 6))
ax.set_title(f"Power Spectrum (N={N}) with $\\beta_{{opt}}$", fontname="MS Gothic")
ax.set_xlabel("Normalized Frequency (f/fs)")
ax.set_ylabel("Power [dBFS]")
ax.text(0.05, -10, f"SNDR = {SNDR_opt:.2f} [dB]", size=12)
ax.text(0.05, -20, f"ENOB = {ENOB_opt:.2f} [bit]", size=12)
ax.text(0.05, -30, f"$\\beta_{{opt}}$ = {beta_opt:.6f}", size=12)
ax.set_ylim(-170, 10)
ax.grid(True)
# 周波数軸の設定
freq_normalized = np.arange(N) / N
ax.plot(freq_normalized[1:int(N/2)], P_dB[1:int(N/2)], label="FFT Output")
ax.legend()
plt.show()
# β探索結果のプロット (オプション)
plt.figure(figsize=(10, 4))
plt.plot(beta_sweep_range, sndr_results)
plt.title("SNDR vs. $\\beta$ Estimation", fontname="MS Gothic")
plt.xlabel("Estimated $\\beta$")
plt.ylabel("SNDR [dB]")
plt.axvline(x=beta_opt, color='r', linestyle='--', label=f'$\\beta_{{opt}} = {beta_opt:.6f}$')
plt.grid(True)
plt.legend()
plt.show()
# 1. 理想バイナリADC (利得無限大, Ca=Cb)
def ideal_binary_cyclic_adc(V_in, V_ref, D_out_sign):
"""V_res = 2*V_in ± V_ref"""
return 2 * V_in + D_out_sign * V_ref # D_out_sign: +1が+Vref、-1が-Vref
# 2. 有限利得バイナリADC (利得 A が有限, Ca=Cb)
def finite_gain_binary_cyclic_adc(V_in, V_ref, A, D_out_sign):
"""V_res = (1 / (1 + 2/A)) * {2*V_in ± V_ref}"""
gain_factor = 1.0 / (1.0 + 2.0 / A)
return gain_factor * (2 * V_in + D_out_sign * V_ref)
# 3. 有限利得 非2進サイクリックADC (利得 A が有限, Ca:Cb=(beta-1):1)
def finite_gain_non_binary_cyclic_adc(V_in, V_ref, beta, A, D_out_sign):
"""V_res = (1 / (1 + beta/A)) * {β*V_in ± (β-1)V_ref}"""
gain_factor = 1.0 / (1.0 + beta / A)
return gain_factor * (beta * V_in + D_out_sign * (beta - 1) * V_ref)
# --- 計算パラメータ ---
V_in_val = 0.6 # 入力電圧 [V]
V_ref_val = 1.0 # 基準電圧 [V]
A_val = 100.0 # オペアンプ利得
beta_val = 1.7 # 非2進ADCの増幅率
# --- 計算と出力 ---
print("--- サイクリックADC 残差電圧計算 ---")
print(f"共通パラメータ: Vin={V_in_val}V, Vref={V_ref_val}V, A={A_val}, β={beta_val}\n")
# 1. 理想バイナリADC
res_ideal_plus = ideal_binary_cyclic_adc(V_in_val, V_ref_val, D_out_sign=1)
res_ideal_minus = ideal_binary_cyclic_adc(V_in_val, V_ref_val, D_out_sign=-1)
print("1. 理想バイナリADC:")
print(f" +V_ref の場合: V_res = {res_ideal_plus:.4f} V")
print(f" -V_ref の場合: V_res = {res_ideal_minus:.4f} V\n")
# 2. 有限利得バイナリADC
res_finite_plus = finite_gain_binary_cyclic_adc(V_in_val, V_ref_val, A_val, D_out_sign=1)
res_finite_minus = finite_gain_binary_cyclic_adc(V_in_val, V_ref_val, A_val, D_out_sign=-1)
print("2. 有限利得バイナリADC:")
print(f" +V_ref の場合: V_res = {res_finite_plus:.4f} V")
print(f" -V_ref の場合: V_res = {res_finite_minus:.4f} V\n")
# 3. 有限利得 非2進サイクリックADC
res_nonbinary_plus = finite_gain_non_binary_cyclic_adc(V_in_val, V_ref_val, beta_val, A_val, D_out_sign=1)
res_nonbinary_minus = finite_gain_non_binary_cyclic_adc(V_in_val, V_ref_val, beta_val, A_val, D_out_sign=-1)
print("3. 有限利得 非2進サイクリックADC:")
print(f" +V_ref の場合: V_res = {res_nonbinary_plus:.4f} V")
print(f" -V_ref の場合: V_res = {res_nonbinary_minus:.4f} V")
