# -*- coding: utf-8 -*-
"""
Personal Processing Functions 1 (ppfunctions_1)
Created on Mon Apr 9 11:48:37 2018
@author: Kevin MAchado
Module file containing Python definitions and statements
"""
# Libraries
import numpy as np
from scipy.signal import kaiserord, lfilter, firwin, butter
from scipy.fftpack import fft
from scipy import signal as sg
#import peakutils # Librery to help in peak detection
# Functions
# Normal energy = np.sum(pcgFFT1**2)
# Normalized average Shannon Energy = sum((l**2)*np.log(l**2))/l.shape[0]
# Third Order Shannon Energy = sum((l**3)*np.log(l**3))/l.shape[0]
# -----------------------------------------------------------------------------
# Statistic Variables
# -----------------------------------------------------------------------------
def _variance(x):
miu = 0.0
vari = 0.0
for i in range(x.shape[0]):
miu =+ x[i]
miu = miu/len(x)
for i in range(x.shape[0]):
vari = vari + (x[i]-miu)**2
vari = vari/len(x)
return vari
def _StandarD(x):
miu = 0.0
vari = 0.0
for i in range(x.shape[0]):
miu += x[i]
miu = miu/len(x)
for i in range(x.shape[0]):
vari = vari + (x[i]-miu)**2
vari = vari/len(x)
_Std = vari **(.5)
return _Std
def _CV(x):
# Coefficient of Variation
var = _variance(x)
std = _StandarD(x)
return 100*(var/std)
# -----------------------------------------------------------------------------
# PDS
# -----------------------------------------------------------------------------
def vec_nor(x):
"""
Normalize the amplitude of a vector from -1 to 1
"""
nVec=np.zeros(len(x)); # Initializate derivate vector
nVec = np.divide(x, max(x))
nVec=nVec-np.mean(nVec);
nVec=np.divide(nVec,np.max(nVec));
return nVec
def running_sum(x):
"""
Running Sum Algorithm of an input signal is y[n]= x[n] + y[n-1]
"""
y = np.zeros(len(x))
for i in range(len(x)):
y[i] = x[i] + y[i-1]
return vec_nor(y)
def derivate_1 (x):
"""
Derivate of an input signal as y[n]= x[n] - x[n-1]
"""
y=np.zeros(len(x)); # Initializate derivate vector
for i in range(len(x)):
y[i]=x[i]-x[i-1];
return vec_nor(y)
def derivate (x):
"""
Derivate of an input signal as y[n]= x[n+1]- x[n-1]
"""
lenght=x.shape[0] # Get the length of the vector
y=np.zeros(lenght); # Initializate derivate vector
for i in range(lenght-1):
y[i]=x[i-1]-x[i];
return y
def derivate_positive (x):
"""
Derivate of an input signal as y[n]= x[n+1]- x[n-1]
for all values where the signal is positive
"""
lenght=x.shape[0] # Get the length of the vector
y=np.zeros(lenght); # Initializate derivate vector
for i in range(lenght-1):
if x[i]>0:
y[i]=x[i-1]-x[i];
return y
# -----------------------------------------------------------------------------
# Energy
# -----------------------------------------------------------------------------
def Energy_value (x):
"""
Energy of an input signal
"""
y = np.sum(x**2)
return y
def shannonE_value (x):
"""
Shannon energy of an input signal
"""
y = sum((x**2)*np.log(x**2))/x.shape[0]
return y
def shannonE_vector (x):
"""
Shannon energy of an input signal
"""
mu = -(x**2)*np.log(x**2)/x.shape[0]
y = -(((x**2)*np.log(x**2)) - mu)/np.std(x)
return y
def shannonE_vector_1 (x):
"""
Shannon energy of an input signal
"""
N = x.shape[0]
# Se = -(1/N) *
mu = -(x**2)*np.log(x**2)/x.shape[0]
y = -(((x**2)*np.log(x**2)) - mu)/np.std(x)
return y
def E_VS_LF (pcgFFT1, vTfft1, on):
"""
Energy of PCG Vibratory Spectrum in low Frequencies (E_VS_LF)
(frequency components, frequency value vector, on = on percentage or not)
According with [1] The total vibratory spectrum can be divided into 7 bands.
This is a modification of this 7 bands
1. 0-5Hz, 2. 5-25Hz; 3. 25-60Hz; 4. 60-120Hz; 5. 120-400Hz
The PCG signal producess vibrations in the spectrum between 0-2k Hz.
[1] Abbas, Abbas K. (Abbas Khudair), Bassam, Rasha and Morgan & Claypool Publishers Phonocardiography signal processing. Morgan & Claypool Publishers, San Rafael, Calif, 2009.
"""
c1 = (np.abs(vTfft1-5)).argmin()
c2 = (np.abs(vTfft1-25)).argmin()
c3 = (np.abs(vTfft1-120)).argmin()
c4 = (np.abs(vTfft1-240)).argmin()
c5 = (np.abs(vTfft1-500)).argmin()
# All vector energy
xAll = Energy_value(pcgFFT1)
# Procesando de 0.01-5 Hz
pcgFFT_F1 = pcgFFT1[0:c1]
x1 = Energy_value(pcgFFT_F1)
# Procesando de 5-25 Hz
pcgFFT_F2 = pcgFFT1[c1:c2]
x2 = Energy_value(pcgFFT_F2)
# Procesando de 25-120 Hz
pcgFFT_F3 = pcgFFT1[c2:c3]
x3 = Energy_value(pcgFFT_F3)
# Procesando de 120-240 Hz
pcgFFT_F4 = pcgFFT1[c3:c4]
x4 = Energy_value(pcgFFT_F4)
# Procesando de 240-500 Hz
pcgFFT_F5 = pcgFFT1[c4:c5]
x5 = Energy_value(pcgFFT_F5)
x = np.array([xAll, x1, x2, x3, x4, x5])
if (on == 'percentage'):
x = 100*(x/x[0])
return x
def E_VS (pcgFFT1, vTfft1, on):
"""
Energy of PCG Vibratory Spectrum
(frequency components, frequency value vector, on = on percentage or not)
According with [1] The total vibratory spectrum can be divided into 7 bands:
1. 0-5Hz, 2. 5-25Hz; 3. 25-120Hz; 4. 120-240Hz; 5. 240-500Hz; 6. 500-1000Hz; 7. 1000-2000Hz
The PCG signal producess vibrations in the spectrum between 0-2k Hz.
[1] Abbas, Abbas K. (Abbas Khudair), Bassam, Rasha and Morgan & Claypool Publishers Phonocardiography signal processing. Morgan & Claypool Publishers, San Rafael, Calif, 2009.
"""
c1 = (np.abs(vTfft1-5)).argmin()
c2 = (np.abs(vTfft1-25)).argmin()
c3 = (np.abs(vTfft1-120)).argmin()
c4 = (np.abs(vTfft1-240)).argmin()
c5 = (np.abs(vTfft1-500)).argmin()
c6 = (np.abs(vTfft1-1000)).argmin()
c7 = (np.abs(vTfft1-2000)).argmin()
# All vector energy
xAll = Energy_value(pcgFFT1)
# Procesando de 0.01-5 Hz
pcgFFT_F1 = pcgFFT1[0:c1]
x1 = Energy_value(pcgFFT_F1)
# Procesando de 5-25 Hz
pcgFFT_F2 = pcgFFT1[c1:c2]
x2 = Energy_value(pcgFFT_F2)
# Procesando de 25-120 Hz
pcgFFT_F3 = pcgFFT1[c2:c3]
x3 = Energy_value(pcgFFT_F3)
# Procesando de 120-240 Hz
pcgFFT_F4 = pcgFFT1[c3:c4]
x4 = Energy_value(pcgFFT_F4)
# Procesando de 240-500 Hz
pcgFFT_F5 = pcgFFT1[c4:c5]
x5 = Energy_value(pcgFFT_F5)
# Procesando de 500-1000 Hz
pcgFFT_F6 = pcgFFT1[c5:c6]
x6 = Energy_value(pcgFFT_F6)
# Procesando de 1000-2000 Hz
pcgFFT_F7 = pcgFFT1[c6:c7]
x7 = Energy_value(pcgFFT_F7)
x = np.array([xAll, x1, x2, x3, x4, x5, x6, x7])
if (on == 'percentage'):
x = 100*(x/x[0])
return x
#-------------------------------------------
def features_1(data_P1, fs):
# Defining the Vibratory Frequency Bands
bVec = [0.01, 120, 240, 350, 425, 500, 999]
# Initializing Vectors
band_matrix = np.zeros((len(bVec),len(data_P1))) # Band Matrix
power_matrix = np.zeros((len(bVec),int(1+(len(data_P1)/2)))) # Band Matrix
freqs_matrix = np.zeros((len(bVec),int(1+(len(data_P1)/2)))) # Band Matrix
SePCG = np.zeros(len(bVec)-1) # Shannon Energy in each Band
SePWR = np.zeros(len(bVec)-1) # Shannon Energy in each Band
for i in range(len(bVec)-1):
band_matrix[i,:] = butter_bp_fil(data_P1, bVec[i], bVec[i+1], fs)
freqs_matrix[i,:], power_matrix[i,:] = sg.periodogram(band_matrix[i,:], fs, scaling = 'density')
SePCG[i] = Energy_value(band_matrix[i,:])
SePWR[i] = Energy_value(power_matrix[i,:])
SePWR = 1*np.log10(SePWR)
SePCG = 1*np.log10(SePCG)
return SePWR, SePCG
# -----------------------------------------------------------------------------
# Filter Processes
# -----------------------------------------------------------------------------
def recursive_moving_average_F(X, Fs, M):
"""
The recursive Moving Average Filter its an algorithm to implement the typical
moving average filter more faster. The algorithm is written as:
y[i] = y[i-1] + x[i+p] - x[i-q]
p = (M - 1)/2, q = p + 1
Ref: Udemy Course "Digital Signal Processing (DSP) From Ground Up™ in Python",
available in: https://www.udemy.com/python-dsp/
---------------------------------------------------------------------------
X: input signal
Fs: Sampling Frequency
M: number of points in M range of time the moving average
"""
p = int(((M*Fs)-1)/2)
q = p + 1
Y = np.zeros(len(X))
for i in range(len(X)):
Y[i] = Y[i-1] + X[i-p] - X[i-q]
return vec_nor(Y)
def butter_bp_coe(lowcut, highcut, fs, order=1):
"""
Butterworth passband filter coefficients b and a
Ref:
[1] https://timsainb.github.io/spectrograms-mfccs-and-inversion-in-python.html
[2] https://gist.github.com/kastnerkyle/179d6e9a88202ab0a2fe
"""
nyq = 0.5 * fs
low = lowcut / nyq
high = highcut / nyq
b, a = butter(order, [low, high], btype='band')
return b, a
def butter_bp_fil(data, lowcut, highcut, fs, order=1):
"""
Butterworth passband filter
Ref:
[1] https://timsainb.github.io/spectrograms-mfccs-and-inversion-in-python.html
[2] https://gist.github.com/kastnerkyle/179d6e9a88202ab0a2fe
"""
b, a = butter_bp_coe(lowcut, highcut, fs, order=order)
y = lfilter(b, a, data)
return vec_nor(y)
def Fpass(X,lp):
"""
Fpass is the function to pass the coefficients of a filter trough a signal'
"""
llp=np.size(lp) # Get the length of the lowpass vector
x=np.convolve(X,lp); # Disrete convolution
x=x[int(llp/2):-int(llp/2)];
x=x-(np.mean(x));
x=x/np.max(x);
y=vec_nor(x); # Vector Normalizing
return y
def FpassBand(X,hp,lp):
"""
FpassBand is the function that develop a pass band filter of the signal 'x' through the
discrete convolution of this 'x' first with the coeficients of a High Pass Filter 'hp' and then
with the discrete convolution of this result with a Low Pass Filter 'lp'
"""
llp=np.shape(lp) # Get the length of the lowpass vector
llp=llp[0]; # Get the value of the length
lhp=np.shape(hp) # Get the length of the highpass vector
lhp=lhp[0]; # Get the value of the length
x=np.convolve(X,lp); # Disrete convolution
x=x[int(llp/2):-int(llp/2)];
x=x-(np.mean(x));
x=x/np.max(x);
y=np.convolve(x,hp); # Disrete onvolution
y=y[int(lhp/2):-int(lhp/2)];
y=y-np.mean(y);
y=y/np.max(y);
x=np.convolve(y,lp); # Disrete convolution
x=x[int(llp/2):-int(llp/2)];
x=x-(np.mean(x));
x=x/np.max(x);
y=np.convolve(x,hp); # Disrete onvolution
y=y[int(lhp/2):-int(lhp/2)];
y=y-np.mean(y);
y=y/np.max(y);
y=vec_nor(y); # Vector Normalizing
return y
def bandpass_filter(data, lowcut, highcut, signal_freq, filter_order):
"""
Method responsible for creating and applying Butterworth filter.
:param deque data: raw data
:param float lowcut: filter lowcut frequency value
:param float highcut: filter highcut frequency value
:param int signal_freq: signal frequency in samples per second (Hz)
:param int filter_order: filter order
:return array: filtered data
"""
nyquist_freq = 0.5 * signal_freq
low = lowcut / nyquist_freq
high = highcut / nyquist_freq
b, a = butter(filter_order, [low, high], btype="band")
y = lfilter(b, a, data)
return y
def FpassBand_1(X,Fs, f1, f2):
"""
Ref: http://scipy-cookbook.readthedocs.io/items/FIRFilter.html
http://lagrange.univ-lyon1.fr/docs/scipy/0.17.1/generated/scipy.signal.firwin.html
FpassBand_1 is a function to develop a passband filter using 'firwin'
and 'lfilter' functions from the "Scipy" library
"""
# The Nyquist rate of the signal.
nyq_rate = Fs / 2.0
# The desired width of the transition from pass to stop,
# relative to the Nyquist rate. We'll design the filter
# with a 5 Hz transition width.
width = 5.0/nyq_rate
# The desired attenuation in the stop band, in dB.
ripple_db = 60.0
# Compute the order and Kaiser parameter for the FIR filter.
N, beta = kaiserord(ripple_db, width)
#
taps = firwin(100, [f1, f2], pass_zero=False)
# taps = firwin(N, L_cutoff_hz/nyq_rate, window=('kaiser', beta))
# taps_2 = firwin(N, H_cutoff_hz/nyq_rate, pass_zero=True)
# Use lfilter to filter x with the FIR filter.
X_pb= lfilter(taps, 1.0, X)
# X_pb= lfilter(taps_2, 1.0, X_l)
return X_pb[N-1:]
def FpassBand_2(X, f1, f2, Fs):
Y = FhighPass(X, Fs, f1)
Y = FlowPass(X, Fs, f2)
return Y
def FhighPass(X, Fs, H_cutoff_hz):
"""
Ref: http://scipy-cookbook.readthedocs.io/items/FIRFilter.html
http://lagrange.univ-lyon1.fr/docs/scipy/0.17.1/generated/scipy.signal.firwin.html
FhighPass is a function to develop a highpass filter using 'firwin'
and 'lfilter' functions from the "Scipy" library
"""
# The Nyquist rate of the signal.
nyq_rate = Fs / 2.0
# The desired width of the transition from pass to stop,
# relative to the Nyquist rate. We'll design the filter
# with a 5 Hz transition width.
width = 5.0/nyq_rate
# The desired attenuation in the stop band, in dB.
ripple_db = 60.0
# Compute the order and Kaiser parameter for the FIR filter.
N, beta = kaiserord(ripple_db, width)
# Use firwin with a Kaiser window to create a lowpass FIR filter.
taps_2 = firwin(N, H_cutoff_hz/nyq_rate, pass_zero=False)
# Use lfilter to filter x with the FIR filter.
X_h= lfilter(taps_2, 1.0, X)
return X_h[N-1:]
def FlowPass(X, Fs, L_cutoff_hz):
"""
Ref: http://scipy-cookbook.readthedocs.io/items/FIRFilter.html
http://lagrange.univ-lyon1.fr/docs/scipy/0.17.1/generated/scipy.signal.firwin.html
FlowPass is a function to develop a lowpass filter using 'firwin'
and 'lfilter' functions from the "Scipy" library
"""
# The Nyquist rate of the signal.
nyq_rate = Fs / 2.0
# The desired width of the transition from pass to stop,
# relative to the Nyquist rate. We'll design the filter
# with a 5 Hz transition width.
width = 5.0/nyq_rate
# The desired attenuation in the stop band, in dB.
ripple_db = 60.0
# Compute the order and Kaiser parameter for the FIR filter.
N, beta = kaiserord(ripple_db, width)
# Use firwin with a Kaiser window to create a lowpass FIR filter.
taps = firwin(N, L_cutoff_hz/nyq_rate, window=('kaiser', beta))
# Use lfilter to filter x with the FIR filter.
X_l= lfilter(taps, 1.0, X)
return X_l[N-1:]
# -----------------------------------------------------------------------------
# Segmentation By Running Sum Algorithm & Filters
# -----------------------------------------------------------------------------
def seg_RSA1(x, fs):
# # Appliying Running Sum Algorithm of PCG filtered from 0.01Hz to 1kHz
F_x = running_sum(vec_nor(butter_bp_fil(x, 0.01, 50, fs)))
# Smoothing the signal by filtering from 0.01Hz to 5Hz
F_x = butter_bp_fil(F_x,0.01, 2, fs)
# Appliying 1st derivative to indentify slope sign changes
xx = derivate_1(F_x)
#
xx = butter_bp_fil(xx, 0.01,2, fs)
# Transforming positives to 1 & negatives to -1
xxS = np.sign(xx)
return xxS, F_x
# -----------------------------------------------------------------------------
# Segmentation By Derivatives
# -----------------------------------------------------------------------------
def seg_Der1(x, fs):
# Segmenting in an specific frequency band
pcgPeaks, peaks, allpeaks = PDP(x, fs)
# -----------------------------------------------------------------------------
# Time processing
dT = 0.4 # [1] mean S1 duration 122ms, mean S2 duration 92ms
timeV = []
timeV.append(0)
pointV = []
pointV.append(0)
segmV = np.zeros(len(x))
for i in range(len(pcgPeaks)-1):
if pcgPeaks[i]>0.5:
timeV.append(i/fs) # Gives the time when a peak get found
pointV.append(i) # Gives the time when a peak get found
if (pointV[-1]/fs)-(pointV[-2]/fs)> dT:
segmV[pointV[-2]:pointV[-1]] = 0.4 # Marc a diastolic segment
else:
segmV[pointV[-2]:pointV[-1]] = 0.6 # Marc a systolic segment
return segmV, pcgPeaks
def seg_Der2(x, fs):
# Appliying Running Sum Algorithm of PCG filtered from 0.01Hz to 1kHz
F_x = running_sum(vec_nor(butter_bp_fil(x, 0.01, 999, fs)))
# Smoothing the signal by filtering from 0.01Hz to 5Hz
F_x = butter_bp_fil(F_x,0.01, 5, fs)
# Time to be represented in samples
time_samples = 0.5
# Number of samples to move over the signal
mC = int(time_samples * fs)
peaks = np.zeros(len(F_x))
p = sg.find_peaks(F_x, distance=mC)
# Defining peaks as +1
for i in range (len(p[0][:])):
peaks[p[0][i]] = 1
return peaks
# -----------------------------------------------------------------------------
# Peak Detection
# -----------------------------------------------------------------------------
def PDP(Xf, samplerate):
"""
Peak Detection Process
"""
timeCut = samplerate*0.25 # Time to count another pulse
vCorte = 0.6 # Amplitude threshold
Xf = vec_nor(Xf) # Normalize signal
dX = derivate_positive(Xf); # Derivate of the signal
dX = vec_nor(dX); # Vector Normalizing
size=np.shape(Xf) # Rank or dimension of the array
fil=size[0]; # Number of rows
positive=np.zeros((1,fil+1)); # Initializating Vector
positive=positive[0]; # Getting the Vector
points=np.zeros((1,fil)); # Initializating the Peak Points Vector
points=points[0]; # Getting the point vector
points1=np.zeros((1,fil)); # Initializating the Peak Points Vector
points1=points1[0]; # Getting the point vector
'''
FIRST! having the positives values of the slope as 1
And the negative values of the slope as 0
'''
for i in range(0,fil):
if Xf[i] > 0:
if dX[i]>0:
positive[i] = Xf[i];
else:
positive[i] = 0;
'''
SECOND! a peak will be found when the ith value is equal to 1 &&
the ith+1 is equal to 0
'''
for i in range(0,fil):
if (positive[i]==Xf[i] and positive[i+1]==0):
points[i] = Xf[i];
else:
points[i] = 0;
'''
THIRD! Define a minimun Peak Height
'''
p=0;
for i in range(0,fil):
if (Xf[i] > vCorte and p==0):
p = i
points1[i] = Xf[i]
else:
points1[i] = 0
if (p+timeCut < i):
p = 0
return points1, points, positive[0:(len(positive)-1)]
# -----------------------------------------------------------------------------
# Peak Detection 2
# -----------------------------------------------------------------------------
def PDP_2(Xf, samplerate):
"""
Peak Detection Process
"""
timeCut = samplerate*0.25 # Time to count another pulse
vCorte = 0.6 # Amplitude threshold
#Xf = vec_nor(Xf) # Normalize signal
dX = np.diff(Xf); # Derivate of the signal
dX = vec_nor(dX); # Vector Normalizing
size=np.shape(Xf) # Rank or dimension of the array
fil=size[0]; # Number of rows
positive=np.zeros((1,fil+1)); # Initializating Vector
positive=positive[0]; # Getting the Vector
points=np.zeros((1,fil)); # Initializating the Peak Points Vector
points=points[0]; # Getting the point vector
points1=np.zeros((1,fil)); # Initializating the Peak Points Vector
points1=points1[0]; # Getting the point vector
'''
FIRST! having the positives values of the slope as 1
And the negative values of the slope as 0
'''
for i in range(0,fil):
if Xf[i] > 0:
if dX[i]>0:
positive[i] = Xf[i];
else:
positive[i] = 0;
'''
SECOND! a peak will be found when the ith value is equal to 1 &&
the ith+1 is equal to 0
'''
for i in range(0,fil):
if (positive[i]==Xf[i] and positive[i+1]==0):
points[i] = Xf[i];
else:
points[i] = 0;
'''
THIRD! Define a minimun Peak Height
'''
p=0;
for i in range(0,fil):
if (Xf[i] > vCorte and p==0):
p = i
points1[i] = Xf[i]
else:
points1[i] = 0
if (p+timeCut < i):
p = 0
return points1, points, positive[0:(len(positive)-1)]
# -----------------------------------------------------------------------------
# Peak Detection 3 - Janko Slavic
# -----------------------------------------------------------------------------
def findpeaks(data, spacing=1, limit=None):
"""
Janko Slavic peak detection algorithm and implementation.
https://github.com/jankoslavic/py-tools/tree/master/findpeaks
Finds peaks in `data` which are of `spacing` width and >=`limit`.
:param ndarray data: data
:param float spacing: minimum spacing to the next peak (should be 1 or more)
:param float limit: peaks should have value greater or equal
:return array: detected peaks indexes array
"""
len = data.size
x = np.zeros(len + 2 * spacing)
x[:spacing] = data[0] - 1.e-6
x[-spacing:] = data[-1] - 1.e-6
x[spacing:spacing + len] = data
peak_candidate = np.zeros(len)
peak_candidate[:] = True
for s in range(spacing):
start = spacing - s - 1
h_b = x[start: start + len] # before
start = spacing
h_c = x[start: start + len] # central
start = spacing + s + 1
h_a = x[start: start + len] # after
peak_candidate = np.logical_and(peak_candidate, np.logical_and(h_c > h_b, h_c > h_a))
ind = np.argwhere(peak_candidate)
ind = ind.reshape(ind.size)
if limit is not None:
ind = ind[data[ind] > limit]
return ind
# -----------------------------------------------------------------------------
# Fast Fourier Transform
# -----------------------------------------------------------------------------
def fft_k(data, samplerate, showFrequency):
'''
Fast Fourier Transform using fftpack from scipy
Ref: https://docs.scipy.org/doc/scipy/reference/tutorial/fftpack.html
'''
# FFT Full Vector 'k' coefficients
pcgFFT = fft(data)
# FFT positives values
short_pcgFFT = 2.0/np.size(data) * np.abs(pcgFFT[0:(np.size(data)//2)])
#short_pcgFFT = 2.0/np.size(data) * np.abs(pcgFFT[(np.size(data)//2):None]) vector selected from the middle to the end
# Vector of frequencies (X-axes)
vTfft = np.linspace(0.0, 1.0/(2.0*(1/samplerate)), np.size(data)//2)
# find the value closest to a value
idx = (np.abs(vTfft-showFrequency)).argmin()
return short_pcgFFT[0:idx], vTfft[0:idx]
def fft_k_N(data, samplerate, showFrequency):
'''
Normalized Fast Fourier Transform
Ref: https://docs.scipy.org/doc/scipy/reference/tutorial/fftpack.html
'''
# FFT Full Vector 'k' coefficients
pcgFFT = fft(data)
# FFT positives values from the middle to the end (to evoid the interference at beginning)
short_pcgFFT = 2.0/np.size(data) * np.abs(pcgFFT[0:(np.size(data)//2)])
#short_pcgFFT = 2.0/np.size(data) * np.abs(pcgFFT[(np.size(data)//2):None]) vector selected from the middle to the end
# Vector of frequencies (X-axes)
vTfft = np.linspace(0.0, 1.0/(2.0*(1/samplerate)), np.size(data)//2)
# find the value closest to a value
idx = (np.abs(vTfft-showFrequency)).argmin()
return vec_nor(short_pcgFFT[0:idx]), vTfft[0:idx]
# -----------------------------------------------------------------------------
# PCG Audio Pre-Processing
# -----------------------------------------------------------------------------
def pre_pro_audio_PCG(x, fs):
# Ensure having a Mono sound
if len(x.shape)>1:
# select the left size
x = x[:,0]
# Resampling Audio PCG to 2k Hz
Frs = 2000
Nrs = int(Frs*(len(x)/fs))
if fs > Frs:
x = sg.resample(x, Nrs)
return vec_nor(x), Frs
# -----------------------------------------------------------------------------
# Pre-Process: Signal basic information (SBI)
# -----------------------------------------------------------------------------
def pre_pro_basicInfo_PCG(x, fs):
# find the time duration of the sound
t_sound = 1/fs*len(x)
# make a vector time for the sound
vec_time = np.linspace(0, t_sound, len(x))
return t_sound, vec_time