|
a |
|
b/ppfunctions_1.py |
|
|
1 |
# -*- coding: utf-8 -*- |
|
|
2 |
""" |
|
|
3 |
Personal Processing Functions 1 (ppfunctions_1) |
|
|
4 |
Created on Mon Apr 9 11:48:37 2018 |
|
|
5 |
@author: Kevin MAchado |
|
|
6 |
|
|
|
7 |
Module file containing Python definitions and statements |
|
|
8 |
""" |
|
|
9 |
|
|
|
10 |
# Libraries |
|
|
11 |
import numpy as np |
|
|
12 |
from scipy.signal import kaiserord, lfilter, firwin |
|
|
13 |
from scipy.fftpack import fft |
|
|
14 |
#import peakutils # Librery to help in peak detection |
|
|
15 |
|
|
|
16 |
# Functions |
|
|
17 |
# Normal energy = np.sum(pcgFFT1**2) |
|
|
18 |
# Normalized average Shannon Energy = sum((l**2)*np.log(l**2))/l.shape[0] |
|
|
19 |
# Third Order Shannon Energy = sum((l**3)*np.log(l**3))/l.shape[0] |
|
|
20 |
|
|
|
21 |
def vec_nor(x): |
|
|
22 |
""" |
|
|
23 |
Normalize the amplitude of a vector from -1 to 1 |
|
|
24 |
""" |
|
|
25 |
lenght=np.size(x) # Get the length of the vector |
|
|
26 |
xMax=max(x); # Get the maximun value of the vector |
|
|
27 |
nVec=np.zeros(lenght); # Initializate derivate vector |
|
|
28 |
nVec = np.divide(x, xMax) |
|
|
29 |
nVec=nVec-np.mean(nVec); |
|
|
30 |
nVec=np.divide(nVec,np.max(nVec)); |
|
|
31 |
|
|
|
32 |
return nVec |
|
|
33 |
|
|
|
34 |
def derivate (x): |
|
|
35 |
""" |
|
|
36 |
Derivate of an input signal as y[n]= x[n+1]- x[n-1] |
|
|
37 |
""" |
|
|
38 |
lenght=x.shape[0] # Get the length of the vector |
|
|
39 |
y=np.zeros(lenght); # Initializate derivate vector |
|
|
40 |
for i in range(lenght-1): |
|
|
41 |
y[i]=x[i-1]-x[i]; |
|
|
42 |
return y |
|
|
43 |
# ----------------------------------------------------------------------------- |
|
|
44 |
# Energy |
|
|
45 |
# ----------------------------------------------------------------------------- |
|
|
46 |
def Energy_value (x): |
|
|
47 |
""" |
|
|
48 |
Energy of an input signal |
|
|
49 |
""" |
|
|
50 |
y = np.sum(x**2) |
|
|
51 |
return y |
|
|
52 |
|
|
|
53 |
def E_VS (pcgFFT1, vTfft1, on): |
|
|
54 |
""" |
|
|
55 |
Energy of PCG Vibratory Spectrum |
|
|
56 |
(frequency components, frequency value vector, on = on percentage or not) |
|
|
57 |
According with [1] The total vibratory spectrum can be divided into 7 bands: |
|
|
58 |
1. 0-5Hz, 2. 5-25Hz; 3. 25-120Hz; 4. 120-240Hz; 5. 240-500Hz; 6. 500-1000Hz; 7. 1000-2000Hz |
|
|
59 |
|
|
|
60 |
The PCG signal producess vibrations in the spectrum between 0-2k Hz. |
|
|
61 |
[1] Abbas, Abbas K. (Abbas Khudair), Bassam, Rasha and Morgan & Claypool Publishers Phonocardiography signal processing. Morgan & Claypool Publishers, San Rafael, Calif, 2009. |
|
|
62 |
""" |
|
|
63 |
c1 = (np.abs(vTfft1-5)).argmin() |
|
|
64 |
c2 = (np.abs(vTfft1-25)).argmin() |
|
|
65 |
c3 = (np.abs(vTfft1-120)).argmin() |
|
|
66 |
c4 = (np.abs(vTfft1-240)).argmin() |
|
|
67 |
c5 = (np.abs(vTfft1-500)).argmin() |
|
|
68 |
c6 = (np.abs(vTfft1-1000)).argmin() |
|
|
69 |
c7 = (np.abs(vTfft1-2000)).argmin() |
|
|
70 |
|
|
|
71 |
# All vector energy |
|
|
72 |
xAll = Energy_value(pcgFFT1) |
|
|
73 |
|
|
|
74 |
# Procesando de 0.01-5 Hz |
|
|
75 |
pcgFFT_F1 = pcgFFT1[0:c1] |
|
|
76 |
x1 = Energy_value(pcgFFT_F1) |
|
|
77 |
|
|
|
78 |
# Procesando de 5-25 Hz |
|
|
79 |
pcgFFT_F2 = pcgFFT1[c1:c2] |
|
|
80 |
x2 = Energy_value(pcgFFT_F2) |
|
|
81 |
|
|
|
82 |
# Procesando de 25-120 Hz |
|
|
83 |
pcgFFT_F3 = pcgFFT1[c2:c3] |
|
|
84 |
x3 = Energy_value(pcgFFT_F3) |
|
|
85 |
|
|
|
86 |
# Procesando de 120-240 Hz |
|
|
87 |
pcgFFT_F4 = pcgFFT1[c3:c4] |
|
|
88 |
x4 = Energy_value(pcgFFT_F4) |
|
|
89 |
|
|
|
90 |
# Procesando de 240-500 Hz |
|
|
91 |
pcgFFT_F5 = pcgFFT1[c4:c5] |
|
|
92 |
x5 = Energy_value(pcgFFT_F5) |
|
|
93 |
|
|
|
94 |
# Procesando de 500-1000 Hz |
|
|
95 |
pcgFFT_F6 = pcgFFT1[c5:c6] |
|
|
96 |
x6 = Energy_value(pcgFFT_F6) |
|
|
97 |
|
|
|
98 |
# Procesando de 1000-2000 Hz |
|
|
99 |
pcgFFT_F7 = pcgFFT1[c6:c7] |
|
|
100 |
x7 = Energy_value(pcgFFT_F7) |
|
|
101 |
|
|
|
102 |
x = np.array([xAll, x1, x2, x3, x4, x5, x6, x7]) |
|
|
103 |
|
|
|
104 |
if (on == 'percentage'): |
|
|
105 |
x = 100*(x/x[0]) |
|
|
106 |
|
|
|
107 |
return x |
|
|
108 |
|
|
|
109 |
def shannonE_value (x): |
|
|
110 |
""" |
|
|
111 |
Shannon energy of an input signal |
|
|
112 |
""" |
|
|
113 |
y = sum((x**2)*np.log(x**2))/x.shape[0] |
|
|
114 |
return y |
|
|
115 |
|
|
|
116 |
def shannonE_vector (x): |
|
|
117 |
""" |
|
|
118 |
Shannon energy of an input signal |
|
|
119 |
""" |
|
|
120 |
mu = -(x**2)*np.log(x**2)/x.shape[0] |
|
|
121 |
y = -(((x**2)*np.log(x**2)) - mu)/np.std(x) |
|
|
122 |
return y |
|
|
123 |
|
|
|
124 |
# ----------------------------------------------------------------------------- |
|
|
125 |
# Filter Processes |
|
|
126 |
# ----------------------------------------------------------------------------- |
|
|
127 |
def Fpass(X,lp): |
|
|
128 |
""" |
|
|
129 |
Fpass is the function to pass the coefficients of a filter trough a signal' |
|
|
130 |
""" |
|
|
131 |
llp=np.size(lp) # Get the length of the lowpass vector |
|
|
132 |
|
|
|
133 |
x=np.convolve(X,lp); # Disrete convolution |
|
|
134 |
x=x[int(llp/2):-int(llp/2)]; |
|
|
135 |
x=x-(np.mean(x)); |
|
|
136 |
x=x/np.max(x); |
|
|
137 |
|
|
|
138 |
y=vec_nor(x); # Vector Normalizing |
|
|
139 |
|
|
|
140 |
return y |
|
|
141 |
|
|
|
142 |
def FpassBand(X,hp,lp): |
|
|
143 |
""" |
|
|
144 |
FpassBand is the function that develop a pass band filter of the signal 'x' through the |
|
|
145 |
discrete convolution of this 'x' first with the coeficients of a High Pass Filter 'hp' and then |
|
|
146 |
with the discrete convolution of this result with a Low Pass Filter 'lp' |
|
|
147 |
""" |
|
|
148 |
llp=np.shape(lp) # Get the length of the lowpass vector |
|
|
149 |
llp=llp[0]; # Get the value of the length |
|
|
150 |
lhp=np.shape(hp) # Get the length of the highpass vector |
|
|
151 |
lhp=lhp[0]; # Get the value of the length |
|
|
152 |
|
|
|
153 |
x=np.convolve(X,lp); # Disrete convolution |
|
|
154 |
x=x[int(llp/2):-int(llp/2)]; |
|
|
155 |
x=x-(np.mean(x)); |
|
|
156 |
x=x/np.max(x); |
|
|
157 |
|
|
|
158 |
y=np.convolve(x,hp); # Disrete onvolution |
|
|
159 |
y=y[int(lhp/2):-int(lhp/2)]; |
|
|
160 |
y=y-np.mean(y); |
|
|
161 |
y=y/np.max(y); |
|
|
162 |
|
|
|
163 |
x=np.convolve(y,lp); # Disrete convolution |
|
|
164 |
x=x[int(llp/2):-int(llp/2)]; |
|
|
165 |
x=x-(np.mean(x)); |
|
|
166 |
x=x/np.max(x); |
|
|
167 |
|
|
|
168 |
y=np.convolve(x,hp); # Disrete onvolution |
|
|
169 |
y=y[int(lhp/2):-int(lhp/2)]; |
|
|
170 |
y=y-np.mean(y); |
|
|
171 |
y=y/np.max(y); |
|
|
172 |
|
|
|
173 |
y=vec_nor(y); # Vector Normalizing |
|
|
174 |
|
|
|
175 |
return y |
|
|
176 |
|
|
|
177 |
def FpassBand_1(X,Fs,H_cutoff_hz, L_cutoff_hz): |
|
|
178 |
""" |
|
|
179 |
Ref: http://scipy-cookbook.readthedocs.io/items/FIRFilter.html |
|
|
180 |
http://lagrange.univ-lyon1.fr/docs/scipy/0.17.1/generated/scipy.signal.firwin.html |
|
|
181 |
FpassBand_1 is a function to develop a passband filter using 'firwin' |
|
|
182 |
and 'lfilter' functions from the "Scipy" library |
|
|
183 |
""" |
|
|
184 |
|
|
|
185 |
# The Nyquist rate of the signal. |
|
|
186 |
nyq_rate = Fs / 2.0 |
|
|
187 |
# The desired width of the transition from pass to stop, |
|
|
188 |
# relative to the Nyquist rate. We'll design the filter |
|
|
189 |
# with a 5 Hz transition width. |
|
|
190 |
width = 5.0/nyq_rate |
|
|
191 |
# The desired attenuation in the stop band, in dB. |
|
|
192 |
ripple_db = 60.0 |
|
|
193 |
# Compute the order and Kaiser parameter for the FIR filter. |
|
|
194 |
N, beta = kaiserord(ripple_db, width) |
|
|
195 |
# Use firwin with a Kaiser window to create a lowpass FIR filter. |
|
|
196 |
taps = firwin(N, L_cutoff_hz/nyq_rate, window=('kaiser', beta)) |
|
|
197 |
taps_2 = firwin(N, H_cutoff_hz/nyq_rate, pass_zero=False) |
|
|
198 |
# Use lfilter to filter x with the FIR filter. |
|
|
199 |
X_l= lfilter(taps, 1.0, X) |
|
|
200 |
X_pb= lfilter(taps_2, 1.0, X_l) |
|
|
201 |
|
|
|
202 |
return X_pb[N-1:] |
|
|
203 |
|
|
|
204 |
def FhighPass(X, Fs, H_cutoff_hz): |
|
|
205 |
""" |
|
|
206 |
Ref: http://scipy-cookbook.readthedocs.io/items/FIRFilter.html |
|
|
207 |
http://lagrange.univ-lyon1.fr/docs/scipy/0.17.1/generated/scipy.signal.firwin.html |
|
|
208 |
FhighPass is a function to develop a highpass filter using 'firwin' |
|
|
209 |
and 'lfilter' functions from the "Scipy" library |
|
|
210 |
""" |
|
|
211 |
# The Nyquist rate of the signal. |
|
|
212 |
nyq_rate = Fs / 2.0 |
|
|
213 |
# The desired width of the transition from pass to stop, |
|
|
214 |
# relative to the Nyquist rate. We'll design the filter |
|
|
215 |
# with a 5 Hz transition width. |
|
|
216 |
width = 5.0/nyq_rate |
|
|
217 |
# The desired attenuation in the stop band, in dB. |
|
|
218 |
ripple_db = 60.0 |
|
|
219 |
# Compute the order and Kaiser parameter for the FIR filter. |
|
|
220 |
N, beta = kaiserord(ripple_db, width) |
|
|
221 |
# Use firwin with a Kaiser window to create a lowpass FIR filter. |
|
|
222 |
taps_2 = firwin(N, H_cutoff_hz/nyq_rate, pass_zero=False) |
|
|
223 |
# Use lfilter to filter x with the FIR filter. |
|
|
224 |
X_h= lfilter(taps_2, 1.0, X) |
|
|
225 |
|
|
|
226 |
return X_h[N-1:] |
|
|
227 |
|
|
|
228 |
def FlowPass(X, Fs, L_cutoff_hz): |
|
|
229 |
""" |
|
|
230 |
Ref: http://scipy-cookbook.readthedocs.io/items/FIRFilter.html |
|
|
231 |
http://lagrange.univ-lyon1.fr/docs/scipy/0.17.1/generated/scipy.signal.firwin.html |
|
|
232 |
FlowPass is a function to develop a lowpass filter using 'firwin' |
|
|
233 |
and 'lfilter' functions from the "Scipy" library |
|
|
234 |
""" |
|
|
235 |
# The Nyquist rate of the signal. |
|
|
236 |
nyq_rate = Fs / 2.0 |
|
|
237 |
# The desired width of the transition from pass to stop, |
|
|
238 |
# relative to the Nyquist rate. We'll design the filter |
|
|
239 |
# with a 5 Hz transition width. |
|
|
240 |
width = 5.0/nyq_rate |
|
|
241 |
# The desired attenuation in the stop band, in dB. |
|
|
242 |
ripple_db = 60.0 |
|
|
243 |
# Compute the order and Kaiser parameter for the FIR filter. |
|
|
244 |
N, beta = kaiserord(ripple_db, width) |
|
|
245 |
# Use firwin with a Kaiser window to create a lowpass FIR filter. |
|
|
246 |
taps = firwin(N, L_cutoff_hz/nyq_rate, window=('kaiser', beta)) |
|
|
247 |
# Use lfilter to filter x with the FIR filter. |
|
|
248 |
X_l= lfilter(taps, 1.0, X) |
|
|
249 |
|
|
|
250 |
return X_l[N-1:] |
|
|
251 |
# ----------------------------------------------------------------------------- |
|
|
252 |
# Peak Detection |
|
|
253 |
# ----------------------------------------------------------------------------- |
|
|
254 |
def PDP(Xf, samplerate): |
|
|
255 |
""" |
|
|
256 |
Peak Detection Process |
|
|
257 |
""" |
|
|
258 |
timeCut = samplerate*0.25 # time to count another pulse |
|
|
259 |
vCorte = 0.6 |
|
|
260 |
|
|
|
261 |
Xf = vec_nor(Xf) |
|
|
262 |
dX=derivate(Xf); # Derivate of the signal |
|
|
263 |
dX=vec_nor(dX); # Vector Normalizing |
|
|
264 |
|
|
|
265 |
size=np.shape(Xf) # Rank or dimension of the array |
|
|
266 |
fil=size[0]; # Number of rows |
|
|
267 |
|
|
|
268 |
positive=np.zeros((1,fil+1)); # Initializating Vector |
|
|
269 |
positive=positive[0]; # Getting the Vector |
|
|
270 |
|
|
|
271 |
points=np.zeros((1,fil)); # Initializating the Peak Points Vector |
|
|
272 |
points=points[0]; # Getting the point vector |
|
|
273 |
|
|
|
274 |
points1=np.zeros((1,fil)); # Initializating the Peak Points Vector |
|
|
275 |
points1=points1[0]; # Getting the point vector |
|
|
276 |
|
|
|
277 |
vCorte = 0.6 |
|
|
278 |
|
|
|
279 |
''' |
|
|
280 |
FIRST! having the positives values of the slope as 1 |
|
|
281 |
And the negative values of the slope as 0 |
|
|
282 |
''' |
|
|
283 |
for i in range(0,fil): |
|
|
284 |
if dX[i]>0: |
|
|
285 |
positive[i]=1; |
|
|
286 |
else: |
|
|
287 |
positive[i]=0; |
|
|
288 |
''' |
|
|
289 |
SECOND! a peak will be found when the ith value is equal to 1 && |
|
|
290 |
the ith+1 is equal to 0 |
|
|
291 |
''' |
|
|
292 |
for i in range(0,fil): |
|
|
293 |
if (positive[i]==1 and positive[i+1]==0): |
|
|
294 |
points[i]=Xf[i]; |
|
|
295 |
else: |
|
|
296 |
points[i]=0; |
|
|
297 |
''' |
|
|
298 |
THIRD! Define a minimun Peak Height |
|
|
299 |
''' |
|
|
300 |
p=0; |
|
|
301 |
for i in range(0,fil): |
|
|
302 |
if (points[i] > vCorte and p==0): |
|
|
303 |
p=i |
|
|
304 |
points1[i]=Xf[i] |
|
|
305 |
|
|
|
306 |
else: |
|
|
307 |
points1[i]=0; |
|
|
308 |
if (p+timeCut < i): |
|
|
309 |
p=0 |
|
|
310 |
|
|
|
311 |
return points1 |
|
|
312 |
# ----------------------------------------------------------------------------- |
|
|
313 |
# Fast Fourier Transform |
|
|
314 |
# ----------------------------------------------------------------------------- |
|
|
315 |
def fft_k(data, samplerate, showFrequency): |
|
|
316 |
''' |
|
|
317 |
Fast Fourier Transform |
|
|
318 |
Ref: https://docs.scipy.org/doc/scipy/reference/tutorial/fftpack.html |
|
|
319 |
''' |
|
|
320 |
pcgFFT = fft(data) # FFT Full Vector |
|
|
321 |
short_pcgFFT = 2.0/np.size(data) * np.abs(pcgFFT[0:np.size(data)//2]) # FFT positives values |
|
|
322 |
vTfft = np.linspace(0.0, 1.0/(2.0*(1/samplerate)), np.size(data)//2) # Vector of frequencies (X-axes) |
|
|
323 |
|
|
|
324 |
idx = (np.abs(vTfft-showFrequency)).argmin() # find the value closest to a value |
|
|
325 |
|
|
|
326 |
return short_pcgFFT[0:idx], vTfft[0:idx] |
|
|
327 |
|
|
|
328 |
def fft_k_N(data, samplerate, showFrequency): |
|
|
329 |
''' |
|
|
330 |
Normalized Fast Fourier Transform |
|
|
331 |
Ref: https://docs.scipy.org/doc/scipy/reference/tutorial/fftpack.html |
|
|
332 |
''' |
|
|
333 |
pcgFFT = fft(data) # FFT Full Vector |
|
|
334 |
short_pcgFFT = 2.0/np.size(data) * np.abs(pcgFFT[0:np.size(data)//2]) # FFT positives values |
|
|
335 |
vTfft = np.linspace(0.0, 1.0/(2.0*(1/samplerate)), np.size(data)//2) # Vector of frequencies (X-axes) |
|
|
336 |
|
|
|
337 |
idx = (np.abs(vTfft-showFrequency)).argmin() # find the value closest to a value |
|
|
338 |
|
|
|
339 |
return vec_nor(short_pcgFFT[0:idx]), vTfft[0:idx] |