1+ classdef triggeredBuffer < bossapi .inst .streamingAsyncBuffer
2+ % TRIGGEREDBUFFER Creates a buffer that stores signal data according to a trigger condition
3+
4+ properties (SetAccess = immutable )
5+ preTrigger_ms
6+ postTrigger_ms
7+
8+ TriggerSignal string
9+ TriggerCondition function_handle
10+ end
11+
12+ properties (GetAccess = private , SetAccess = immutable , Hidden )
13+ RemainingPostTriggerSamplesInitial int32
14+ end
15+
16+ properties (SetAccess = private )
17+ isTriggered logical
18+ isArmed logical
19+
20+ TriggerTime {mustBePositive } % Time of occurrence of trigger event
21+ end
22+
23+ properties (Dependent )
24+ isComplete logical
25+ end
26+
27+ properties (Access = private , Hidden )
28+ RemainingPostTriggerSamplesCurrent int32
29+ Target slrealtime.Target
30+ Inst slrealtime.Instrument
31+ end
32+
33+ events
34+ BufferComplete
35+ end
36+
37+ methods
38+ function obj = triggeredBuffer(bossObj , signalName , triggerSignal , triggerCondition , preTrigger_ms , postTrigger_ms , options )
39+ arguments
40+ bossObj {mustBeA(bossObj ," bossdevice" )}
41+ signalName {mustBeTextScalar }
42+ triggerSignal {mustBeTextScalar }
43+ triggerCondition function_handle
44+ preTrigger_ms {mustBeInteger ,mustBeNonnegative }
45+ postTrigger_ms {mustBeInteger ,mustBeNonnegative }
46+ options.ArrayIndex {mustBeVector(options .ArrayIndex," allow-all-empties" ),mustBeInteger } = [];
47+ options.SignalProps {mustBeText } = {};
48+ end
49+
50+ options.AppName = bossObj .appName;
51+ bufOptions = struct2pairs(options );
52+ obj@bossapi.inst.streamingAsyncBuffer(signalName , ' ' , (preTrigger_ms + postTrigger_ms )/1000 , true , bufOptions{: });
53+
54+ obj.preTrigger_ms = preTrigger_ms ;
55+ obj.postTrigger_ms = postTrigger_ms ;
56+ obj.RemainingPostTriggerSamplesInitial = 1 + round(postTrigger_ms / obj .getSamplePeriod(bossObj .appName));
57+ % Check if trigger signal exists by calling its info. Errors out if signal is not found
58+ bossapi .inst.getInfoSignalFromMldatx(options .AppName,triggerSignal );
59+ obj.TriggerSignal = triggerSignal ;
60+ obj.TriggerCondition = triggerCondition ;
61+
62+ obj.Target = bossObj .targetObject;
63+
64+ % Reset current object once is set up
65+ obj .reset;
66+
67+ function C = struct2pairs(S )
68+ % Turns a scalar struct S into a cell of string-value pairs C
69+ if iscell(S )
70+ C= S ; return
71+ elseif length(S )>1
72+ error(' Input must be a scalar struct or cell' );
73+ end
74+
75+ C= [fieldnames(S ).' ; struct2cell(S ).' ];
76+ C= C(: ).' ;
77+ end
78+ end
79+
80+ function isComplete = get .isComplete(obj )
81+ isComplete = obj .RemainingPostTriggerSamplesCurrent <= 0 ;
82+ end
83+
84+ function addToInstrument(obj , instObj )
85+ addToInstrument@bossapi.inst.signalCallbackManager(obj , instObj );
86+ end
87+
88+ function arm(obj )
89+ assert(~obj .isArmed,' Buffer is already armed.' );
90+ obj.Inst = slrealtime .Instrument;
91+ obj .Inst.addSignal(obj .TriggerSignal);
92+ obj .addToInstrument(obj .Inst);
93+ obj .Inst.connectCallback(@obj .write);
94+
95+ obj .Target.addInstrument(obj .Inst);
96+ obj.isArmed = true ;
97+ end
98+
99+ function disarm(obj )
100+ obj .Target.removeInstrument(obj .Inst);
101+ obj.isArmed = false ;
102+ end
103+
104+ function reset(obj )
105+ obj.isArmed = false ;
106+ obj.isTriggered = false ;
107+ obj.RemainingPostTriggerSamplesCurrent = obj .RemainingPostTriggerSamplesInitial;
108+ reset@bossapi.inst.streamingAsyncBuffer(obj );
109+ end
110+
111+ function delete(obj )
112+ obj .disarm;
113+ end
114+
115+ function out = read(obj )
116+ if obj .isComplete
117+ out = read@bossapi.inst.streamingAsyncBuffer(obj ,' extractAsTimetable' ,true );
118+ else
119+ error(' Buffer is not complete yet. Please wait.' );
120+ end
121+ end
122+
123+ function write(obj , instObj , event )
124+ arguments
125+ obj
126+ instObj slrealtime.Instrument
127+ event slrealtime.internal.instrument.AcquireGroupDataEvent
128+ end
129+
130+ if obj .Enable && ~obj .isComplete
131+ % Get data for trigger signal
132+ [triggerTime ,triggerData ] = getCallbackDataForSignal(instObj , event , obj .TriggerSignal);
133+ % Get data for buffer signal
134+ [signalTime ,signalData ] = obj .getCallbackDataForSignal(instObj , event );
135+ if ~ismatrix(signalData )
136+ signalData = squeeze(signalData )' ;
137+ end
138+
139+ % Evaluate trigger condition
140+ cond = feval(obj .TriggerCondition, triggerData );
141+ triggerIdx = find(cond == true ,1 ," first" );
142+
143+ if ~isempty(triggerIdx ) && ~obj .isTriggered
144+ % Trigger found in this data chunk
145+ obj.TriggerTime = triggerTime(triggerIdx );
146+ obj.isTriggered = true ;
147+
148+ % Add all buffers pretrigger to buffer
149+ obj .writeToBuffer(signalTime(1 : triggerIdx - 1 ), signalData(1 : triggerIdx - 1 ,: ));
150+
151+ % Remove rows already added to buffer
152+ signalTime(1 : triggerIdx - 1 ) = [];
153+ signalData(1 : triggerIdx - 1 ,: ) = [];
154+ end
155+
156+ % Fill buffer with new samples depending on trigger
157+ numRows = numel(signalTime );
158+ if obj .isTriggered
159+ rowsToAdd = min(obj .RemainingPostTriggerSamplesCurrent, numRows );
160+ obj .writeToBuffer(signalTime(1 : rowsToAdd ), signalData(1 : rowsToAdd ,: ));
161+
162+ % Update remaining post trigger samples
163+ obj.RemainingPostTriggerSamplesCurrent = obj .RemainingPostTriggerSamplesCurrent - rowsToAdd ;
164+ else
165+ % Fill buffer with all new samples
166+ obj .writeToBuffer(signalTime , signalData );
167+ end
168+
169+ % Check if buffer is complete in this call
170+ if obj .isComplete
171+ notify(obj ,' BufferComplete' );
172+ end
173+ end
174+ end
175+
176+ function p = plot(obj , parentFig )
177+ arguments
178+ obj
179+ parentFig = uifigure
180+ end
181+
182+ % WIP
183+ data = obj .read;
184+ p = plot(parentFig , data .Time, data .Variables);
185+ end
186+ end
187+
188+ methods (Access = private )
189+ function writeToBuffer(obj , time , data )
190+ if ~isempty(data )
191+ if ~isempty(obj .ArrayIndex)
192+ obj .Buffer.write([time , data(: , obj .ArrayIndex)]);
193+ else
194+ obj .Buffer.write([time , data ]);
195+ end
196+ end
197+ end
198+ end
199+
200+ methods (Hidden )
201+ function peek(varargin )
202+ peek@bossapi.inst.streamingAsyncBuffer(varargin );
203+ end
204+ end
205+
206+ end
0 commit comments