image thumbnail
from Contractometer by Geoff Boynton
Times and plots contractions during labor.

contractometer.m
%contractometer.m
%
%Measures durations and intervals between uterine contractions during
%labor.  
%
%When a contraction starts, click the mouse on the upper left green
%'Contraction Start' button. This will start the clock in the upper
%right, and a horizontal red line will rise over time indicating the
%duration of the current contraction on the left graph.
%
%When the contraction stops, click on the upper left red 'Contraction Stop'
%button (formerly the green 'Contraction Start' button).  The duration of 
%this contraction will be added to the plot on the left.  If this is not the 
%first contraction then the interval between the start of this and the previous
%contraction will be added to the plot on the right.  
%
%Data are stored in a text file, 'contractionTimes.txt' in the current directory.  
%The file is updated every time the user indicates a start or end of a 
%contraction.  This means that the user can quit the program (and quit MATLAB) 
%and resume to it without loss of data.  
%
%Data in 'contractionTimes.txt' are stored as date strings followed by a
%return character.  Successive lines indicate contraction start and stop
%times.  Contractions are separated by a blank line (return character).
%
%The only way the user can clear the data is to remove (or rename) the file
%'contractionTimes.txt'.  
%
%The user can edit contractionTimes.txt with a text editor (if, for example 
%a button is pressed inadvertently, or times need to be added manually after 
%driving to the hospital).  The program is not robust with respect to errors 
%in the text file, so be sure that the data are entered in the proper format 
%(e.g. no extra characters, or extra lines at the end of the file).
%
%Example:
%
%08-Jul-2007 10:58:37
%08-Jul-2007 10:58:47
%
%08-Jul-2007 11:09:06
%08-Jul-2007 11:09:16
%
%08-Jul-2007 11:38:24
%08-Jul-2007 11:38:34
%
%08-Jul-2007 11:50:43
%08-Jul-2007 11:50:53
%
%The 'Quit' button returns control flow back to the matlab prompt.  Upon
%quitting, the button turns in to a 'Resume' button which starts the program
%again.  Quitting and Resuming is a convenient way to incorporate manual
%changes in the data file 'contractionTimes.txt'.
%
%The standard literature says that it's time to go to the hospital when
%contraction are <= 5 minutes apart, and last longer than about 60 seconds, 
%hence the pink zones on the graphs.  But don't hold me responsible for this
%advice!  Be sure to talk to your physician about what to do with the
%information gathered by this program.
%
%Written by G.M. Boynton, July 2007 in anticipation of a baby nerd due
%August 1st.

%Define some stuff for plotting and saving data.
fileName = 'contractionTimes.txt';
newline = sprintf('\n');
markerSize =5;
lineWidth = 1;

%Load in or initialize parameters
if exist(fileName,'file');
    %Load in data from text file.  This section is sort of lame - 
    %not much error handling to deal with problems with the text file.  
    fp = fopen(fileName,'r');
    startStr = 'foo';
    i=0;
    startTimes = [];stopTimes = [];
    clear state
    while ischar(startStr)
        i = i+1;
        startStr = fgetl(fp);
        if ischar(startStr)
            startTimes(i) = datenum(startStr);
            state =1;  
        end
        stopStr = fgetl(fp);
        if ischar(stopStr)
            if ~isempty(stopStr)
                stopTimes(i) = datenum(stopStr);
                state = 0;
            end
        end
        foo = fgetl(fp);
    end
    fclose(fp);
else  %data file doesn't exist, so initialize parameters.
    startTimes = [];  %vector of starting clock times
    stopTimes = [];   %vector of stop clock times. 
    state = 0;        %0: between contractions, 1: during contraction
end

%The vectors startTimes and stopTimes contain times in the form of serial
%date numbers (see datenum and datestr).

%Set up the graphs and UI buttons
figure(1)
clf
set(gcf,'NumberTitle','off','Name','Contractometer');

%Right graph: time between contractions
hsubplot1 = subplot('Position',[.55,.1,.4,.65]);
patch([0,1000,1000,0],[0,0,5,5],[1,.75,.75],'EdgeColor','none');  %pink zone
hold on
for y=0:5:450
    plot([0,1000],[y,y],'-','Color',[.8,.8,.8]);
end
hplot1 = plot(0,0,'bo-','MarkerFaceColor','b','MarkerSize',markerSize,'LineWidth',lineWidth,'Visible','off');
hline1 = plot([0,1000],[0,0],'r-','LineWidth',lineWidth);
set(gca,'YLim',[0,45]);
set(gca,'XTick',[-inf,inf]);
ylabel('Minutes');
title('Time between contractions');

%Left graph: duration of contractions
hsubplot2 = subplot('Position',[.075,.1,.4,.65]);
patch([0,1000,1000,0],[60,60,1000,1000],[1,.75,.75],'EdgeColor','none');  %pink zone
hold on
for y=0:10:300
    plot([0,1000],[y,y],'-','Color',[.8,.8,.8]);
end
hplot2 = plot(0,0,'bo-','MarkerFaceColor','b','MarkerSize',markerSize,'LineWidth',lineWidth,'Visible','off');
hline2 = plot([0,1000],[0,0],'r-','LineWidth',lineWidth);
%plot([0,1000],60*[1,1],'r:','LineWidth',lineWidth);
ylabel('Seconds');
title('Duration of contractions');
set(gca,'YLim',[0,70]);
set(gca,'XTick',[-inf,inf]);

%Contraction Start/Stop push button
hbutton = uicontrol('Units','normalized');
set(hbutton,'Position',[0,.85,.5,.15],'Style','togglebutton');
set(hbutton,'BusyAction','cancel');
set(hbutton,'ForegroundColor','w');
set(hbutton,'FontSize',12,'FontWeight','bold');
set(hbutton,'Value',state);
if state == 0
    set(hbutton,'String','Contraction Start');
    set(hbutton,'BackgroundColor','g');
else
    set(hbutton,'String','Contraction Stop');
    set(hbutton,'BackgroundColor','r');
end

%Quit/Resume button
hquit = uicontrol('units','normalized');
set(hquit,'Style','togglebutton');
set(hquit,'Position',[.9,.8,.1,.05]);
set(hquit,'String','Quit');
set(hquit,'BackgroundColor','w');

%Timing text handle
htext = uicontrol('Units','normalized');
set(htext,'Position',[0.5,.85,.5,.15]);
set(htext,'FontSize',12,'FontWeight','bold');

%loop until 'Quit' button is pressed.
while ~get(hquit,'Value')
    t = datenum(clock);  %get the clock time

    if ~isempty(startTimes)
        dt = t-startTimes(end);  %time since start of previous contraction.
    else
        dt = NaN;
    end

    switch state  %set up the text for the timing text window.
        case 0  %We're in between contractions
            str = 'Time since last contraction';
            set(hline2,'Visible','off');
        case 1  %We're in the middle of a contraction
            str = 'Duration of current contraction';
            set(hline2,'Visible','on');
            set(hline2,'YData',24*60*60*dt*[1,1]);  %creeping red line on left graph
    end
    %convert dt to minutes and seconds
    min = floor(24*60*dt);
    sec = floor(mod(24*60*60*dt,60));
    if ~isnan(dt)
        if sec<10
            set(htext,'String',sprintf('%30s %d:0%d',str,min,sec));
        else
            set(htext,'String',sprintf('%30s %d:%d',str,min,sec));
        end
    end
    set(hline1,'YData',24*60*dt*[1,1]);  %creeping red line on right graph.

    dateLabel = datestr(startTimes,'HH:MM');

    if length(startTimes)>1  %plot the data on the right graph
        contractionDiff = 24*60*diff(startTimes);
        set(hplot1,'XData',1:length(contractionDiff));
        set(hplot1,'YData',contractionDiff);
        set(hplot1,'Visible','on');
        set(hsubplot1,'XLim',[.5,length(contractionDiff)+.5]);
        set(hsubplot1,'YLim',[0,max([max(contractionDiff)*1.05,30,1.05*24*60*dt])]);
        set(hsubplot1,'XTick',1:length(contractionDiff));
    end

    if length(stopTimes)>0  %plot the data on the left graph
        contractionDur = 24*60*60*(stopTimes-startTimes(1:length(stopTimes)));
        set(hplot2,'XData',1:length(contractionDur));
        set(hplot2,'YData',contractionDur);
        set(hplot2,'Visible','on');
        set(hsubplot2,'XLim',[.5,length(contractionDur)+.5]);
        if state == 0
            set(hsubplot2,'YLim',[0,max([max(contractionDur)*1.05,70])]);
        else
            set(hsubplot2,'YLim',[0,max([max(contractionDur)*1.05,70,1.05*24*60*60*dt])]);
        end

        set(hsubplot2,'XTick',1:length(contractionDur));
        set(hsubplot2,'XTickLabel',dateLabel(1:length(contractionDur),:),'FontSize',8);
    end

    newState = get(hbutton,'Value');
    if newState~=state %user has pushed the 'Contraction Start/Stop' button
        t = round(t*24*60*60)/(24*60*60);  %round time to nearest second.
        state = newState;
        switch state
            case 0  %Contraction is starting!
                stopTimes =  [stopTimes, t];
                set(hbutton,'String','Contraction Start');
                set(hbutton,'BackgroundColor','g');
            case 1  %Contraction has ended.
                startTimes = [startTimes,t];
                set(hbutton,'String','Contraction Stop');
                set(hbutton,'BackgroundColor','r');
        end
        %Save the data in 'contractionTimes.txt'
        fp = fopen(fileName,'w');
        for i=1:length(startTimes)
            fwrite(fp,[datestr(startTimes(i)),newline]);
            if length(stopTimes)>=i;
                fwrite(fp,[datestr(stopTimes(i)),newline]);
            end
            fwrite(fp,newline);
        end
        fclose(fp);
    end
    drawnow
end

%We're here, so the user must have selected the 'Quit' button.
%Prepare for 'Resume' mode and exit the program
set(hquit,'String','Resume');
set(htext,'ForegroundColor',[.5,.5,.5]);
set(hbutton,'Visible','off');
set(hquit,'CallBack','contractometer');  %Hitting 'Resume' button calls this program again.







Contact us