import numpy as np
from scipy import signal
from scipy import fftpack
import matplotlib.pyplot as plt
# フーリエ変換をする関数
def calc_fft(data, samplerate):
spectrum = fftpack.fft(data) # 信号のフーリエ変換
amp = np.sqrt((spectrum.real ** 2) + (spectrum.imag ** 2)) # 振幅成分
amp = amp / (len(data) / 2) # 振幅成分の正規化(辻褄合わせ)
phase = np.arctan2(spectrum.imag, spectrum.real) # 位相を計算
phase = np.degrees(phase) # 位相をラジアンから度に変換
freq = np.linspace(0, samplerate, len(data)) # 周波数軸を作成
return spectrum, amp, phase, freq
# 波形(x, y)からn個のピークを幅wで検出する関数(xは0から始まる仕様)
def findpeaks(x, y, n, w, extract):
index_all = list(signal.argrelmax(y, order=w)) # scipyのピーク検出
index = [] # ピーク指標の空リスト
peaks = [] # ピーク値の空リスト
# n個分のピーク情報(指標、値)を格納
for i in range(n):
index.append(index_all[0][i])
peaks.append(y[index_all[0][i]])
index = np.array(index) * x[1] # xの分解能x[1]をかけて指標を物理軸に変換
# 大きい順に任意数抽出する
peaks_sort = np.sort(peaks)[::-1]
index_sort = np.argsort(peaks)[::-1]
index = index[index_sort]
peaks = peaks_sort[:extract]
index = index[:extract]
return index, peaks
# ヒルベルト変換で包絡線を計算する関数
def hilbert(spectrum):
# 負周波数域(ナイキスト周波数以降)を0にする(実部と虚部の両方)
zeros = np.zeros(int(len(spectrum) / 2))
spectrum[int(len(spectrum) / 2):len(spectrum)] = zeros
# 正周波数域(ナイキスト周波数まで)を2倍する
spectrum *= 2
# 操作した実部と虚部を持つ周波数波形をIFFTし、絶対値をとる(包絡線を得る)
ifft_time = np.abs(fftpack.ifft(spectrum))
return ifft_time
# サンプル波形を生成する。
samplerate = 25600 # サンプリング周波数
t = np.arange(0, 10, 1/samplerate) # 時間軸
amp_c = 1 # キャリア振幅
freq_c = 10 # キャリア周波数
freq_am = 2 # 変調周波数
m = 0.5 # 振幅変調指数(0<=m<=1)
s = np.sin(2 * np.pi * freq_am * t) # 変調波(<=1)
wave = amp_c * (1 + m * s) * np.cos(2 * np.pi * freq_c * t) # 振幅変調波
# 振幅変調波のフーリエ変換してピーク検出
spectrum, amp, phase, freq = calc_fft(wave, samplerate)
index, peaks = findpeaks(freq, amp, n=100, w=6, extract=3)
# ヒルベルト変換でエンベロープを作成
envelope = hilbert(spectrum)
spectrum2, amp2, phase2, freq2 = calc_fft(envelope, samplerate)
# ここからグラフ描画-------------------------------------
# フォントの種類とサイズを設定する。
plt.rcParams['font.size'] = 14
plt.rcParams['font.family'] = 'Times New Roman'
# 目盛を内側にする。
plt.rcParams['xtick.direction'] = 'in'
plt.rcParams['ytick.direction'] = 'in'
# グラフの上下左右に目盛線を付ける。
fig = plt.figure()
ax1 = fig.add_subplot(211)
ax1.yaxis.set_ticks_position('both')
ax1.xaxis.set_ticks_position('both')
ax2 = fig.add_subplot(212)
ax2.yaxis.set_ticks_position('both')
ax2.xaxis.set_ticks_position('both')
# 軸のラベルを設定する。
ax1.set_xlabel('Time [s]')
ax1.set_ylabel('Amplitude')
ax2.set_xlabel('Frequency [Hz]')
ax2.set_ylabel('Amplitude')
# スケールの設定をする。
ax1.set_xlim(0, 2)
ax2.set_xticks(np.arange(0, 100, 2))
ax2.set_xlim(0, 20)
ax2.set_ylim(0, 1.5)
# データプロット
ax1.plot(t, wave, label='wave', lw=1, color='red')
ax1.plot(t, envelope, label='envelope', lw=1, color='blue', linestyle='dashed')
ax1.plot(t, envelope * -1, label='envelope', lw=1, color='blue', linestyle='dashed')
ax2.plot(freq, amp, label='wave', lw=1, color='red')
ax2.plot(freq, amp2, label='wave', lw=1, color='blue', linestyle='dashed')
ax2.scatter(index, peaks, color='yellow', linewidth=1, edgecolor='black')
for i in range(len(index)):
plt.text(index[i] - 0.5, peaks[i] + 0.1, str(round(peaks[i], 2)), fontsize=16)
plt.text(16, 1.2, 'freq_c=' + str(round(freq_c, 2)), fontsize=16)
plt.text(16, 1.0, 'freq_am=' + str(round(freq_am, 2)), fontsize=16)
plt.text(16, 0.8, 'm=' + str(round(m, 2)), fontsize=16)
# レイアウト設定
fig.tight_layout()
# グラフを表示する。
plt.show()
plt.close()
# ---------------------------------------------------
コメント