function varargout = serialcommgui(varargin)
% SERIALCOMMGUI M-file for serialcommgui.fig
%      SERIALCOMMGUI, by itself, creates a new SERIALCOMMGUI or raises the existing
%      singleton*.
%
%      H = SERIALCOMMGUI returns the handle to a new SERIALCOMMGUI or the handle to
%      the existing singleton*.
%
%      SERIALCOMMGUI('CALLBACK',hObject,eventData,handles,...) calls the local
%      function named CALLBACK in SERIALCOMMGUI.M with the given input arguments.
%
%      SERIALCOMMGUI('Property','Value',...) creates a new SERIALCOMMGUI or raises the
%      existing singleton*.  Starting from the left, property value pairs are
%      applied to the GUI before serialcommgui_OpeningFunction gets called.  An
%      unrecognized property name or invalid value makes property application
%      stop.  All inputs are passed to serialcommgui_OpeningFcn via varargin.
%
%      *See GUI Options on GUIDE's Tools menu.  Choose "GUI allows only one
%      instance to run (singleton)".
%
% See also: GUIDE, GUIDATA, GUIHANDLES

% Copyright 2002-2003 The MathWorks, Inc.

% Edit the above text to modify the response to help serialcommgui

% Last Modified by GUIDE v2.5 04-Dec-2004 13:14:01

% Begin initialization code - DO NOT EDIT
gui_Singleton = 1;
gui_State = struct('gui_Name',       mfilename, ...
                   'gui_Singleton',  gui_Singleton, ...
                   'gui_OpeningFcn', @serialcommgui_OpeningFcn, ...
                   'gui_OutputFcn',  @serialcommgui_OutputFcn, ...
                   'gui_LayoutFcn',  [] , ...
                   'gui_Callback',   []);
if nargin && ischar(varargin{1})
    gui_State.gui_Callback = str2func(varargin{1});
end

if nargout
    [varargout{1:nargout}] = gui_mainfcn(gui_State, varargin{:});
else
    gui_mainfcn(gui_State, varargin{:});
end
% End initialization code - DO NOT EDIT


% --- Executes just before serialcommgui is made visible.
function serialcommgui_OpeningFcn(hObject, eventdata, handles, varargin)
% This function has no output args, see OutputFcn.
% hObject    handle to figure
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)
% varargin   command line arguments to serialcommgui (see VARARGIN)

% Choose default command line output for serialcommgui
handles.output = hObject;
handles.num_points = 2500;
handles.max_time = 15;
handles.use_time_not_points = 1;
handles.cancel_data_acq = 0;
handles.shownondata = 0;

% Update handles structure
guidata(hObject, handles);

set(handles.timeradio, 'Value', 1);
set(handles.numpointsbox, 'Enable', 'Off');

% Set up the axis
axes(handles.axes1);
xlabel('time (seconds)');
ylabel('data');


global cancel_data_acq;
cancel_data_acq = 0;
clear cancel_data_acq;

% UIWAIT makes serialcommgui wait for user response (see UIRESUME)
% uiwait(handles.figure1);

% --- Outputs from this function are returned to the command line.
function varargout = serialcommgui_OutputFcn(hObject, eventdata, handles) 
% varargout  cell array for returning output args (see VARARGOUT);
% hObject    handle to figure
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

% Get default command line output from handles structure
varargout{1} = handles.output;


function numpointsbox_Callback(hObject, eventdata, handles)
% hObject    handle to numpointsbox (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

% Hints: get(hObject,'String') returns contents of numpointsbox as text
%        str2double(get(hObject,'String')) returns contents of numpointsbox as a double

val = str2num(get(hObject, 'String'));
if (length(val) == 1) && (val > 0) && (val <= 1e6)
    handles.num_points = str2num(get(hObject, 'String'));
    guidata(hObject, handles);
else
    set(hObject, 'String', num2str(handles.num_points));
end

% --- Executes during object creation, after setting all properties.
function numpointsbox_CreateFcn(hObject, eventdata, handles)
% hObject    handle to numpointsbox (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    empty - handles not created until after all CreateFcns called

% Hint: edit controls usually have a white background on Windows.
%       See ISPC and COMPUTER.
if ispc
    set(hObject,'BackgroundColor','white');
else
    set(hObject,'BackgroundColor',get(0,'defaultUicontrolBackgroundColor'));
end


% --- Executes on button press in getdatabutton.
function getdatabutton_Callback(hObject, eventdata, handles)
% hObject    handle to getdatabutton (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

set(handles.getdatabutton, 'Enable', 'off');    % Disable get data button.
set(handles.stopbutton, 'Enable', 'on');        % Enable stop button.
set(handles.timeradio, 'Enable', 'inactive');   % Disable time radio
set(handles.pointsradio, 'Enable', 'inactive'); % Disable time radio
if strcmp(get(handles.maxtimebox, 'Enable'), 'on')    % Disable time/points boxes
    set(handles.maxtimebox, 'Enable', 'inactive');
else
    set(handles.numpointsbox, 'Enable', 'inactive');
end
set(handles.popaxisbutton, 'Enable', 'off');    % Disable pop up axis button
set(handles.ndpointscheckbox, 'Enable', 'off'); % Disable non-data points checkbox


% Clear the axis
axes(handles.axes1);
cla;

global cancel_data_acq;

% Destroy any open serial port objects on COM1
s = instrfind('Port', 'COM1');
if length(s) ~= 0
    fclose(s);
    delete(s);
end

% A trial serial read to determine whether serial I/O is functioning.
% If not, 'fread(s)' will time out and data will have length 0.
s = serial('COM1', 'BaudRate', 57600, 'InputBufferSize', 1);
fopen(s);
data = fread(s);
fclose(s);
delete(s);
clear s;

if length(data) ~= 0    % If there was data in the buffer (reads are functioning)...

    % Create the serial port object. With a buffer size of 512, at a data rate
    % of 239 bps, it takes about 2 seconds to fill the buffer.
    s = serial('COM1', 'BaudRate', 57600, 'InputBufferSize', 512);
    fopen(s);

    data = [];

    badtime = []; % vector listing times where data was 255 (for debugging)
    
    set(handles.messagewindow, 'String', 'Getting data...');
    
    % Variable initialization.
    elaptime = 0;
    time = [];
    timelimit = handles.max_time;
    pointslimit = handles.num_points;
    percent_complete = 0;
    tic;
    while (handles.use_time_not_points && (toc < timelimit)) || ...
            (~handles.use_time_not_points && (length(data) < pointslimit)),

        % Pause to allow for interrupts, etc. We won't miss data doing
        % this, because the data buffer gets filled in the background.
        pause(0.5);
        
        if cancel_data_acq
            cancel_data_acq = 0;
            break;
        end
        
        new_data_index = length(data) + 1;
        
        data = [data fread(s)'];
        elaptime = toc;
        
        if handles.use_time_not_points
            if floor((elaptime / timelimit)*10) > percent_complete
                percent_complete = floor((elaptime / timelimit)*10);
                set(handles.messagewindow, 'String', ...
                    [num2str(min(100, percent_complete*10)) '% complete - ' ...
                    num2str(max(0,floor((timelimit - elaptime)*10))/10) ' seconds remaining...']);
            end
            
        else
            if floor((length(data) / pointslimit)*10) > percent_complete
                percent_complete = floor((length(data) / pointslimit)*10);
                set(handles.messagewindow, 'String', ...
                    [num2str(min(percent_complete*10)) '% complete - ' ...
                    num2str(max(0,pointslimit - length(data))) ' data points remaining...']);
            end
        end
        
        if length(time) == 0
            newtime = linspace(0, elaptime, 513);
        else
            newtime = linspace(time(length(time)), elaptime, 513);
        end
        
        time = [time newtime(2:length(newtime))];
        
        % Clear all values of 255 from data
        count = new_data_index;

        % Leading 255's:
        if new_data_index == 1
            while data(1) == 255,
                badtime = [badtime time(1)];
                data = data(2:length(data));
                time = time(2:length(time));
            end
            count = count + 1;
        end
        % Middle 255's:
        while count < length(time),
            if data(count) == 255
                badtime = [badtime time(count)];
                data = [data(1:count-1) data(count+1:length(data))];
                time = [time(1:count-1) time(count+1:length(time))];
            else
                count = count + 1;
            end
        end
        % Trailing 255
        if data(length(data)) == 255
            badtime = [badtime time(length(time))];
            data = data(1:length(data)-1);
            time = time(1:length(time)-1);
        end
        
        % Plot the data on the GUI's axes.
        plot(time, data);

        
    end
    
    
    % If we have received data (i.e., if data acquisition hasn't been immediately
    % cancelled)...
    if length(time) > 1
        % If the data isn't ALL invalid...
        if ~(all(data == 255))

            % Truncate the vectors to the appropriate time if time limit was used.
            if handles.use_time_not_points
                count = 1;
                while (time(count) <= timelimit) && (count < length(time)),
                    count = count + 1;
                end
                data = data(1:count);
                time = time(1:count);

            else
                % Truncate the vectors to the appropriate number of points if
                % points limit was used.
                data = data(1:min(pointslimit, length(data)));
                time = time(1:min(pointslimit, length(time)));
            end

            % Make the bad data well-formed
            count = 1;
            while count < length(badtime),
                if badtime(count) > time(length(time))
                    badtime = badtime(1:count - 1);
                    break;
                else
                    count = count + 1;
                end
            end

            baddata = [];
            btcount = 1;
            while badtime(btcount) <= time(1),
                baddata = [baddata data(1)];
                btcount = btcount + 1;
            end

            count = 1;
            while count <= length(time)
                if time(count) >= badtime(btcount)
                    baddata = [baddata data(count - 1)];
                    btcount = btcount + 1;
                    if btcount > length(badtime)
                        break
                    end
                else
                    count = count + 1;
                end
            end

            while btcount <= length(badtime)
                baddata(btcount) = data(length(data));
                btcount = btcount + 1;
            end

            clear btcount count;
            
            % Save time and data to the current workspace, so the user can mess
            % with them.
            assignin('base', 'baddata', baddata);
            assignin('base', 'badtime', badtime);
            assignin('base', 'mouse_data', data);
            assignin('base', 'mouse_time', time);

            set(handles.messagewindow, 'String', ...
                'Data vectors created in current workspace.');


            % Plot the data on the GUI's axes.
            plot(time, data); hold on;
            if handles.shownondata
                plot(badtime, baddata, 'r.');
            end
            
        else
            % If the data IS all invalid...
            set(handles.messagewindow, 'String', ...
                'No valid data received.');

        end
    end
    
    fclose(s);
    delete(s);
    clear s;

% If we didn't receive any data when clearing the input buffer and testing
% for I/O timout, then print an error.
else
    set(handles.messagewindow, 'String', 'Problem with serial I/O...');
end

set(handles.stopbutton, 'Enable', 'off');   % Disable stop button (reads are over).
pause(5);   % Wait for 5 seconds so the user can read the message.
set(handles.messagewindow, 'String', ...
    'Welcome to the Mouse-Implanted RFID Data Acquisition Tool!');
set(handles.getdatabutton, 'Enable', 'on');     % Re-enable get data button.
set(handles.timeradio, 'Enable', 'on');         % Re-enable time radio
set(handles.pointsradio, 'Enable', 'on');       % Re-enable time radio
if strcmp(get(handles.maxtimebox, 'Enable'), 'off')   % Re-enable time/points boxes
    set(handles.numpointsbox, 'Enable', 'on');
else
    set(handles.maxtimebox, 'Enable', 'on');
end
set(handles.popaxisbutton, 'Enable', 'on');     % Re-enable pop up axis button
set(handles.ndpointscheckbox, 'Enable', 'on'); % Re-enable non-data points checkbox



function maxtimebox_Callback(hObject, eventdata, handles)
% hObject    handle to maxtimebox (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

% Hints: get(hObject,'String') returns contents of maxtimebox as text
%        str2double(get(hObject,'String')) returns contents of maxtimebox as a double

val = str2num(get(hObject, 'String'));
if (length(val) == 1) & (val > 0) & (val <= 6000)
    handles.max_time = str2num(get(hObject, 'String'));
    guidata(hObject, handles);
else
    set(hObject, 'String', num2str(handles.max_time));
end

% --- Executes during object creation, after setting all properties.
function maxtimebox_CreateFcn(hObject, eventdata, handles)
% hObject    handle to maxtimebox (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    empty - handles not created until after all CreateFcns called

% Hint: edit controls usually have a white background on Windows.
%       See ISPC and COMPUTER.
if ispc
    set(hObject,'BackgroundColor','white');
else
    set(hObject,'BackgroundColor',get(0,'defaultUicontrolBackgroundColor'));
end


% --- Executes on button press in timeradio.
function timeradio_Callback(hObject, eventdata, handles)
% hObject    handle to timeradio (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

% Hint: get(hObject,'Value') returns toggle state of timeradio


% --- Executes on button press in pointsradio.
function pointsradio_Callback(hObject, eventdata, handles)
% hObject    handle to pointsradio (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

% Hint: get(hObject,'Value') returns toggle state of pointsradio


function messagewindow_Callback(hObject, eventdata, handles)
% hObject    handle to messagewindow (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

% Hints: get(hObject,'String') returns contents of messagewindow as text
%        str2double(get(hObject,'String')) returns contents of messagewindow as a double


% --- Executes during object creation, after setting all properties.
function messagewindow_CreateFcn(hObject, eventdata, handles)
% hObject    handle to messagewindow (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    empty - handles not created until after all CreateFcns called

% Hint: edit controls usually have a white background on Windows.
%       See ISPC and COMPUTER.
if ispc
    set(hObject,'BackgroundColor','white');
else
    set(hObject,'BackgroundColor',get(0,'defaultUicontrolBackgroundColor'));
end


function radiopanel_SelectionChangeFcn(hObject, eventdata, handles)
% hObject    handle to radiopanel (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

if strcmp(get(hObject, 'Tag'), 'pointsradio')
    set(handles.numpointsbox, 'Enable', 'On');
    set(handles.maxtimebox, 'Enable', 'Off');
    handles.use_time_not_points = 0;
    guidata(hObject, handles);
else
    set(handles.numpointsbox, 'Enable', 'Off');
    set(handles.maxtimebox, 'Enable', 'On');
    handles.use_time_not_points = 1;
    guidata(hObject, handles);
end


function radiopanel_CreateFcn(hObject, eventdata, handles)
% hObject    handle to radiopanel (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    empty - handles not created until after all CreateFcns called


% --- Executes on button press in stopbutton.
function stopbutton_Callback(hObject, eventdata, handles)
% hObject    handle to stopbutton (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)
global cancel_data_acq;
cancel_data_acq = 1;
set(handles.messagewindow, 'String', 'Cancelling...');


% --- Executes on button press in popaxisbutton.
function popaxisbutton_Callback(hObject, eventdata, handles)
% hObject    handle to popaxisbutton (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

% Pop up an external axis
figure;
time = evalin('base', 'mouse_time');
data = evalin('base', 'mouse_data');
badtime = evalin('base', 'badtime');
baddata = evalin('base', 'baddata');

plot(time, data);
xlabel('time (seconds)');
ylabel('data');
title('Mouse Data');
hold on;
if handles.shownondata
    plot(badtime, baddata, 'r.');
end



% --- Executes on button press in ndpointscheckbox.
function ndpointscheckbox_Callback(hObject, eventdata, handles)
% hObject    handle to ndpointscheckbox (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

% Hint: get(hObject,'Value') returns toggle state of ndpointscheckbox

handles.shownondata = get(hObject, 'Value');
% Update handles structure
guidata(hObject, handles);
