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?

コヒーレントサンプリングに必要な入力周波数を求める

Last updated at Posted at 2025-03-10

正弦波信号の並び替えと可視化

はじめに

本記事では、サンプリングされた正弦波信号の並び替え(wave sorting)を行い、
その結果を可視化するシミュレーションをPythonで実装します。

また、コヒーレントサンプリングに必要な入力周波数を求める関数 freq_search を定義し、
その計算結果を出力するコードについても解説します。

設定するパラメータ

以下のパラメータを使用してシミュレーションを行います。

  • サンプリング周波数 (fs): 1 MHz (1,000,000 サンプル/秒)
  • 入力信号周波数 (fin): freq_search 関数で算出
  • サンプル数 (N): 1024
  • シミュレーション時間: (N / fs)

コヒーレントサンプリングの周波数算出

import numpy as np

def freq_search(N: int, fs: float, finRate=8):
    """
    コヒーレントサンプリングに必要な入力周波数を求める関数
    Function to calculate the input frequency for coherent sampling.

    Parameters
    ----------
    N : int
        サンプル数(N=log2(num)) | Log2 of sample count
    fs : float
        サンプリング周波数 | Sampling frequency
    finRate : int, optional
        入力周波数の位置 (デフォルト: 8) | Desired input freq position (default: 8)

    Returns
    -------
    fin : float
        算出された入力周波数 | Calculated input frequency
    """
    freq = []
    FFT_points = 2**N  # FFTポイント数 | Number of FFT points
    for n in range(2, N):
        prime = np.exp(n * np.log(2)) - 1  # 2^n-1を算出(オーバーフロー対策) | Compute 2^n - 1
        fin_per_fs = prime / FFT_points    # 正規化周波数 | Normalized frequency
        freq.append(fin_per_fs)

    array = np.array(freq)
    index = (np.abs(array - (1 / finRate))).argmin()  # 最も近いインデックスを取得 | Find nearest index

    fin = freq[index] * fs  # 入力周波数を計算 | Calculate input frequency
    return fin

# ==== パラメータ設定 | Parameter settings ====
N = 10          # 例: 2^10 = 1024 サンプル | Sample count
fs = 1e6        # サンプリング周波数 1 MHz | Sampling frequency
finRate = 8     # 入力周波数の位置 | Target fin/fs ratio

# ==== 関数実行 | Run function ====
calculated_fin = freq_search(N, fs, finRate)

# ==== 2^N を表示 | Print 2^N ====
FFT_points = 2**N
print(f"FFT points (2^{N}) = {FFT_points}")

# ==== 結果表示 | Show results ====
print(f"Calculated Input Frequency: {calculated_fin:.3f} Hz")

# ==== シミュレーション時間を計算 | Compute simulation time ====
sim_time = (FFT_points / fs) * 1.000000000000001  # 少し余裕を持たせる | Add slight margin

# ==== シミュレーション時間を表示 | Show simulation time ====
print(f"Recommended simulation time: {sim_time:.6f} seconds")


シミュレーションコード

import matplotlib.pyplot as plt

def wave_sort(data, fs, fin):
    """
    信号の並び替えを行う関数
    :param data: 入力信号
    :param fs: サンプリング周波数
    :param fin: 入力信号周波数
    :return: 並び替え後の信号
    """
    indices = np.argsort(np.angle(np.exp(1j * 2 * np.pi * fin * np.arange(len(data)) / fs)))
    return data[indices]

# シミュレーションパラメータ
simulation_time = N / fs  # シミュレーション時間

# 時間軸
t = np.arange(0, simulation_time, 1 / fs)
original_data = np.sin(2 * np.pi * calculated_fin * t)  # 正弦波信号

# 並び替え
sorted_data = wave_sort(original_data, fs, calculated_fin)

# プロット
plt.figure(figsize=(10, 6))

# 並び替え前の波形
plt.subplot(2, 1, 1)
plt.plot(original_data, marker='o', linestyle='-', markersize=3, label="Original Data")
plt.title("Original Data")
plt.xlabel("Sample Index")
plt.ylabel("Amplitude")
plt.legend()
plt.grid()

# 並び替え後の波形
plt.subplot(2, 1, 2)
plt.plot(sorted_data, marker='o', linestyle='-', markersize=3, label="Sorted Data", color='r')
plt.title("Sorted Data")
plt.xlabel("Sample Index")
plt.ylabel("Amplitude")
plt.legend()
plt.grid()

plt.tight_layout()
plt.show()

実行結果と考察

コヒーレントサンプリングの周波数選定

  • freq_search 関数を用いて、サンプリングと整合する入力周波数を計算。
  • 計算結果を print() により確認可能。

並び替え前の波形

もとの波形は通常の正弦波信号であり、サンプリング間隔ごとにデータが取得されています。

並び替え後の波形

並び替え後の波形は、入力周波数に基づいた角度の昇順でソートされています。
これにより、波形が特定のパターンで整列されることが確認できます。

まとめ

本記事では、

  • コヒーレントサンプリングに必要な入力周波数の算出
  • サンプリング信号の並び替え
  • ソートされた波形の可視化

を実装しました。この技術は、信号処理やデータ解析の一環として利用可能です。

おまけ 活用事例

import numpy as np
import matplotlib.pyplot as plt
from scipy.fftpack import fft

# 条件設定
f_in = 124.511e3  # 入力サイン周波数 (Hz)
f_s = 1e6  # サンプリング周波数 (Hz)
N = 2048  # FFT点数
A = 1  # スケール係数

# 計算: サンプリング周期とシミュレーション時間
T_s = 1 / f_s  # サンプリング周期
T_in = 1 / f_in  # 入力サイン周期
T_sim = T_s * N * A  # シミュレーション時間

# 時間軸の生成
t = np.arange(0, T_sim, T_s)  # 0からシミュレーション時間までの時間配列

# サイン波の生成
signal = np.sin(2 * np.pi * f_in * t)

# FFT解析
spectrum = fft(signal, N)  # FFT変換
spectrum_magnitude = np.abs(spectrum[:N//2])  # FFTの前半分(正の周波数成分)
freqs = np.fft.fftfreq(N, T_s)[:N//2]  # 周波数軸

# SNDR(信号対ノイズ+歪比)計算
signal_power = np.max(spectrum_magnitude) ** 2  # 信号のピークパワー
noise_power = np.sum(spectrum_magnitude ** 2) - signal_power  # ノイズ+歪成分のパワー
SNDR = 10 * np.log10(signal_power / noise_power)  # SNDR計算

# ENOB(有効ビット数)の計算
ENOB = (SNDR - 1.76) / 6.02  # ENOB計算

# SFDR(スプリアスフリーダイナミックレンジ)の計算
spurious_spectrum = spectrum_magnitude.copy()
spurious_spectrum[np.argmax(spectrum_magnitude)] = 0  # 最大信号成分を除外
SFDR = 20 * np.log10(np.max(spectrum_magnitude) / np.max(spurious_spectrum))  # SFDR計算

# FFTスペクトルの正規化
spectrum_magnitude_db = 20 * np.log10(spectrum_magnitude / np.max(spectrum_magnitude))

# ------------------------------------
# **プロット**
# (1) 入力サイン波
# (2) FFTスペクトル
# ------------------------------------

plt.figure(figsize=(10, 6))

# ---- (1) 入力サイン波のプロット ----
plt.subplot(2, 1, 1)
plt.plot(t[:200], signal[:200], 'b-', label='Input Sine Wave')
plt.xlabel('Time [s]')
plt.ylabel('Amplitude')
plt.title(f'Sampled Sine Wave (f_in = {f_in / 1e3:.3f} kHz, f_s = {f_s / 1e6:.1f} MHz)')
plt.grid(True)
plt.legend()

# ---- (2) FFTスペクトルのプロット ----
plt.subplot(2, 1, 2)
plt.plot(freqs / f_s, spectrum_magnitude_db, 'r-', label='FFT Spectrum (Normalized)')
plt.axhline(y=0, color='k', linestyle='--', label='0 dB Reference')
plt.xlabel('Normalized Frequency (f/f_s)')
plt.ylabel('Magnitude [dB]')
plt.title('FFT Spectrum (0 dB = max amplitude)')
plt.legend()
plt.grid(True)

plt.tight_layout()
plt.show()

# ------------------------------------
# **計算結果の表示**
# ------------------------------------
print(f"入力サイン周期 (T_in): {T_in:.9f} s")
print(f"サンプリング周期 (T_s): {T_s:.9f} s")
print(f"シミュレーション時間 (T_sim): {T_sim:.9f} s")
print(f"SNDR: {SNDR:.2f} dB")
print(f"ENOB: {ENOB:.2f} bits")
print(f"SFDR: {SFDR:.2f} dB")
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?