@@ -23,17 +23,25 @@ def respirationprefilter(
2323 # return operations.filter_physio(rawresp, cutoffs=[lowerpass, upperpass], method='bandpass')
2424
2525
26- def respenvelopefilter (squarevals , Fs , upperpass = 0.1 , order = 8 , debug = False ):
26+ def respenvelopefilter (squarevals , Fs , upperpass = 0.05 , order = 8 , debug = False ):
2727 if debug :
2828 print (f"respenvelopefilter: Fs={ Fs } order={ order } , upperpass={ upperpass } " )
2929 return butterlpfiltfilt (Fs , upperpass , squarevals , order , debug = debug )
3030
3131
3232@due .dcite (references .ROMANO_2023 )
33- def respiratorysqi (rawresp , Fs , debug = False ):
33+ def respiratorysqi (
34+ rawresp , Fs , envelopelpffreq = 0.05 , slidingfilterpctwidth = 10.0 , debug = False
35+ ):
3436 """Implementation of Romano's method from A Signal Quality Index for Improving the Estimation of
3537 Breath-by-Breath Respiratory Rate During Sport and Exercise,
36- IEEE SENSORS JOURNAL, VOL. 23, NO. 24, 15 DECEMBER 2023"""
38+ IEEE SENSORS JOURNAL, VOL. 23, NO. 24, 15 DECEMBER 2023
39+
40+ NB: In part A, Romano does not specify the upper pass frequency for the respiratory envelope filter.
41+ 0.05Hz looks pretty good.
42+ In part B, the width of the sliding window bandpass filter is not specified. We use a range of +/- 5% of the
43+ center frequency.
44+ """
3745
3846 # rawresp = physio_or_numpy(rawresp)
3947
@@ -51,6 +59,8 @@ def respiratorysqi(rawresp, Fs, debug=False):
5159 if debug :
5260 plt .plot (rawresp )
5361 plt .plot (prefiltered )
62+ plt .title ("Raw and prefiltered respiratory signal" )
63+ plt .legend (["Raw" , "Prefiltered" ])
5464 plt .show ()
5565 if debug :
5666 print ("prefiltered: " , prefiltered )
@@ -67,25 +77,35 @@ def respiratorysqi(rawresp, Fs, debug=False):
6777 normderiv = 2.0 * (derivative - derivmin ) / derivrange - 1.0
6878 if debug :
6979 plt .plot (normderiv )
80+ plt .title ("Normalized derivative of respiratory signal" )
81+ plt .legend (["Normalized derivative" ])
7082 plt .show ()
7183
7284 # amplitude correct by flattening the envelope function
7385 esuperior = 2.0 * respenvelopefilter (
74- np .square (np .where (normderiv > 0.0 , normderiv , 0.0 )), Fs
86+ np .square (np .where (normderiv > 0.0 , normderiv , 0.0 )),
87+ Fs ,
88+ upperpass = envelopelpffreq ,
7589 )
7690 esuperior = np .sqrt (np .where (esuperior > 0.0 , esuperior , 0.0 ))
7791 einferior = 2.0 * respenvelopefilter (
78- np .square (np .where (normderiv < 0.0 , normderiv , 0.0 )), Fs
92+ np .square (np .where (normderiv < 0.0 , normderiv , 0.0 )),
93+ Fs ,
94+ upperpass = envelopelpffreq ,
7995 )
80- einferior = np .sqrt (np .where (einferior > 0.0 , einferior , 0.0 ))
96+ einferior = - np .sqrt (np .where (einferior > 0.0 , einferior , 0.0 ))
8197 if debug :
8298 plt .plot (normderiv )
8399 plt .plot (esuperior )
84- plt .plot (- einferior )
100+ plt .plot (einferior )
101+ plt .legend (["Normalized derivative" , "esuperior" , "einferior" ])
85102 plt .show ()
86- rmsnormderiv = normderiv / (esuperior + einferior )
103+ rmsnormderiv = ( normderiv - einferior ) / (esuperior - einferior )
87104 if debug :
88105 plt .plot (rmsnormderiv )
106+ plt .title (
107+ "Normalized derivative of respiratory signal after envelope correction"
108+ )
89109 plt .show ()
90110
91111 # B. Detection of breaths in sliding window
@@ -112,10 +132,9 @@ def respiratorysqi(rawresp, Fs, debug=False):
112132 segment -= np .mean (segment )
113133 thex , they = welch (segment , Fs , nfft = 4096 )
114134 peakfreqs [i ] = thex [np .argmax (they )]
115- respfilterpctwidth = 10.0
116135 respfilterorder = 1
117- lowerfac = 1.0 - respfilterpctwidth / 200.0
118- upperfac = 1.0 + respfilterpctwidth / 200.0
136+ lowerfac = 1.0 - slidingfilterpctwidth / 200.0
137+ upperfac = 1.0 + slidingfilterpctwidth / 200.0
119138 lowerpass = peakfreqs [i ] * lowerfac
120139 upperpass = peakfreqs [i ] * upperfac
121140 if debug :
@@ -138,6 +157,8 @@ def respiratorysqi(rawresp, Fs, debug=False):
138157 print (peakfreqs )
139158 plt .plot (rmsnormderiv )
140159 plt .plot (respfilteredderivs )
160+ plt .title ("Normalized derivative before and after targeted bandpass filtering" )
161+ plt .legend (["Normalized derivative" , "Filtered normalized derivative" ])
141162 plt .show ()
142163
143164 # C. Breaths segmentation
0 commit comments