/ concept-collection / seqlab
Sign in
concept-collection / seqlab
seqlab / src / engine / pulseq / +mr / makeBlockPulse.m
109 lines · 3.7 KBBlameHistoryRaw
1function [rf, delay] = makeBlockPulse(flip,varargin)
2%makeBlockPulse Create a block pulse with optional slice selectiveness.
3% rf=makeBlockPulse(flip, 'Duration', dur) Create block pulse
4% with given flip angle and duration.
5%
6% rf=makeBlockPulse(flip, 'Bandwidth', bw) Create block pulse
7% with given flip angle and bandwidth (Hz). The duration is calculated as
8% 1/(4*bw)
9%
10% rf=makeBlockPulse(..., 'freqOffset', f,'phaseOffset',p)
11% Create block pulse with frequency offset and phase offset.
13% rf=makeBlockPulse(..., 'ppmOffset')
14% Create block RF pulse with frequency offset specified in PPM (e.g.
15% actual frequency offset proportional to the true Larmor frequency); can
16% be combined with the 'freqOffset' specified in Hz.
18% [rf, delay]=makeBlockPulse(...) returns the corresponding delay object
19% that takes care of the RF ringdown time.
21% See also Sequence.addBlock
23validPulseUses = mr.getSupportedRfUse();
25persistent parser
26if isempty(parser)
27 parser = mr.aux.InputParserCompat;
28 parser.FunctionName = 'makeBlockPulse';
30 % RF params
31 addRequired(parser, 'flipAngle', @isnumeric);
32 addOptional(parser, 'system', [], @isstruct); % for slice grad
33 addParamValue(parser, 'duration', 0, @isnumeric);
34 addParamValue(parser, 'freqOffset', 0, @isnumeric);
35 addParamValue(parser, 'phaseOffset', 0, @isnumeric);
36 addParamValue(parser, 'freqPPM', 0, @isnumeric);
37 addParamValue(parser, 'phasePPM', 0, @isnumeric);
38 addParamValue(parser, 'timeBwProduct', 0, @isnumeric);
39 addParamValue(parser, 'bandwidth', 0, @isnumeric);
40 % Slice params
41 addParamValue(parser, 'maxGrad', 0, @isnumeric);
42 addParamValue(parser, 'maxSlew', 0, @isnumeric);
43 addParamValue(parser, 'sliceThickness', 0, @isnumeric);
44 % Delay
45 addParamValue(parser, 'delay', 0, @isnumeric);
46 % whether it is a refocusing pulse (for k-space calculation)
47 addParamValue(parser, 'use', 'u', @(x) any(validatestring(x,validPulseUses)));
48end
49parse(parser, flip, varargin{:});
50opt = parser.Results;
52if isempty(opt.system)
53 system=mr.opts();
54else
55 system=opt.system;
56end
58if opt.duration == 0
59 if opt.timeBwProduct > 0 && opt.bandwidth > 0
60 opt.duration = opt.timeBwProduct/opt.bandwidth;
61 elseif opt.bandwidth > 0
62 opt.duration = 1/(4*opt.bandwidth);
63 else
64 error('Either bandwidth or duration must be defined and must be larger than 0');
65 end
66end
68N = round(opt.duration/system.rfRasterTime);
69if N == 0
70 error('Duration is too short: it rounds to zero RF raster intervals');
71end
72opt.duration = N*system.rfRasterTime; % quantize the duration to the RF raster so that the achieved flip angle is exact
73t = [0; N]*system.rfRasterTime; % we start at 0 and end at N
74signal = opt.flipAngle/(2*pi)/opt.duration*ones(size(t));
76rf.type = 'rf';
77rf.signal = signal;
78rf.t = t;
79rf.shape_dur=t(end);
80rf.freqOffset = opt.freqOffset;
81rf.phaseOffset = opt.phaseOffset;
82rf.freqPPM = opt.freqPPM;
83rf.phasePPM = opt.phasePPM;
84rf.deadTime = system.rfDeadTime;
85rf.ringdownTime = system.rfRingdownTime;
86rf.delay = opt.delay;
87rf.center = rf.shape_dur/2;
88if ~isempty(opt.use)
89 rf.use=opt.use;
90end
91if rf.deadTime > rf.delay
92 rf.delay = rf.deadTime;
93end
95% v1.4 finally eliminates RF zerofilling
96% if rf.ringdownTime > 0
97% tFill = (1:round(rf.ringdownTime/1e-6))*1e-6; % Round to microsecond
98% rf.t = [rf.t rf.t(end)+tFill];
99% rf.signal = [rf.signal, zeros(size(tFill))];
100% end
101if nargout > 1
102 delay=mr.makeDelay(mr.calcDuration(rf)); % calcDuration already includes the ringdown time
103end
105% RF amplitude check
106rf_amplitude=max(abs(rf.signal));
107if rf_amplitude>system.maxB1
108 warning('WARNING: system maximum RF amplitude exceeded (%.01f%%)', rf_amplitude/system.maxB1*100);
109end
moveopenescclose