My research interests include haemodynamics, blood pressure and cardiometabolic health.
I mostly write code for Stata and Matlab.
Reservoir analysis for BPplus files
License: GNU General Public License v3.0
My research interests include haemodynamics, blood pressure and cardiometabolic health.
I mostly write code for Stata and Matlab.
Version 1 (Beta 4)
Would you be ok if I extract the code that reads BP+ XML files into a function?
Separation of concerns reduces the effort to maintain code by improving reuse and testability. I have created MATLAB code for comparing BP+ results to invasive recordings for a new validation paper against pressure tip cath recordings. I think it is helpful sharing code that overlaps.
For ease of use, I think it require a "rename" of variables where _
is replaced with .
to group variables by the source waveforms.
ss_ai
becomes ss.ai
, etc. for other supra-systolic variables and waveformsba_sbp
becomes ba.sbp
, ba_p_all
becomes ba.p_all
, etc. for intra-arterial brachial variables and waveformsaosbp
becomes ao.sbp
, ao_p_all
becomes ao.p_all
, etc. for intra-arterial aortic variables and waveformssamplerate
becomes metadata.samplerate
, snr
becomes metadata.snr
etc for other measurement parameters that are not measured.i.e. we end up with a function along the lines of the following that works for both XML formats.
I wanted to ask for your thoughts on this before doing the work and proposing a PR for you repository.
function [data, metadata, ss, ba, ao]=read_BPplus(folder_name, filename)
if not (isfile([folder_name filename]))
return;
end
[data] = xml2struct([folder_name filename]);
% do we extract the measurement ID number?
metadata.id = filename;
%% Read Data from CardioScope legacy xml
if sum(strcmp(fieldnames(data), 'CardioScope')) == 1
% meta data
metadata.algo=data.CardioScope.Results.Result.Attributes.algorithm_revision; % Software algorithm
metadata.samplerate=str2double(data.CardioScope.MeasDataLogger.SampleRate.Text); % sample rate, Hz
metadata.datestring=data.CardioScope.MeasDataLogger.Attributes.datetime; % Date as text string
metadata.guid = data.CardioScope.MeasDataLogger.Attributes.guid;
metadata.snr=str2double(data.CardioScope.Results.Result.SNR.Text); % Signal to noise ratio, dB
metadata.patinet_id = '';
metadata.notes = '';
metadata.RawSuprasystolicPressure=data.CardioScope.MeasDataLogger.SSBuff.Text; % raw base 64 data
% start of pulses.
metadata.baTransitTime = 0.18;
if (contains(data.CardioScope.MeasDataLogger.Attributes.software_version,"038"))
metadata.baTransitTime = 0.06;
end
% extract CardioScope values
% nibp brachial measurement
ba.sbp=str2double(data.CardioScope.MeasDataLogger.Sys.Text); % brachial systolic BP, mmHg
ba.dbp=str2double(data.CardioScope.MeasDataLogger.Dia.Text); % diastolic BP, mmHg
ba.pp=ba.sbp-ba.dbp; % brachial pulse pressure, mmHg
ba.map=str2double(data.CardioScope.MeasDataLogger.Mean.Text); % mean arterial pressure, mmHg
ba.hr=str2double(data.CardioScope.MeasDataLogger.Hr.Text); % heart rate, bpm
% calculated aortic measurements
ao.sbp=str2double(data.CardioScope.Results.Result.aoSys.Text); % cSBP calculated by BP+, mmHg
ao.dbp=str2double(data.CardioScope.Results.Result.aoDia.Text); % cDBP calculated by BP+, mmHg
ao.map=str2double(data.CardioScope.Results.Result.aoMap.Text); % cMAP calculated by BP+, mmHg
ao.pp=ao.sbp-ao.dbp; % cPP calculated by BP+, mmHg
ao.ed = -1; % TODO Calcualte ED
ss.prv=str2double(data.CardioScope.Results.Result.RMSSD.Text); % RMSSD from suprasystolic signal
ss.ai=str2double(data.CardioScope.Results.Result.ssAI.Text); % AI from suprasystolic signal
ss.dpdt=str2double(data.CardioScope.Results.Result.ssDpDtMax.Text); % dp/dt from suprasystolic signal in uncorrected units
ss.harm=str2double(data.CardioScope.Results.Result.ssHARM.Text); % Normalized sAI
ss.pp=str2double(data.CardioScope.Results.Result.ssPP.Text); % Suprasystolic Pulse Pressure
ss.ppv=str2double(data.CardioScope.Results.Result.ssPPV.Text); % Suprasystolic Pulse Pressure Variation, %
ss.RWTTFoot=str2double(data.CardioScope.Results.Result.ssRWTTFoot.Text); % reflected wave transit time from foot of suprasystolic signal
ss.RWTTPeak=str2double(data.CardioScope.Results.Result.ssRWTTPeak.Text); % reflected wave transit time from peak of suprasystolic signal
ss.sep=str2double(data.CardioScope.Results.Result.ssSEP.Text); % Systolic ejection period, s
ss.Tn=split(data.CardioScope.Results.Result.ssAverageBeatPointsIdxs.Text,','); % Times of characteristic points?
%% brachial rhythm
ba.p_all=str2double(split(data.CardioScope.Results.Result.baEstimate.Text,','));
%% suprasystolic rhythm, average beat & start of pulses.
ss.p_all=[]; % not scaled to arterial mmHg
ss.pulseStartIndexes=str2double(split(data.CardioScope.Results.Result.ssBeatStartIdxs.Text,','));
ss.p_av = str2double(split(data.CardioScope.Results.Result.ssAverageBeat.Text,',')); % not scaled to arterial mmHg
ss.averagePulsePointsIndexes=str2double(split(data.CardioScope.Results.Result.ssAverageBeatPointsIdxs.Text,','));
%% aortic rhythm, average beat & start of pulses.
ao.p_all=str2double(split(data.CardioScope.Results.Result.aoEstimate.Text,','));
ao.pulseStartIndexes=str2double(split(data.CardioScope.Results.Result.cPulseStartIndexes.Text,','));
ao.p_av = str2double(split(data.CardioScope.Results.Result.aoAverageBeat.Text,','));
ao.averagePulsePointsIndexes=str2double(split(data.CardioScope.Results.Result.cAveragePulsePointsIndexes.Text,','));
%% aortic average beat, ao_p_av
a0=str2double(split(data.CardioScope.Results.Result.aoAverageBeat.Text,','));
% create a double beat' to deal with the errors in definition of dbp
a=[a0; a0];
plot(a);
% identify start and end of beat as minima
[~, locmin1]=min(a);
[~, locmin2]=min(a(locmin1+50:end));
locmin2=locmin2+locmin1+50;
%plot(a(locmin1:locmin2));
% filter derivative of new beat with SG to get rid of kinks at or around join
aa = sgolayfilt(diff(a),Npoly,Frame); % filter the derivative - order and framelen defined above
a1=cumsum(aa)-min(cumsum(aa))+min(a); % reconstruct
ao.p_av =a1(locmin1:locmin2-1)'; % crop to cycle
clear a0 a aa a1 locmin1 locmin2;
ao.offset = round(baTransitTime * metadata.samplerate);
ss.beatStartIdxs=str2double(split(data.CardioScope.Results.Result.ssBeatStartIdxs.Text,','));
ao.pulseStartIndexes = ss.beatStartIdxs - ao.offset;
for i = 1:length(ao.pulseStartIndexes)
if ao.pulseStartIndexes(i)<0
ao.pulseStartIndexes(i)=0;
end
end
else
%% Read Data from BPplus xml
metadata.algo=data.BPplus.Results.Result.Attributes.algorithm_revision; % Software algorithm
metadata.samplerate=str2double(data.BPplus.MeasDataLogger.SampleRate.Text); % sample rate, Hz
metadata.datestring=data.BPplus.MeasDataLogger.Attributes.datetime; % Date as text string
metadata.guid = data.BPplus.MeasDataLogger.Attributes.guid;
metadata.snr=str2double(data.BPplus.Results.Result.SNR.Text); % Signal to noise ratio, dB
metadata.RawSuprasystolicPressure=data.BPplus.MeasDataLogger.RawSuprasystolicPressure.Text; % raw base 64 data
% extract BPplus values
ba.sbp=str2double(data.BPplus.MeasDataLogger.Sys.Text); % brachial systolic BP, mmHg
ba.dbp=str2double(data.BPplus.MeasDataLogger.Dia.Text); % diastolic BP, mmHg
ba.pp=ba.sbp-ba.dbp; % brachial pulse pressure, mmHg
ba.map=str2double(data.BPplus.MeasDataLogger.Map.Text); % mean arterial pressure, mmHg
ba.hr=str2double(data.BPplus.MeasDataLogger.Pr.Text); % heart rate, bpm
ao.sbp=str2double(data.BPplus.Results.Result.cSys.Text); % cSBP calculated by BP+, mmHg
ao.dbp=str2double(data.BPplus.Results.Result.cDia.Text); % cDBP calculated by BP+, mmHg
ao.map=str2double(data.BPplus.Results.Result.cMap.Text); % cDBP calculated by BP+, mmHg
ao.pp=ao.sbp-ao.dbp; % cPP calculated by BP+, mmHg
ao.ed=str2double(data.BPplus.Results.Result.cST.Text)/1000; % cST calculated by BP+, ms, duration of systole
ss.prv=str2double(data.BPplus.Results.Result.sPRV.Text); % RMSSD from suprasystolic signal
ss.ai=str2double(data.BPplus.Results.Result.sAI.Text); % AI from suprasystolic signal
ss.dpdt=str2double(data.BPplus.Results.Result.sDpDtMax.Text); % dp/dt from suprasystolic signal in uncorrected units
%ssHARM= Can be calculated from sAI if needed
ss.pp=str2double(data.BPplus.Results.Result.sPP.Text); % Suprasystolic Pulse Pressure in the Cuff, mmHg (PP in cuff is small)
ss.ppv=str2double(data.BPplus.Results.Result.sPPV.Text); % Pulse pressure variation, % [?]
ss.RWTTFoot=str2double(data.BPplus.Results.Result.sRWTTFoot.Text); % reflected wave transit time from foot of suprasystolic signal
ss.RWTTPeak=str2double(data.BPplus.Results.Result.sRWTTPeak.Text); % reflected wave transit time from peak of suprasystolic signal
ss.sep=str2double(data.BPplus.Results.Result.sSEP.Text)/1000; % Systolic ejection period, s
ss.Tn=split(data.BPplus.Results.Result.sAveragePulsePointsIndexes.Text,','); % Times of characteristic points?
%% brachial rhythm
ba.p_all=str2double(split(data.BPplus.Results.Result.baEstimate.Text,','));
%% suprasystolic rhythm and average beat
ss.p_all=str2double(split(data.BPplus.Results.Result.sBaseLined.Text,',')); % not scaled to arterial mmHg
ss.pulseStartIndexes=str2double(split(data.BPplus.Results.Result.sPulseStartIndexes.Text,','));
ss.SelectedPulseIndexes=str2double(split(data.BPplus.Results.Result.sSelectedPulseIndexes.Text,','));
ss.p_av = split(data.BPplus.Results.Result.sAveragePulse.Text,','); % not scaled to arterial mmHg
ss.averagePulsePointsIndexes=str2double(split(data.BPplus.Results.Result.sAveragePulsePointsIndexes.Text,','));
%% aortic rhythm, average beat & start of pulses.
ao.p_all=str2double(split(data.BPplus.Results.Result.cEstimate.Text,','));
ao.pulseStartIndexes=str2double(split(data.BPplus.Results.Result.cPulseStartIndexes.Text,','));
ao.p_av=str2double(split(data.BPplus.Results.Result.cAveragePulse.Text,','))';
ao.averagePulsePointsIndexes=str2double(split(data.BPplus.Results.Result.cAveragePulsePointsIndexes.Text,','));
end
%% categorise quality based on SNR
if metadata.snr>=12
metadata.quality='Excellent';
elseif metadata.snr>=9
metadata.quality='Good';
elseif metadata.snr>=6
metadata.quality='Acceptable';
elseif metadata.snr>0
metadata.quality='Poor';
else
metadata.quality='Unacceptable';
end
end
Updated documentation to reflect changes in beta 5 - in progress
[ ] Extend scope of calculated variables so they match key Sphygmocor variables. [in progress]
[x ] Improve detection of end of systole
[x] Fix bug in Wf1 and Wf2 peak identification
[ ] better identification of problems with fits
[x] Improve error traps
[x] Fix bugs in figures and cut number of figures to one per dataset
[x] Improve setting default folder (rather than hard coding it) using a json as configuration file
[ ] Minor bug fixes and tidy code including converting script components to function [in progress]
[ ] SBP2 definition
[ ] SV estimation (possible beta 6 feature)
[ ] EF1 analog (possible beta 6 feature)
I have updated to 2024a version of MATLAB.
I now get the following warning
Warning: Colon operands must be real scalars. This warning will become an error in a future release.
> In kreservoir_v14 (line 152)
In BPfitres_v1 (line 38)
%find crossover point
Nd=length(pd);
np=[2:Nd Nd];
prend=pr(nn:end);
k=find((prend-prd).*(prend(np)-prd(np))<=0);
%% k = 3 15 40 82
%% nn = 55
prr=pr;
prr(nn+k:end)=prd(k+1:end); %% <- this is line 152.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.