0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

8ビットADCのFFT解析と性能評価のダミーシミュレーション(SNDR・ENOB・SFDRの計算)

Last updated at Posted at 2025-03-10

8ビットADCのFFT解析と性能評価

1. 概要

本記事では、8ビットADCのFFT解析を行い、SNDR(信号対ノイズ歪み比)、ENOB(有効ビット数)、SFDR(スプリアスフリー・ダイナミックレンジ)を計算します。Pythonを用いたシミュレーションを通じて、ADCの性能評価を実施します。


2. シミュレーション環境

  • サンプリング周波数: 1 MSPS(1MHz)
  • 入力信号周波数: 124.511 kHz
  • 振幅: 8ビットADCのフルスケール範囲
  • FFTポイント数: 4096
  • 時間領域のシミュレーション: 4.096 ms
    補足として、入力信号はコヒーレントサンプリングに基づいて計算します


3. 実装コード

以下のPythonコードでは、信号の生成、量子化、FFT解析を行い、SNDR・ENOB・SFDRの計算を実施します。

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 wave_sort(data, fs: float, f_signal: float):
    """Sort data for one cycle"""
    data_num = len(data)
    t_signal = 1 / f_signal
    Ts = 1 / fs
    dx_list = [(Ts * n) % t_signal for n in range(data_num)]
    sorted_indices = sorted(range(len(dx_list)), key=lambda k: dx_list[k])
    sort = [data[i] for i in sorted_indices]
    return sort

def ideal_adc(input_voltage, n_bits: int, v_min: float, v_max: float):
    """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):
    """Evaluate ADC's AC performance"""
    f = np.divide(data, 2 ** (bit - 1))
    dt = 1 / fs
    t = np.arange(0, N * dt, dt)
    freq = np.fft.fftfreq(N, dt)
    freq_norm = freq / fs
    F = np.fft.fft(f) * 2 / N
    Power = np.abs(F) ** 2
    Pow_dB = 10 * np.log10(Power / 1.0)

    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)

    # --- 結果表示 / Print results ---
    print("===== AC Evaluation Results =====")
    print("FFT Peak Frequency =", round(freq[signal_index], 2), "Hz")
    print("FFT Peak Power =", round(Pow_dB[signal_index], 2), "dB")
    print("SNDR =", round(SNDR, 2), "dB")
    print("ENOB =", round(ENOB, 2), "bit")
    print("SFDR =", round(SFDR, 2), "dB")
    print("=================================")

    # --- グラフ表示 / Plotting ---
    sorted_data = wave_sort(data, fs, f_in)
    fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 4))

    ax1.set_title("Sorted Wave")
    ax1.set_xlabel("Time [sec]")
    ax1.set_ylabel("Code")
    ax1.grid()
    ax1.set_ylim(0, 2**bit)
    ax1.plot(t, sorted_data, marker=".", linestyle="", markersize=4)

    ax2.set_title("Power Spectrum")
    ax2.set_xlabel("f/fs")
    ax2.set_ylabel("Power [dB]")
    ax2.grid()
    ax2.plot(freq_norm[1:int(N/2)], Pow_dB[1:int(N/2)])
    ax2.text(0.3, -5, f"SNDR = {round(SNDR, 2)} dB")
    ax2.text(0.3, -10, f"ENOB = {round(ENOB, 2)} bit")
    ax2.text(0.3, -15, f"SFDR = {round(SFDR, 2)} dB")

    plt.tight_layout()
    plt.show()

# -------------------- メイン処理 / Main Execution --------------------

N = 12                # FFTポイントの指数(2^Nポイント)
fs = 1000000          # サンプリング周波数
bit = 8               # ADCビット数
fin_rate = 8          # 入力周波数に対するfsの比(整合サンプリングのため)
v_amplitude = 2.5     # 振幅
v_offset = 2.5        # オフセット
v_min = 0             # ADCの最小電圧
v_max = 5             # ADCの最大電圧
noise = 0         # ノイズ振幅

# 入力周波数と信号生成
f_in = freq_search(N, fs, fin_rate)
print("Input signal frequency:", round(f_in, 2), "Hz")

# 入力信号の周期を計算 / Calculate signal period
T_signal = 1 / f_in
print("Input signal period T =", T_signal, "s")


# 入力信号の周期を計算 / Calculate signal period
T_signal = 1 / f_in

# 入力信号の周期を表示 / Print the input signal period
print("Input signal period T =", T_signal, "s")


# FFTポイントとシミュレーション時間
FFT_points = 2 ** N
Ts = 1 / fs
simulation_time = FFT_points * Ts

print("FFT Points =", FFT_points)
print("Sampling Period Ts =", Ts, "s")
print("Simulation Time =", simulation_time, "s ( = FFT Points × Ts )")

# パラメータ確認
print("===== Experiment Parameters =====")
print("N =", N, "->", 2**N, "points")
print("fs =", fs, "Hz")
print("bit =", bit)
print("fin_rate =", fin_rate)
print("v_amplitude =", v_amplitude, "V")
print("v_offset =", v_offset, "V")
print("v_min =", v_min, "V")
print("v_max =", v_max, "V")
print("noise amplitude =", noise, "V")
print("=================================")

# 入力周波数と信号生成
f_in = freq_search(N, fs, fin_rate)
print("Input signal frequency:", round(f_in, 2), "Hz")

v_signal = sine_wave_generator(2**N, f_in, fs, v_amplitude, v_offset)
v_signal += noise * np.random.rand(2**N)  # ノイズ加算



# ADC変換と評価
data = ideal_adc(v_signal, bit, v_min, v_max)
AC_evaluation(bit, data, 2**N, fs, f_in)

# 時間軸生成
t = np.arange(0, 2**N) / fs

# 入力信号全体プロット(アナログ信号)
plt.figure(figsize=(10, 4))
plt.plot(t, v_signal, label="Input Signal")
plt.title("Full Input Signal")
plt.xlabel("Time [s]")
plt.ylabel("Voltage [V]")
plt.grid()
plt.tight_layout()
plt.show()

# 入力信号全体プロットでADCコードをプロット
# ADCコードによる全体波形をプロット / Plot full ADC output (Code)
plt.figure(figsize=(10, 4))
plt.plot(t, data, label="ADC Output Code")  # ← data に変更
plt.title("Full ADC Output")
plt.xlabel("Time [s]")
plt.ylabel("Code")  # ← Voltage → Code に変更
plt.grid()
plt.tight_layout()
plt.show()


# 1周期のサンプル数でADCコードをプロット
cycle_length = int(fs / f_in)  # 1周期のサンプル数
data_cycle = data[:cycle_length]
t_cycle = t[:cycle_length]

plt.figure(figsize=(10, 4))
plt.plot(t_cycle, data_cycle, marker='o', linestyle='-', label="1 Cycle Code")
plt.title("One Cycle of ADC Output")
plt.xlabel("Time [s]")
plt.ylabel("Code")  # Codeとして縦軸表示
plt.grid()
plt.tight_layout()
plt.show()

# 5周期のサンプル数でADCコードをプロット

# -------------------- ADCコードの5周期分をプロット / Plot 5 cycles of ADC code --------------------

# 1周期あたりのサンプル数を計算 / Calculate samples per cycle
samples_per_cycle = int(fs / f_in)

# 5周期分のインデックス範囲 / Total number of samples for 5 cycles
num_samples_5cycles = samples_per_cycle * 5

# 時間軸とデータを抽出 / Extract time and ADC data for 5 cycles
t_5cycle = t[:num_samples_5cycles]
data_5cycle = data[:num_samples_5cycles]

# プロット / Plot
plt.figure(figsize=(10, 4))
plt.plot(t_5cycle, data_5cycle, marker='o', linestyle='-', label="5 Cycles of ADC Code")
plt.title("Five Cycles of ADC Output")
plt.xlabel("Time [s]")
plt.ylabel("Code")
plt.grid()
plt.tight_layout()
plt.show()

# -------------------- 入力信号とADCコードを先頭と末尾10点表示 / Display first and last 10 points of signal and ADC code --------------------

print("===== First 10 Points =====")
print(" Index |     Time [s]     |  Voltage [V]  |  ADC Code")
print("-----------------------------------------------")
for i in range(10):
    print(f"{i:6d} | {t[i]:.6f} s | {v_signal[i]:.6f} V | {data[i]:9d}")

print("\n===== Last 10 Points =====")
print(" Index |     Time [s]     |  Voltage [V]  |  ADC Code")
print("-----------------------------------------------")
for i in range(10):
    idx = -10 + i
    print(f"{len(t)+idx:6d} | {t[idx]:.6f} s | {v_signal[idx]:.6f} V | {data[idx]:9d}")




# --- 並び替え前後の波形を個別にプロット / Plot before and after sorting (separately) ---
sorted_data = wave_sort(data, fs, f_in)

# 並び替え前 / Original Wave
plt.figure(figsize=(10, 4))
plt.title("Original Wave")
plt.xlabel("Time [sec]")
plt.ylabel("Code")
plt.grid()
plt.ylim(0, 2**bit)
plt.plot(t, data, marker=".", linestyle="", markersize=4)
plt.tight_layout()
plt.show()

# 並び替え後 / Sorted Wave
plt.figure(figsize=(10, 4))
plt.title("Sorted Wave")
plt.xlabel("Time [sec]")
plt.ylabel("Code")
plt.grid()
plt.ylim(0, 2**bit)
plt.plot(t, sorted_data, marker=".", linestyle="", markersize=4)
plt.tight_layout()
plt.show()



結果

image.png

追加CSVファイルで読み込んだ波形をFFT

import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
from google.colab import files
from scipy.signal import find_peaks
from scipy.fft import fft

# ========================================
# 📥 ① CSVファイルのアップロード / Upload CSV
# ========================================
uploaded = files.upload()
filename = next(iter(uploaded))
df = pd.read_csv(filename)

# ========================================
# ⏱ ② 信号データの抽出と前処理 / Extract & preprocess signal
# ========================================
time = df["time_data"]
signal = df["signal_data"]
adjusted_signal = signal - 127  # DCオフセット除去 / Remove DC offset

# ========================================
# 📊 ③ 波形の表示 / Plot original & adjusted signals
# ========================================
plt.figure(figsize=(12, 4))
plt.plot(time, signal, label="Original Signal", color='blue', alpha=0.6)
plt.plot(time, adjusted_signal, label="Adjusted Signal", color='green', alpha=0.6)
plt.title("Original vs Adjusted Signal")
plt.xlabel("Time [s]")
plt.ylabel("Amplitude")
plt.grid(True)
plt.legend()
plt.show()

# ========================================
# 🧾 ④ 先頭と末尾の値を確認 / Print head & tail of signals
# ========================================
print("=== Original Signal: First 10 values ===")
print(signal.head(10))
print("=== Original Signal: Last 10 values ===")
print(signal.tail(10))

print("=== Adjusted Signal: First 10 values ===")
print(adjusted_signal.head(10))
print("=== Adjusted Signal: Last 10 values ===")
print(adjusted_signal.tail(10))

# ========================================
# 🎯 ⑤ サンプリング処理 / Sampling
# ========================================
sampling_freq = 10_000_000  # 10 MHz
sampling_period = 1 / sampling_freq
sampled_times = np.arange(time.iloc[0], time.iloc[-1], sampling_period)

# インデックスを検索してサンプリング / Find nearest indices
time_np = time.to_numpy()
idx = np.searchsorted(time_np, sampled_times, side="left")
idx = np.clip(idx, 0, len(time_np) - 1)

sampled_original = signal.to_numpy()[idx]
sampled_adjusted = adjusted_signal.to_numpy()[idx]

# プロット / Plot sampled signals
plt.figure(figsize=(12, 4))
plt.plot(time, signal, alpha=0.3, label="Original")
plt.scatter(sampled_times, sampled_original, s=10, c='blue', label="Sampled Original")
plt.plot(time, adjusted_signal, alpha=0.3, label="Adjusted")
plt.scatter(sampled_times, sampled_adjusted, s=10, c='green', label="Sampled Adjusted")
plt.title("Sampled Signals")
plt.xlabel("Time [s]")
plt.ylabel("Amplitude")
plt.legend()
plt.grid(True)
plt.show()

# ========================================
# ⏱ ⑥ 周波数推定 / Frequency estimation
# ========================================
def estimate_frequency(signal, label):
    peaks, _ = find_peaks(signal, height=10)
    if len(peaks) < 2:
        print(f"{label} → Not enough peaks.")
        return None
    peak_times = time.iloc[peaks].to_numpy()
    periods = np.diff(peak_times)
    avg_period = np.mean(periods)
    freq = 1 / avg_period
    print(f"{label} Frequency: {freq:.2f} Hz, Period: {avg_period:.2e} s")
    return avg_period

period_orig = estimate_frequency(signal, "Original")
period_adj = estimate_frequency(adjusted_signal, "Adjusted")

# ========================================
# 🔄 ⑦ 1周期で並び替え / Sort samples by phase
# ========================================
def sort_by_phase(t, y, period):
    phase = t % period
    sorted_idx = np.argsort(phase)
    return phase[sorted_idx], y[sorted_idx]

if period_orig and period_adj:
    phase_orig, sorted_orig = sort_by_phase(sampled_times, sampled_original, period_orig)
    phase_adj, sorted_adj = sort_by_phase(sampled_times, sampled_adjusted, period_adj)

    plt.figure(figsize=(12, 4))
    plt.plot(phase_orig, sorted_orig, '.', markersize=3, label="Original")
    plt.plot(phase_adj, sorted_adj, '.', markersize=3, label="Adjusted")
    plt.title("Sorted by Phase")
    plt.xlabel("Phase [s]")
    plt.ylabel("Amplitude")
    plt.legend()
    plt.grid(True)
    plt.show()

# ========================================
# 🔍 ⑧ FFTによるAC性能評価 / AC performance via FFT
# ========================================
def fft_ac_analysis(signal, fs, label):
    N = len(signal)
    bit_resolution = 8
    v_min = np.min(signal)
    v_max = np.max(signal)

    # 正規化(8ビットADC相当)/ Normalize to 8-bit range
    normalized = (signal - v_min) / (v_max - v_min) * (2**bit_resolution - 1)

    # FFT & パワースペクトル / FFT and power spectrum
    F = fft(normalized) * 2 / N
    Power = np.abs(F)**2
    freq = np.fft.fftfreq(N, d=1/fs)        # 実周波数 / Actual frequency
    freq_norm = freq / fs                   # 正規化周波数 / Normalized frequency
    Pow_dB = 10 * np.log10(Power + 1e-12)   # Avoid log(0)

    # 基本波成分 / Fundamental component
    signal_idx = np.argmax(Power[1:N//2]) + 1
    Signal = Power[signal_idx]
    Noise = np.sum(Power[1:N//2]) - Signal

    # SNDR, ENOB計算 / Compute SNDR and ENOB
    SNDR = 10 * np.log10(Signal / Noise)
    ENOB = (SNDR - 1.76) / 6.02

    # SFDR(スプリアス除去比)/ Spurious-Free Dynamic Range
    spurious = [np.max(Pow_dB[signal_idx * i - 2: signal_idx * i + 2]) for i in range(2, 5)]
    SFDR = Pow_dB[signal_idx] - np.max(spurious)

    # 正規化:最大成分を0dBに調整 / Normalize peak to 0 dB
    Pow_dB -= Pow_dB[signal_idx]

    # プロット / Plot
    plt.figure(figsize=(10, 4))
    plt.plot(freq_norm[1:N//2], Pow_dB[1:N//2])
    plt.title(f"{label} - Power Spectrum (Normalized to 0 dB)")
    plt.xlabel("f/fs")
    plt.ylabel("Power [dB]")
    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()

    # 結果の表示 / Print metrics
    print(f"===== {label} FFT Results =====")
    print(f"Peak Frequency Index: {signal_idx}")
    print(f"→ Normalized Freq: {freq_norm[signal_idx]:.6f} f/fs")
    print(f"→ Actual Freq:     {freq[signal_idx]:.2f} Hz")
    print(f"SNDR: {SNDR:.2f} dB")
    print(f"ENOB: {ENOB:.2f} bit")
    print(f"SFDR: {SFDR:.2f} dB")
    print("================================")

# FFT解析の実行 / Run FFT analysis
fft_ac_analysis(sampled_original, sampling_freq, "Original")
fft_ac_analysis(sampled_adjusted, sampling_freq, "Adjusted")

import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
from google.colab import files
from scipy.signal import find_peaks
from scipy.fft import fft

# ========================================
# 📥 ① CSVファイルのアップロード / Upload CSV
# ========================================
# Google Colab で CSV ファイルをアップロードする
# Upload a CSV file from your local machine into Google Colab
uploaded = files.upload()
filename = next(iter(uploaded))
df = pd.read_csv(filename)

# ========================================
# ⏱ ② 信号データの抽出と前処理 / Extract & preprocess signal
# ========================================
# time_data列を取得し、全体を0.0001秒だけ前にシフト
# Get time_data and subtract 0.0001s from all values
time = df["time_data"] - 0.0001

# 信号データを取得
# Get the raw signal values
signal = df["signal_data"]

# DCオフセット(127)を除去して中央を0に揃える
# Remove DC offset assuming center at 127 (e.g., 8-bit ADC)
adjusted_signal = signal - 127

# 時間が0以上のデータだけを残す(負の時間は無効とする)
# Filter out negative time values (keep only time >= 0)
mask = time >= 0
time = time[mask].reset_index(drop=True)
signal = signal[mask].reset_index(drop=True)
adjusted_signal = adjusted_signal[mask].reset_index(drop=True)

# ========================================
# 📊 ③ 波形の表示 / Plot original & adjusted signals
# ========================================
# 元の信号とDCオフセット除去後の信号を同時にプロット
# Plot both original and adjusted signal on the same graph
plt.figure(figsize=(12, 4))
plt.plot(time, signal, label="Original Signal", color='blue', alpha=0.6)
plt.plot(time, adjusted_signal, label="Adjusted Signal", color='green', alpha=0.6)
plt.title("Original vs Adjusted Signal")
plt.xlabel("Time [s]")
plt.ylabel("Amplitude")
plt.grid(True)
plt.legend()
plt.show()

# ========================================
# 🧾 ④ 先頭と末尾の値を確認 / Print head & tail of signals
# ========================================
# 信号と調整後信号の最初と最後の10点を表示
# Print first and last 10 values of both original and adjusted signals
print("=== Original Signal: First 10 values ===")
print(signal.head(10))
print("=== Original Signal: Last 10 values ===")
print(signal.tail(10))

print("=== Adjusted Signal: First 10 values ===")
print(adjusted_signal.head(10))
print("=== Adjusted Signal: Last 10 values ===")
print(adjusted_signal.tail(10))

# ========================================
# 🎯 ⑤ サンプリング処理 / Sampling
# ========================================
# サンプリング周波数を10MHz(=0.1μs間隔)に設定
# Set sampling frequency to 10 MHz (0.1 µs interval)
sampling_freq = 10_000_000
sampling_period = 1 / sampling_freq

# サンプリング時間軸を作成(等間隔)
# Create evenly spaced sampling timestamps
sampled_times = np.arange(time.iloc[0], time.iloc[-1], sampling_period)

# 最も近い実データ点を検索し、インデックスとして取得
# Find indices of actual data points nearest to the sampling times
time_np = time.to_numpy()
idx = np.searchsorted(time_np, sampled_times, side="left")
idx = np.clip(idx, 0, len(time_np) - 1)  # 範囲外を防ぐ / Prevent out-of-bounds

# サンプリングした信号の配列を作成
# Extract sampled signals using the indices
sampled_original = signal.to_numpy()[idx]
sampled_adjusted = adjusted_signal.to_numpy()[idx]

# サンプリング結果のプロット
# Plot sampled points over the original and adjusted signals
plt.figure(figsize=(12, 4))
plt.plot(time, signal, alpha=0.3, label="Original")
plt.scatter(sampled_times, sampled_original, s=10, c='blue', label="Sampled Original")
plt.plot(time, adjusted_signal, alpha=0.3, label="Adjusted")
plt.scatter(sampled_times, sampled_adjusted, s=10, c='green', label="Sampled Adjusted")
plt.title("Sampled Signals")
plt.xlabel("Time [s]")
plt.ylabel("Amplitude")
plt.legend()
plt.grid(True)
plt.show()

# ========================================
# ⏱ ⑥ 周波数推定 / Frequency estimation
# ========================================
# ピーク検出から信号周期・周波数を推定する関数
# Function to estimate frequency from signal peaks
def estimate_frequency(signal, label):
    peaks, _ = find_peaks(signal, height=10)
    if len(peaks) < 2:
        print(f"{label} → Not enough peaks.")
        return None
    peak_times = time.iloc[peaks].to_numpy()
    periods = np.diff(peak_times)
    avg_period = np.mean(periods)
    freq = 1 / avg_period
    print(f"{label} Frequency: {freq:.2f} Hz, Period: {avg_period:.2e} s")
    return avg_period

# オリジナルと調整後の周期を取得
# Estimate the period for original and adjusted signals
period_orig = estimate_frequency(signal, "Original")
period_adj = estimate_frequency(adjusted_signal, "Adjusted")

# ========================================
# 🔄 ⑦ 1周期で並び替え / Sort samples by phase
# ========================================
# 位相順にデータを並べ替える関数
# Sort signal data by phase modulo period
def sort_by_phase(t, y, period):
    phase = t % period
    sorted_idx = np.argsort(phase)
    return phase[sorted_idx], y[sorted_idx]

# 周期が得られていれば、位相順で並べてプロット
# If period is valid, sort and plot by phase
if period_orig and period_adj:
    phase_orig, sorted_orig = sort_by_phase(sampled_times, sampled_original, period_orig)
    phase_adj, sorted_adj = sort_by_phase(sampled_times, sampled_adjusted, period_adj)

    plt.figure(figsize=(12, 4))
    plt.plot(phase_orig, sorted_orig, '.', markersize=3, label="Original")
    plt.plot(phase_adj, sorted_adj, '.', markersize=3, label="Adjusted")
    plt.title("Sorted by Phase")
    plt.xlabel("Phase [s]")
    plt.ylabel("Amplitude")
    plt.legend()
    plt.grid(True)
    plt.show()

# ========================================
# 🔍 ⑧ FFTによるAC性能評価 / AC performance via FFT
# ========================================
# FFTを使ってSNDR, ENOB, SFDRを計算しプロットする関数
# Analyze AC performance (SNDR, ENOB, SFDR) using FFT
def fft_ac_analysis(signal, fs, label):
    N = len(signal)
    bit_resolution = 8
    v_min = np.min(signal)
    v_max = np.max(signal)

    # 0〜255に正規化(8ビットADC相当)
    # Normalize signal to 8-bit digital levels
    normalized = (signal - v_min) / (v_max - v_min) * (2**bit_resolution - 1)

    # FFTの実行とパワースペクトルの計算
    # Perform FFT and compute power spectrum
    F = fft(normalized) * 2 / N
    Power = np.abs(F)**2
    freq = np.fft.fftfreq(N, d=1/fs)
    freq_norm = freq / fs
    Pow_dB = 10 * np.log10(Power + 1e-12)  # log(0)回避 / Avoid log(0)

    # 基本波のインデックスを探す
    # Identify fundamental frequency component
    signal_idx = np.argmax(Power[1:N//2]) + 1
    Signal = Power[signal_idx]
    Noise = np.sum(Power[1:N//2]) - Signal

    # SNDRとENOBの計算
    SNDR = 10 * np.log10(Signal / Noise)
    ENOB = (SNDR - 1.76) / 6.02

    # SFDR(スプリアスの最大値との差)
    spurious = [np.max(Pow_dB[signal_idx * i - 2: signal_idx * i + 2]) for i in range(2, 5)]
    SFDR = Pow_dB[signal_idx] - np.max(spurious)

    # 最大成分を0dBに正規化
    Pow_dB -= Pow_dB[signal_idx]

    # パワースペクトルのプロット
    plt.figure(figsize=(10, 4))
    plt.plot(freq_norm[1:N//2], Pow_dB[1:N//2])
    plt.title(f"{label} - Power Spectrum (Normalized to 0 dB)")
    plt.xlabel("f/fs")
    plt.ylabel("Power [dB]")
    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()

    # 結果出力
    print(f"===== {label} FFT Results =====")
    print(f"Peak Frequency Index: {signal_idx}")
    print(f"→ Normalized Freq: {freq_norm[signal_idx]:.6f} f/fs")
    print(f"→ Actual Freq:     {freq[signal_idx]:.2f} Hz")
    print(f"SNDR: {SNDR:.2f} dB")
    print(f"ENOB: {ENOB:.2f} bit")
    print(f"SFDR: {SFDR:.2f} dB")
    print("================================")

# FFTによる解析を実行 / Run FFT analysis
fft_ac_analysis(sampled_original, sampling_freq, "Original")
fft_ac_analysis(sampled_adjusted, sampling_freq, "Adjusted")
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?