Code covered by the BSD License  

Highlights from
CO2gui - lab control and automation

CO2gui - lab control and automation

by

 

06 Jan 2010 (Updated )

Software used for controlling and data logging lab equipment.

fibreobjreadintensity(serialObject)
function [intensity, allData] = fibreobjreadintensity(serialObject)
% FIBREOBJREADINTENSITY returns information about the current FOR intensity
% [intensity, allData] = fibreobjreadintensity(serialObject) returns the intensity of
% the reflected light from the FOR if it is broadcasting.  If it is not
% broadcasting, it will error.  If a second output argument is supplied,
% then all the data used in analysing the raw data is also returned.

% checks the number of arguments
error(nargchk(1, 1, nargin))

% this RELIES on the data always being read out 100% successfully, or we'll
% get problems, since the only way we can tell the difference between
% channel 1 and channel 2 is that channel 1 is sent first!

% if there is no data in the buffer, wait a bit
if ~serialbytesavailable(serialObject)
    % wait
    pause(1)

    % if there STILL isn't any data left, just error
    if ~serialbytesavailable(serialObject)
        % error
        error('No data available to collect.')
    end
end

% depends on the channels!
allData.activeChannels = logical(serialObject.UserData.activeChannels);

% defines the sum for convenience
allData.totalChannels = sum(allData.activeChannels);

% error if there are no active channels - DEBUG not sure what happens if
% there are no active channels yet - presumably it just sends out nothing
if ~allData.totalChannels
    % error
    error('No active channels - cannot collect data.')
end

% defines the packet size - if we have two active channels then we MUST
% read out even numbers of 6 byte packets, otherwise we'll lose track of
% which packet is from which channel, however, we don't need this
% restriction for a single channel
allData.packetSize = 6 * allData.totalChannels;

% gets the number of available packets, e.g. if there are 3 channels, then
% 18-byte chunks
allData.packetsRead = fix(serialbytesavailable(serialObject) / allData.packetSize);

% calculates the number of bytes to read (labelled in the past tense here)
allData.bytesRead = allData.packetSize * allData.packetsRead;

% read out as many packets as possible, and turns it into a row vector for
% tidiness
rawData = serialread(serialObject, allData.bytesRead)';

% defines the time it was collected
allData.serialTime = now;

% reshape it into 6 byte rows for convenience
allData.data = reshape(rawData, 6, [])';

% define an OK vector for each line (this assumes its all good data)
allData.okFlag = true(size(allData.data, 1), 1);

% pre-allocates the processing
allData.rawIntensities = zeros(size(allData.data, 1), 1);

% for each row
for m = 1:size(allData.data, 1)
    % check that the 5th byte is zero, and that the checksum is correct
    if allData.data(m, 5) || allData.data(m, 6) ~= fibreobjchecksum(allData.data(m, 1:4))
        % it ain't right - mark it as bad data
        allData.okFlag(m) = false;
    end
    
    % try and process it - typecast does not work with arrays
    allData.rawIntensities(m) = double(typecast(uint8(allData.data(m, 1:4)), 'uint32'));
end

% defines the channels for reference
allData.labels = {  'Channel 1';...
                    'Channel 2';...
                    'Internal Diode';...
                    'Noise';...
                    'Temperature';...
                    'Reference'};

% need to reshape depending on which channels are active
if allData.totalChannels >= 2
    % reshape both into columns (for each channel) - note this reshapes
    % by ROW, and not by column the way reshape is normally used AND flip
    % the columns round, so it reads in order: channel 1, channel 2,
    % internal diode, noise, temperature, reference
    allData.okFlag = fliplr(reshape(allData.okFlag, allData.totalChannels, [])');
    allData.rawIntensities = fliplr(reshape(allData.rawIntensities, allData.totalChannels, [])');
end

% defines the scaling factors (this strictly only applies to the channel
% light intensities - not the rest of them)
allData.gradient = 1.82800636878895e-007;
allData.intercept = -47.9199538652254;

% defines the channel numbers for each column
channelIndices = find(allData.activeChannels);

% find the ones which are NOT temperature
intensityIndices = find(channelIndices ~= 5);

% convert the intensity data into the correct intensity (in dB) - the
% numbers were obtained by linear interpolation
%allData.intensities(intensityIndices, :) = allData.rawIntensities(intensityIndices, :) * allData.gradient + allData.intercept;
allData.intensities(:, intensityIndices) = allData.rawIntensities(:, intensityIndices) * allData.gradient + allData.intercept;

% pre-define the intensity as an empty cell array
intensity = cell(1, numel(allData.activeChannels));

% we want the last good pair of data - check this by beginning from the end
% and going backwards (there must be a cleaner vectorised way of doing
% this)
for o = size(allData.intensities, 1):-1:1
    % are all of the OK flags in that row OK? (if there is only 1 column,
    % then obviously there will only be one error flag to check)
    if all(allData.okFlag(o, :))
        % then we have good data - store it
        intensity(allData.activeChannels) = num2cell(allData.intensities(o, :));

        % leave the loop
        break
    end
end

% DEBUG
%assignin('base', 'allData', allData)

% if the intensity is still empty, then we didn't find any matching data
if isempty([intensity{:}])
    % if error checking is on
    if isfield(serialObject.UserData, 'errorChecking') && serialObjectUserData.errorChecking
        % give a warning that some of the data might be suspect
        warning('fibreObj:readIntensityError', 'No clean data was collected.')

    else
        % save the default of off
        serialObject.UserData.errorChecking = false;
    end
    
    % to fix this, one could check the most recent 6-byte chunk, then shift
    % backwards until you find one where the checksum matches BUT, this
    % won't help you if you have more than one channel active, but it may
    % help you if each channel is visually different
    
    % defines a found data flag
    foundData = false;

    % try this approach
    for p = 1:5
        % get the packet
        shiftPacket = rawData(end - p - 5:end - p);

        % check the data
        if ~shiftPacket(5) && shiftPacket(6) == fibreobjchecksum(shiftPacket(1:4))
            % we've found it!
            foundData = true;

            % read and discard the unnecessary bytes
            serialread(serialObject, 6 - p);

            % if there is only 1 channel
            if allData.totalChannels == 1
                % get the data out and convert it - it can still be rescued
                intensity(allData.activeChannels) = allData.gradient * typecast(uint8(shiftPacket(1:4)), 'uint32') + allData.intercept;
            end

            % leave the loop
            break
        end
    end
        
    % if its false...
    if ~foundData && serialObject.UserData.errorChecking
        % display
        disp('Could not fix data collection.')
    end
    
    % set the intensity as the last one anyway
    intensity(allData.activeChannels) = num2cell(allData.intensities(end, :));
    
    % DEBUG - saves all data to the main workspace so we can inspect it
    %assignin('base', 'allData', allData)
    %assignin('base', 'intensity', intensity)
    %assignin('base', 'f', serialObject)
end

Contact us