ECG-Kit 1.0
(11,761 bytes)
%% (Internal) Design the wavelet decomposition filters for wavedet algorithm
%
% Prototype:
% ----------
% q_filters = qs_filter_design(scales, fs, N);
%
% Description:
% ------------
% Mimics the transfer function of the filters used for ECG delineation in
% [Martinez et al. 2004] for an arbitrary sampling frequency and filter order N.
%
% Martinez et al. "A Wavelet-Based ECG Delineator: Evaluation on Standard
% Databases" IEEE TRANSACTIONS ON BIOMEDICAL ENGINEERING, VOL. 51, NO. 4,
% APRIL 2004.
%
% WARNING :
% ---------
% As this routines iterates through several configurations in order to
% converge, the user should check the transfer functions of the filters. My
% suggestion is once you obtain a desired filter bank for a given Fs or
% configuration, save or cache it in a .mat file in order to use it during
% operation. For example, if you usually work with signals sampled at 360
% Hz, a good choice is to have a cached version of the filters for this Fs
% in a .mat file called "wt_filters_6 scales_360 Hz.mat". You can use this
% function on-line with your algorithm at your own risk.
%
% Examples :
% ----------
%
% q_filters = qs_filter_design(4, 250);
%
% q_filters = qs_filter_design(5, 360);
%
% q_filters = qs_filter_design(6, 1000);
%
% you can check filter characteristics using:
%
% fss = [250 360 500 1000]; %Hz
% for fs = fss
% fvtool(q_filters, 'fs', fs )
% end
%
% Author: Mariano Llamedo Soria (llamedom at {electron.frba.utn.edu.ar; unizar.es}
% Version: 0.1 beta
% Birthdate: 17/2/11
% Last update: 22/02/13
% Copyright 2008-2015
%
function q_filters = qs_filter_design(scales, fs, N)
design_iter_attemps = 10;
if( nargin < 2 || isempty(fs) )
fs = 250; %Hz
end
if( nargin < 3 || isempty(N) )
%valor empírico obtenido de varios diseños.
N = max(10, round(fs*4/30 + 16+2/3));
end
%Pruebo que N no sea demasiado distinto a lo recomendable.
recommended_N = max(10, round(fs*4/30 + 16+2/3));
if( abs(N - recommended_N) > 0.1*N )
warning(['Check the transfer functions of the differentiator filters designed. Recommended order N = ' num2str(recommended_N) ]);
end
%frecuencia a la que está diseñado el delineador, y que se toma para
%referencia para que las escalas signifiquen lo mismo a cualquier Fs.
f_ref = 250; %Hz
f_ratio = f_ref/fs;
% Funciones de transferencia correctas para el diseño de los filtros utilizadas
% por el wavedet a 250 Hz.
empirical_tf = { ...
[2;-2] ...
[0.250000000000000;0.750000000000000;0.500000000000000;-0.500000000000000;-0.750000000000000;-0.250000000000000;] ...
[0.0312500000000000;0.0937500000000000;0.187500000000000;0.312500000000000;0.343750000000000;0.281250000000000;0.125000000000000;-0.125000000000000;-0.281250000000000;-0.343750000000000;-0.312500000000000;-0.187500000000000;-0.0937500000000000;-0.0312500000000000;] ...
[0.00390625000000000;0.0117187500000000;0.0234375000000000;0.0390625000000000;0.0585937500000000;0.0820312500000000;0.109375000000000;0.140625000000000;0.160156250000000;0.167968750000000;0.164062500000000;0.148437500000000;0.121093750000000;0.0820312500000000;0.0312500000000000;-0.0312500000000000;-0.0820312500000000;-0.121093750000000;-0.148437500000000;-0.164062500000000;-0.167968750000000;-0.160156250000000;-0.140625000000000;-0.109375000000000;-0.0820312500000000;-0.0585937500000000;-0.0390625000000000;-0.0234375000000000;-0.0117187500000000;-0.00390625000000000;] ...
[0.000488281250000000;0.00146484375000000;0.00292968750000000;0.00488281250000000;0.00732421875000000;0.0102539062500000;0.0136718750000000;0.0175781250000000;0.0219726562500000;0.0268554687500000;0.0322265625000000;0.0380859375000000;0.0444335937500000;0.0512695312500000;0.0585937500000000;0.0664062500000000;0.0727539062500000;0.0776367187500000;0.0810546875000000;0.0830078125000000;0.0834960937500000;0.0825195312500000;0.0800781250000000;0.0761718750000000;0.0708007812500000;0.0639648437500000;0.0556640625000000;0.0458984375000000;0.0346679687500000;0.0219726562500000;0.00781250000000000;-0.00781250000000000;-0.0219726562500000;-0.0346679687500000;-0.0458984375000000;-0.0556640625000000;-0.0639648437500000;-0.0708007812500000;-0.0761718750000000;-0.0800781250000000;-0.0825195312500000;-0.0834960937500000;-0.0830078125000000;-0.0810546875000000;-0.0776367187500000;-0.0727539062500000;-0.0664062500000000;-0.0585937500000000;-0.0512695312500000;-0.0444335937500000;-0.0380859375000000;-0.0322265625000000;-0.0268554687500000;-0.0219726562500000;-0.0175781250000000;-0.0136718750000000;-0.0102539062500000;-0.00732421875000000;-0.00488281250000000;-0.00292968750000000;-0.00146484375000000;-0.000488281250000000;] ...
[6.10351562500000e-05;0.000183105468750000;0.000366210937500000;0.000610351562500000;0.000915527343750000;0.00128173828125000;0.00170898437500000;0.00219726562500000;0.00274658203125000;0.00335693359375000;0.00402832031250000;0.00476074218750000;0.00555419921875000;0.00640869140625000;0.00732421875000000;0.00830078125000000;0.00933837890625000;0.0104370117187500;0.0115966796875000;0.0128173828125000;0.0140991210937500;0.0154418945312500;0.0168457031250000;0.0183105468750000;0.0198364257812500;0.0214233398437500;0.0230712890625000;0.0247802734375000;0.0265502929687500;0.0283813476562500;0.0302734375000000;0.0322265625000000;0.0339965820312500;0.0355834960937500;0.0369873046875000;0.0382080078125000;0.0392456054687500;0.0401000976562500;0.0407714843750000;0.0412597656250000;0.0415649414062500;0.0416870117187500;0.0416259765625000;0.0413818359375000;0.0409545898437500;0.0403442382812500;0.0395507812500000;0.0385742187500000;0.0374145507812500;0.0360717773437500;0.0345458984375000;0.0328369140625000;0.0309448242187500;0.0288696289062500;0.0266113281250000;0.0241699218750000;0.0215454101562500;0.0187377929687500;0.0157470703125000;0.0125732421875000;0.00921630859375000;0.00567626953125000;0.00195312500000000;-0.00195312500000000;-0.00567626953125000;-0.00921630859375000;-0.0125732421875000;-0.0157470703125000;-0.0187377929687500;-0.0215454101562500;-0.0241699218750000;-0.0266113281250000;-0.0288696289062500;-0.0309448242187500;-0.0328369140625000;-0.0345458984375000;-0.0360717773437500;-0.0374145507812500;-0.0385742187500000;-0.0395507812500000;-0.0403442382812500;-0.0409545898437500;-0.0413818359375000;-0.0416259765625000;-0.0416870117187500;-0.0415649414062500;-0.0412597656250000;-0.0407714843750000;-0.0401000976562500;-0.0392456054687500;-0.0382080078125000;-0.0369873046875000;-0.0355834960937500;-0.0339965820312500;-0.0322265625000000;-0.0302734375000000;-0.0283813476562500;-0.0265502929687500;-0.0247802734375000;-0.0230712890625000;-0.0214233398437500;-0.0198364257812500;-0.0183105468750000;-0.0168457031250000;-0.0154418945312500;-0.0140991210937500;-0.0128173828125000;-0.0115966796875000;-0.0104370117187500;-0.00933837890625000;-0.00830078125000000;-0.00732421875000000;-0.00640869140625000;-0.00555419921875000;-0.00476074218750000;-0.00402832031250000;-0.00335693359375000;-0.00274658203125000;-0.00219726562500000;-0.00170898437500000;-0.00128173828125000;-0.000915527343750000;-0.000610351562500000;-0.000366210937500000;-0.000183105468750000;-6.10351562500000e-05;] ...
};
Grid_size = 1024;
% Creo una grilla de muestreo en frecuencia logaritmica para que la
% zona en que derivan los filtros sea una recta de pendiente constante.
F_log = logspace(-3, min(0, log10(fs/f_ref)), Grid_size) * pi;
filter_count = 1;
for ii = rowvec(scales)
[g_amp F] = freqz(empirical_tf{ii},1, F_log);
F = F * f_ratio / pi;
g_amp = abs(g_amp);
%averiguo hasta qué muestra se comporta como un derivador, ya que será
%un parámetro de diseño.
slope_aux = diff( log10(g_amp) );
end_diff_idx = find(slope_aux < 0.95*slope_aux(1), 1, 'first');
%diseño un derivador hasta dicha frecuencia, con una banda de
%transición dada por la expresion , cuando sea posible.
ftrans = min(70, 251 * exp(-0.63*ii));
diff_order = round(N*1.8.^((ii*0.4 -0.6)));
%Los N tienen que ser necesariamente pares para el diseño del derivador.
if( rem(diff_order,2) ~= 0 )
diff_order = diff_order + 1;
end
[msgstr, msgid] = lastwarn;
%itero hasta que se diseña correctamente.
jj = 0;
effective_order = diff_order;
while( jj < design_iter_attemps )
d = fdesign.differentiator('n,fp,fst', effective_order, F(end_diff_idx), min( 0.95, F(end_diff_idx)+(ftrans*2/fs) ) );
try
Hd = design(d,'equiripple');
[~, msgid] = lastwarn('','');
catch MException
%on error, force iteration with different config.
msgid = 'signal:firpm:DidNotConverge';
end
if(strcmpi(msgid,'signal:firpm:DidNotConverge'))
%fallo en la convergencia, iteramos de nuevo.
%recorro linealmente por el rango +10:-50%
effective_order = round(diff_order * (jj*(0.5-1.1)/design_iter_attemps+1.1));
%Los effective_order tienen que ser necesariamente pares para el diseño del derivador.
if( rem(effective_order,2) ~= 0 )
effective_order = effective_order + 1;
end
jj = jj + 1;
else
jj = design_iter_attemps;
end
end
if(strcmpi(msgid,'signal:firpm:DidNotConverge'))
%fallo en la convergencia, reportamos el error.
error('qs_filter_design:Impossible2Design', 'Impossible to design the differentiator filter. Please try another N value, or check the filters transfer functions manually.')
end
%Vuelvo a ver la transferencia real del derivador diseñado y del filtro
%a emular para que tengan una respuesta similar en la zona en que se
%comporta como derivador. Averiguo el factor de escala entre ambas
%transferencias.
g_amp_emp = freqz(empirical_tf{ii},1, F_log);
g_amp_emp = abs(g_amp_emp);
g_amp_real = freqz(Hd, F * pi);
g_amp_real = abs(g_amp_real);
aux_idx = round(end_diff_idx/2);
aux_scale = g_amp_emp(aux_idx)/g_amp_real(aux_idx);
%Escalo la zona del derivador.
Hd.Numerator = Hd.Numerator * aux_scale;
g_amp_real = g_amp_real * aux_scale;
%ahora diseño un filtro de compensación para que la zona en que no se
%deriva sea similar al filtro de referencia. Esta irá desde el máximo
%de la transferencia pasobanda hasta donde haya una amplitud mayor a
%0.5 veces.
[~, max_idx] = max(g_amp);
aux_3db_unique = find( g_amp_real > 0.5 & g_amp_emp > 0.5, 1, 'last');
if( max_idx >= aux_3db_unique)
max_idx = end_diff_idx;
end
aux_idx = max_idx:aux_3db_unique;
%Creo una grilla de frecuencia - magnitud arbitraria para el diseño del
%filtro. Esta transferencia no deberá afectar la zona derivadora (H(w)
%= 1) y compensar la zona indicada por aux_idx, emulando la
%transferencia de referencia y teniendo un valor de atenuación
%considerable en Nyquist (H(w) = 1e-3)
F_comp = [0 max(0.9*F(end_diff_idx), (F(max_idx)-F(end_diff_idx))/2) F(aux_idx) 1];
g_amp_comp = [1 1 g_amp(aux_idx)./g_amp_real(aux_idx) 1e-3];
W = ones(1, length(F_comp));
W(3:end-1) = 10;
%Se diseña el filtro
d = fdesign.arbmag('N,F,A', diff_order, F_comp, g_amp_comp);
Hd_comp = design(d,'firls', 'weights', W, 'FilterStructure', 'dfsymfir');
% y se cascadea al original.
Hd = dfilt.cascade(Hd, Hd_comp);
q_filters(filter_count) = Hd;
filter_count = filter_count + 1;
end