/ concept-collection / seqlab
Sign in
concept-collection / seqlab
seqlab / src / engine / pulseq / +mr / calcDuration.m
132 lines · 6.9 KBBlameHistoryRaw
1function duration=calcDuration(varargin)
2%calcDuration Calculate the duration of an event, set of events, or block.
3%
4% PURPOSE
5% Compute the time, in seconds, that one or more sequence events
6% occupy. Used pervasively when laying out blocks: TE/TR delay
7% calculations, gradient alignment, and consistency checks between
8% block timing and event timing all call this function.
9%
10% SIGNATURES
11% dur = mr.calcDuration(event) % single event struct
12% dur = mr.calcDuration(e1, e2, ...) % max over multiple events
13% dur = mr.calcDuration(block) % a block struct from seq.getBlock(n)
14% dur = mr.calcDuration({e1, e2, ...}) % cell array of events (auto-unwrapped)
15%
16% For multiple events the return value is the maximum of each event's
17% duration (events in a block run concurrently, not sequentially).
18% Unknown event types are silently ignored and contribute 0 (this behaviour may
19% change in future).
20%
21% INPUTS
22% varargin [required] One or more arguments, each one of:
23% - event struct with a .type field set to one of
24% 'rf', 'grad', 'trap', 'adc', 'delay', 'output', 'trigger'
25% - a cell array containing such structs or a single
26% numeric scalar
27% - a block struct (i.e. has a .rf field; typically
28% obtained from mr.Sequence/getBlock). Only a single
29% block struct may be passed.
30% - a numeric scalar interpreted as a blockDuration
31% field (seconds). Used internally when block2events
32% expands a block struct; rarely passed directly by
33% user code, but possibly as a member of cell array.
34%
35% OUTPUT
36% duration double, seconds. The maximum event duration encountered.
37% Returned as 0 if all arguments are unknown event types.
38%
39% ERRORS
40% - MATLAB:assertion:failed: a numeric blockDuration argument is
41% smaller than the maximum event duration encountered before it
42% in argument order. Indicates an inconsistent block whose declared
43% blockDuration is shorter than its longest event.
44% - 'Only a single block structure can be added' (from mr.block2events):
45% more than one argument was a block struct.
46% - 'Index exceeds array bounds' (from mr.block2events): called with
47% no arguments. Always pass at least one event.
48%
49% NOTES
50% - Per-event duration formulas (all in seconds):
51% rf : event.delay + event.shape_dur + event.ringdownTime
52% grad : event.delay + event.shape_dur (arbitrary gradient)
53% trap : event.delay + event.riseTime + event.flatTime + event.fallTime
54% adc : event.delay + event.numSamples*event.dwell + event.deadTime
55% delay : event.delay
56% output : event.delay + event.duration
57% trigger : event.delay + event.duration
58% - For 'rf' and 'grad' events the function relies on a precomputed
59% event.shape_dur field; it does not re-derive duration from the
60% waveform samples.
61% - For 'adc', the deadTime addend is the post-acquisition dead time
62% captured at construction (adc.deadTime), not the pre-acquisition
63% delay (which is already included via adc.delay).
64% - Note that 'delay' is not a true Pulseq object as it is not stored
65% in the event table; it is just used as a dummy to construct blocks
66% with a minimal duration
67% - Unknown .type values are silently skipped. A struct without a
68% .type field whose value matches no case will not raise. (This
69% behaviour may change in future)
70%
71% EXAMPLE
72% sys = mr.opts('MaxGrad', 30, 'GradUnit', 'mT/m', ...
73% 'MaxSlew', 170, 'SlewUnit', 'T/m/s');
74% Nx = 256; fov = 256e-3; deltak = 1/fov;
75% gx = mr.makeTrapezoid('x', sys, 'FlatArea', Nx*deltak, 'FlatTime', 6.4e-3);
76% gxPre = mr.makeTrapezoid('x', sys, 'Area', -gx.area/2, 'Duration', 1e-3);
77% adc = mr.makeAdc(Nx, sys, 'Duration', gx.flatTime, 'Delay', gx.riseTime);
78% % Longest event in this readout block
79% dur = mr.calcDuration(gxPre, gx, adc);
80% % TE delay rounded up to gradient raster
81% TE = 10e-3;
82% delayTE = ceil((TE - mr.calcDuration(gx)/2)/sys.gradRasterTime)*sys.gradRasterTime;
83%
84% SEE ALSO
85% mr.block2events, mr.makeDelay, mr.Sequence/getBlock,
86% mr.Sequence/addBlock
88duration=0;
90% Convert block structure to cell array of events
91varargin=mr.block2events(varargin);
93% Loop over events and calculate maximum duration
94for i=1:length(varargin)
95 events = varargin{i};
96 for j=1:length(events)
97 event=events(j);
98 if isnumeric(event) % this is the "blockDuration" field
99 assert(duration<=event);
100 duration=event;
101 continue;
102 end
103 switch event.type
104 case 'delay' % not a true object since v140 but still used to set a minimal block duration
105 duration=max(duration, event.delay);
106 case 'rf'
107 duration=max(duration, event.delay + event.shape_dur + event.ringdownTime); % rf has now a field called 'shape_dur' because it is impossible to calculate duration here in a general case...
108 case 'grad'
109% % duration=max(duration, event.t(end) + event.delay );
110% % MZ: we need to increase the last timestamp by one gradient
111% % raster time because otherwise the duration of the gradint
112% % containing one sample will be zero
113% % however, we do not have access to the gradient raster time
114% % here, so we opt of a hack, but it will actually fail for the
115% % gradint containg one sample...
116% % event.t(2) - event.t(1) gives us one gradient raster time
117% duration=max(duration, event.t(end) + event.t(2) - event.t(1) + event.delay );
118 duration=max(duration, event.delay+event.shape_dur); % shaped gradient has now a field called 'shape_dur' because it is impossible to calculate duration here in a general case...
119 case 'adc'
120 duration=max(duration, event.delay + ...
121 event.numSamples*event.dwell + event.deadTime);
122 case 'trap'
123 duration=max(duration, event.delay + event.riseTime + ...
124 event.flatTime + event.fallTime);
125 % duration=max(duration, event.riseTime+ event.flatTime +event.fallTime);
126 case {'output','trigger'}
127 duration=max(duration, event.delay + event.duration);
128 end
129 end
130end
132end
moveopenescclose