ECG-Kit 1.0
(10,267 bytes)
%% Reads an ECG recording
% This function is the main I/O interface with ECG recordings. It can read
% several formats, such as 'MIT' 'AHA' 'ISHNE', 'HES', 'MAT' and 'Mortara'.
% Also has the feature of "auto-detect" file format.
%
% Example
%
% [ECG heasig ann recording_format] = read_ECG(recording_name, ECG_start_idx, ECG_end_idx, recording_format)
%
% Arguments: (specified as ECGwrapper('arg_name1', arg_val1, ... , 'arg_nameN', arg_valN) )
%
% + recording_name : (char) the full filename of the ECG
% recording.
%
% + ECG_start_idx : (number) the sample to start reading. Empty
% or 1 to start at the begining.
%
% + ECG_end_idx : (number) the sample to end reading. Empty to
% read all.
%
% + recording_format: (char) recording format of "recording_name"
% file. You can check the following links regarding file
% formats:
%
% - AHA: http://physionet.org/physiotools/old/dbpg/dbu_84.htm
% - ISHNE: http://thew-project.org/THEWFileFormat.html
% - MIT: http://physionet.org/physiotools/wag/signal-5.htm
%
% HES and mortara are propietary formats and no
% information is distributed with the kit. You may need
% SuperECG from mortara in order con extract the binary
% files required to read this kind of files. MAT format
% requieres a typical "*.mat" matlab file with the
% following variables inside:
%
% - signal: the signal matrix of size [ header.nsamp header.nsig ]
% - header: A struct with all signal properties. See the
% example recording included with the kit
% \recordings\example_recording.mat for an example of
% this struct format, and Physionet documentation for
% further details:
% http://physionet.org/physiotools/wag/header-5.htm
% - ann: Annotations struct included with the signal in MIT
% format as the one returned by "readannot" function.
% The "time" field must be present, being this a
% numeric vector with the sample indexes of the
% annotations. For the "type" and "subtype" fields, see
% the MIT format documentation in Physionet:
% http://www.physionet.org/physiobank/annotations.shtml
%
% See also ECGwrapper, ECGformat, read_AHA_format, read_ishne_format, read_HES_format, read_Mortara_format
%
% Author: Mariano Llamedo Soria
% <matlab:web('mailto:llamedom@electron.frba.utn.edu.ar','-browser') (email)>
% Version: 0.1 beta
% Birthdate: 5/01/2014
% Last update: 19/11/2014
% Copyright 2008-2015
%
function [ECG heasig ann recording_format end_sample] = read_ECG(recording_name, ECG_start_idx, ECG_end_idx, recording_format)
ECG = [];
heasig = [];
ann = [];
cKnownFormats = {'MIT' 'AHA' 'ISHNE', 'HES', 'MAT', 'Mortara'};
matformat_definitions();
end_sample = [];
if( nargin < 4 || isempty(recording_format) || ~any(strcmpi( recording_format, cKnownFormats)) )
%Try guessing the ECG format
recording_format = ECGformat(recording_name);
if( isempty(recording_format) )
error('read_ECG:UnknownDataFormat', 'Unknown data format.')
end
end
if( nargin < 2 || ~isnumeric(ECG_start_idx) )
ECG_start_idx = [];
end
if( nargin < 3 || ~isnumeric(ECG_end_idx))
ECG_end_idx = [];
end
if( strcmp(recording_format, 'ISHNE') )
if( nargout > 1 )
[ECG heasig ann end_sample] = read_ishne_format(recording_name, ECG_start_idx, ECG_end_idx );
else
ECG = read_ishne_format(recording_name, ECG_start_idx, ECG_end_idx );
end
elseif( strcmp(recording_format, 'MAT') )
aux_load = load(recording_name);
fnames = fieldnames(aux_load);
signal_name = intersect( fnames, cMatSignalNames);
header_name = intersect( fnames, cMatSignalHeaderNames);
ann_name = intersect( fnames, cMatSignalAnnNames);
if( isempty(header_name) )
error( 'read_ECG:HeaderNotFound', ['Can''t find annotations for file : ' rowvec(recording_name') ] );
else
heasig = aux_load.(header_name{1});
end
if( nargout > 1 )
if( ~isempty(ann_name) )
ann = aux_load.(ann_name{1});
end
end
if( ~isempty(signal_name) )
ECG = aux_load.(signal_name{1});
end
[nsamp_found, nsig_found] = size(ECG);
if( nsamp_found ~= heasig.nsamp && nsamp_found < nsig_found )
ECG = ECG';
end
if( all( [nsamp_found, nsig_found] ~= heasig.nsamp) || all( [nsamp_found, nsig_found] ~= heasig.nsig) )
cprintf('[1,0.5,0]','Header information is not consistent with the signal found. signal is [ %d x %d ] and header is [ %d x %d ]\n', nsamp_found, nsig_found, heasig.nsamp, heasig.nsig );
end
if( isempty(ECG_start_idx) )
ECG_start_idx = 1;
end
if( isempty(ECG_end_idx) )
ECG_end_idx = heasig.nsamp;
end
ECG = ECG(ECG_start_idx:ECG_end_idx,:);
clear aux_load
elseif( strcmp(recording_format, 'Mortara') )
[ECG heasig end_sample] = read_Mortara_format(recording_name, ECG_start_idx, ECG_end_idx );
elseif( strcmp(recording_format, 'AHA') )
if( nargout > 1 )
[ECG heasig ann end_sample] = read_AHA_format(recording_name, ECG_start_idx, ECG_end_idx );
else
ECG = read_AHA_format(recording_name, ECG_start_idx, ECG_end_idx );
end
elseif( strcmp(recording_format, 'HES') )
if( nargout > 1 )
[ECG heasig ann end_sample] = read_HES_format(recording_name, ECG_start_idx, ECG_end_idx );
else
ECG = read_HES_format(recording_name, ECG_start_idx, ECG_end_idx );
end
elseif( strcmp(recording_format, 'MIT') )
%% MIT various data formats
if( nargout > 2 )
strAnnExtension = {'ari' 'atr' 'ecg' 'cardmean'};
bAnnotationFound = false;
for ii = 1:length(strAnnExtension)
annFileName = [recording_name(1:end-3) strAnnExtension{ii}];
if( exist(annFileName, 'file') )
bAnnotationFound = true;
break
end
end
if(~bAnnotationFound)
error( 'read_ECG:AnnNotFound', ['Can''t find annotations for file : ' rowvec(recording_name') ] );
end
ann = readannot(annFileName);
end
[ recording_path rec_name ] = fileparts(recording_name);
recording_path = [recording_path filesep];
heasig = readheader([recording_name(1:end-3) 'hea']);
if( ~isfield(heasig, 'fname') )
heasig.fname = repmat(rec_name, heasig.nsig, 1);
end
recording_files = unique(cellstr(heasig.fname));
% Sometime I filtered only ECG signals right here.
% ECG_idx = get_ECG_idx_from_header(heasig);
ECG_idx = 1:heasig.nsig;
if( isempty( ECG_start_idx ) )
ECG_start_idx = 1;
else
ECG_start_idx = max(1,ECG_start_idx);
end
if( isempty( ECG_end_idx ) )
%Intento la lectura total por defecto
ECG_end_idx = heasig.nsamp;
else
ECG_end_idx = min(heasig.nsamp, ECG_end_idx);
end
if( ECG_end_idx == 0 )
ECG_end_idx = realmax;
end
samples2read = ECG_end_idx - ECG_start_idx + 1;
%No leer bloques mas grandes de 200 megabytes
MaxIOread = 200; %megabytes
if( (samples2read*heasig.nsig*2) > (MaxIOread * 1024^2) )
samples2read = (MaxIOread * 1024^2) / heasig.nsig / 2;
ECG_end_idx = samples2read + ECG_start_idx - 1;
warning(['Limited to read ' num2str(MaxIOread) ' Mb.'])
end
for ii = 1:length(recording_files)
sig_idx = find(strcmpi(recording_files(ii), cellstr(heasig.fname)));
% this should respect the stride of the data
nsig_present = length(sig_idx);
sig2_idx = intersect(sig_idx, ECG_idx);
nsig2read = length(sig2_idx);
fmt = heasig.fmt(sig2_idx(1));
ECG(:,sig2_idx) = read_MIT_ecg( [recording_path heasig.fname(sig2_idx(1),:)], ECG_start_idx, ECG_end_idx, nsig2read, nsig_present, fmt, heasig);
end
% ECG = int16(ECG);
end
heasig.ECG_format = recording_format;
function ECG = read_MIT_ecg(recording_name, ECG_start_idx, ECG_end_idx, nsig2read, nsig_present, fmt, heasig)
if( exist(recording_name, 'file') )
if( fmt == 16 || fmt == 61 || isnan(fmt) )
if( fmt == 16 || isnan(fmt) )
byte_ordering = 'ieee-le';
else
byte_ordering = 'ieee-be';
end
ECG_size = ECG_end_idx - (ECG_start_idx-1);
fidECG = fopen(recording_name, 'r');
try
fseek(fidECG, ((ECG_start_idx-1)*nsig_present)*2, 'bof');
ECG = fread(fidECG, [nsig2read ECG_size ], '*int16', (nsig_present-nsig2read)*2 , byte_ordering)';
fclose(fidECG);
catch ME
fclose(fidECG);
rethrow(ME);
end
elseif(fmt == 212)
ECG = rdsign212(recording_name, heasig.nsig, ECG_start_idx, ECG_end_idx);
elseif(fmt == 310)
ECG = read_310_format(recording_name, ECG_start_idx, ECG_end_idx, heasig );
elseif(fmt == 311)
warning('read_ECG:UntestedRegion', 'Untested!! danger ...')
ECG = read_311_format(recording_name, ECG_start_idx, ECG_end_idx, heasig );
else
error('read_ECG:UnknownDataFormat', 'Unknown data format.')
end
else
error('read_ECG:FileNotFound', disp_string_framed(0, sprintf( 'Could not find %s', recording_name ) ) );
end