forked from TASBE/TASBEFlowAnalytics
-
Notifications
You must be signed in to change notification settings - Fork 1
/
get_bead_peaks.m
123 lines (116 loc) · 5.79 KB
/
get_bead_peaks.m
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
% Copyright (C) 2010-2017, Raytheon BBN Technologies and contributors listed
% in the AUTHORS file in TASBE analytics package distribution's top directory.
%
% This file is part of the TASBE analytics package, and is distributed
% under the terms of the GNU General Public License, with a linking
% exception, as described in the file LICENSE in the TASBE analytics
% package distribution's top directory.
function [peaks units batch] = get_bead_peaks(model,channel,batch)
% Model must be a precise string match with the catalog, e.g., "SpheroTech RCP-30-5A"
% Channel can either be a Channel matched to laser/filter, or a name matched to the name in the catalog
% Batch is an optional parameter: if no batch is specified (or spec is empty), the first listed batch will be used
if nargin<3, batch = []; end; % if batch undefined, set to empty
catalog = getBeadCatalog();
% search for a matching model of bead (e.g., 'SpheroTech RCP-30-5A')
for i=1:numel(catalog)
modelEntry = catalog{i};
if strcmp(model,modelEntry{1})
% search for a matching bead batch, if specified (e.g., 'Lot AG01')
for j=2:numel(modelEntry)
batchEntry = modelEntry{j};
if isempty(batch) || strcmp(batch,batchEntry{1})
% search for a matching channel, e.g., laser = 488, filter = 530/30
% laser/filter must match precisely; beyond that there is no requirement
for k=2:numel(batchEntry)
channelEntry = batchEntry{k};
if ischar(channel) % if string, lookup by units; comparison will happen later
match = strcmp(channel,channelEntry{1});
end
if match
batch = batchEntry{1}; % report the actual used batch
units = channelEntry{2};
peaks = channelEntry{3};
return;
end
end
if isempty(batch), batch = 'unspecified'; end; % put in a scratch name for an omitted batch name
error('Unable to find bead catalog channel entry for model %s, batch %s, channel %s',model,batch,channel);
end
end
error('Unable to find bead catalog batch entry for model %s, batch %s',model,batch);
end
end
error('Unable to find bead catalog model entry for %s',model);
end
% The catalog has a 4-layer cell structure:
% { {'model', {'batch', {Channel, [peak, peak, ...]}, ... }, ...}, ...}
function returned = getBeadCatalog(forceReload)
persistent catalog;
if nargin<1, forceReload=false; end;
if isempty(catalog) || forceReload,
warning('off','MATLAB:xlsread:ActiveX')
[nums txts combo] = xlsread('BeadCatalog.xls');
catalog = parseCatalog(combo);
end
returned = catalog;
end
function catalog = parseCatalog(entries)
currentLine = 1;
catalog = {};
while currentLine<=size(entries,1)
if isnan(entries{currentLine,1}) % skip lines that start with blanks
currentLine = currentLine+1;
else % otherwise, try to parse as a bead model entry
[catalog{end+1} currentLine] = parseModel(entries,currentLine);
% ensure there are no duplicates
assert(isempty(find(cellfun(@(x)(strcmp(catalog{end}{1},x{1})),catalog(1:end-1)),1)),'Line %i: Multiple bead catalog entries for model %s',currentLine,catalog{end}{1});
end
end
end
function [model currentLine] = parseModel(entries,currentLine)
% from first line, take first cell to be name; ignore rest
name = entries{currentLine,1};
assert(ischar(name),'Line %i: Expected bead model name, but first cell in row is not a string',currentLine);
model = {name};
currentLine = currentLine+1; % advance to start reading batches
% now parse batches until we find one not starting with a string
while currentLine<=size(entries,1) && ischar(entries{currentLine,1})
[model{end+1} currentLine] = parseBatch(entries,currentLine);
% ensure there are no duplicates
assert(isempty(find(cellfun(@(x)(strcmp(model{end}{1},x{1})),model(2:end-1)),1)),'Line %i: Multiple bead catalog entries for batch %s in %s',currentLine,model{end}{1},name);
end
end
function [batch currentLine] = parseBatch(entries,currentLine)
% from first line, take first cell to be batch name; rest will be parsed amongst
name = entries{currentLine,1};
assert(ischar(name),'Line %i: Expected bead batch name, but first cell in row is not a string',currentLine);
batch = {name};
% read read of first line as channel, then advance
batch{2} = parseChannel(entries,currentLine);
currentLine = currentLine+1;
% now parse batches until we hit the end or find a non-nan line or a blank link
while currentLine<=size(entries,1) && ~(ischar(entries{currentLine,1}) || ~ischar(entries{currentLine,2}))
batch{end+1} = parseChannel(entries,currentLine);
currentLine = currentLine+1;
end
end
function channelEntry = parseChannel(entries,currentLine)
row = entries(currentLine,2:end);
name = row{1};
laser = row{2};
if ischar(row{3}), filter = row{3};
elseif isnan(row{3}), filter = 'Unspecified';
else error('Line %i: filter must be either a string or blank',currentLine);
end
units = row{4};
try
lastPeak = find(~cellfun(@isnan,row(5:end)),1,'last');
catch e
error('Line %i: couldn''t interpret peak specifications',currentLine);
end
peaks = [row{4+(1:lastPeak)}];
if isempty(peaks)
error('Line %i: bead peak specification must contain at least one peak.',currentLine);
end
channelEntry = {name, units, peaks};
end