-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathGRE.m
288 lines (181 loc) · 11.3 KB
/
GRE.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
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
% GRE object of the hybrid GRE TSE sequence. Refer to the Main script for class construction and usage
% Author Junzhou Chen @ UCLA
classdef GRE
properties
dG=170e-6; %Gradient rise time
tEx=0.4e-3; %Exitation RF time
fspR=0.5; %Readout spoiler (crusher) area factor
flipex=[5,1] * pi/180; %Exitation RF flip angle
%%%%%% The following properties are calculated in the class constructor %%%%%%%
%Structure that defines the following properties for scan
%parameters:
% fov : [xfov, yfov, zfov] in m
% Nx : Number of samples in the readout direction
% Ny : Number of samples in the phase encoding direction
% Nz : Number of samples in the partition direction
% nechos : GRE echo train length of each segments e.g. [150,150]
% samplingTime : readout sampling time in s
% echoSpacing : Echo spacing time (or TE) in s
scanParams = [];
readoutTime = []; %Total readout gradient time: samplingTime + 2*adc_deadtime in s
tExwd = []; % Excitation RF time with dead time in s
RFex1 = []; %First Set of RF object (default 5 degree)
RFex2 = []; %Second Set of RF object (default 1 degree)
tSp = []; % Spoiler (crusher gradient) time in s
tPreph = []; % Prephasing gradient time in s
GR3 = []; %Prephasing readout gradient
GR6 = []; %Readout gradient
GR7 = []; %Readout gradient spoiler
ADC = []; %ADC object (struct)
PE_area_steps = []; % Array of phase encoding (Ky) gradient areas m^-1
Par_area_steps = []; % Array of partition encoding (Kz) gradient areas m^-1
end
methods
%GRE object constructor
function obj = GRE(system,scanParams)
if nargin == 1
%In case scanParams are not specified, we use defaults
newScanParams.fov = [256e-3, 256e-3, 256e-3]; % [xfov, yfov, zfov]
newScanParams.Nx = 128;
newScanParams.Ny = 128;
newScanParams.Nz = 128;
newScanParams.nechos = [150,150];
newScanParams.samplingTime = 4e-3;
newScanParams.echoSpacing = 6e-3;
obj.scanParams = newScanParams;
elseif nargin == 2
obj.scanParams = scanParams;
else
error('GRE Constructor: Number of parameters is not correct. Usage: GRE(system,scanParams)')
end
%========== Calculate TSE object properties ========
%Enforce GO raster
disp('Enforcing RO raster, sampling Time might be slightly changed')
obj.scanParams.samplingTime = ceil(obj.scanParams.samplingTime/obj.scanParams.Nx/system.adcRasterTime)...
* system.adcRasterTime * obj.scanParams.Nx;% ADC raster
% Gradient raster
obj.scanParams.samplingTime = ceil(obj.scanParams.samplingTime./system.gradRasterTime) * system.gradRasterTime;
obj.readoutTime = obj.scanParams.samplingTime + 2*system.adcDeadTime;
obj.tExwd = obj.tEx + system.rfRingdownTime + system.rfDeadTime;
obj.RFex1 = mr.makeBlockPulse(obj.flipex(1),system,'PhaseOffset',0,'Duration',obj.tEx);
obj.RFex2 = mr.makeBlockPulse(obj.flipex(2),system,'PhaseOffset',0,'Duration',obj.tEx);
fov = obj.scanParams.fov;
Nx = obj.scanParams.Nx;
Ny = obj.scanParams.Ny;
Nz = obj.scanParams.Nz;
deltaKx=1/fov(1);
kxWidth = Nx*deltaKx;
deltaKy = 1/fov(2);
deltaKz = 1/fov(3);
pe_steps = (1:(Ny))-0.5*Ny-1;
par_steps = (1:(Nz))-0.5*Nz-1;
obj.PE_area_steps = pe_steps*deltaKy;
obj.Par_area_steps = par_steps*deltaKz;
%Preliminary Readout acuisition gradient and spoiler gradient.
%Used for later calculation of actual gradients
GRacq = mr.makeTrapezoid('x',system,'FlatArea',kxWidth,'FlatTime',obj.readoutTime);%,'riseTime',obj.dG);
ROGradientTime = obj.readoutTime + GRacq.riseTime + GRacq.fallTime;
obj.tSp = 2/4 * (obj.scanParams.echoSpacing - ROGradientTime - obj.tExwd);
obj.tPreph = 2/4 * (obj.scanParams.echoSpacing - ROGradientTime - obj.tExwd);
GRspr = mr.makeTrapezoid('x',system,'area',GRacq.area,'duration',obj.tSp);%,'riseTime',obj.dG);
AGRspr=GRspr.area; %GRacq.area/2*fspR;
AGRpreph = -1* GRacq.area/2;%GRacq.area*(1+fspR)/2;
GRpreph = mr.makeTrapezoid('x',system,'Area',AGRpreph,'duration',obj.tPreph);%,'riseTime',obj.dG); %Prephase readout gradient
%Readout objects calculation
obj.GR3 = GRpreph;
GR6times=[0, GRacq.riseTime, GRacq.riseTime + obj.readoutTime];
GR6amp=[0, GRacq.amplitude, GRacq.amplitude];
obj.GR6 = mr.makeExtendedTrapezoid('x','times',GR6times,'amplitudes',GR6amp);
GR7times=[0 GRspr.riseTime GRspr.riseTime+GRspr.flatTime GRspr.riseTime+GRspr.flatTime+GRspr.fallTime];
GR7amp=[GRacq.amplitude GRspr.amplitude GRspr.amplitude 0];
obj.GR7 = mr.makeExtendedTrapezoid('x','times',GR7times,'amplitudes',GR7amp);
obj.ADC = mr.makeAdc(obj.scanParams.Nx,'Duration',obj.scanParams.samplingTime, 'Delay', system.adcDeadTime + GRacq.riseTime);
%Readout objects calculation END
%========== Calculate TSE object properties END ========
end
%Append a GRE block to the seq object
% INPUTS:
% -(obj: reference to this TSE object itself)
% - seq : The sequence object to be appended to
% - system: system object (struct) keeping track of heardware limitations
% - pe_indices: [ky, kz] Array of kspace locations. size(pe_indices,1) should be equal to nechos
% OUTPUTS:
% - seq: appended seq object
function seq = AppendGREBlock(obj,seq,system,pe_indices)
totalEchos = sum(obj.scanParams.nechos(:));
if size(pe_indices,1) ~= totalEchos
error(['GRE Block append: size of pe_indices is not consistant with number of echos ',...
'(',num2str(totalEchos),')'])
end
ky_array = pe_indices(:,1);
kz_array = pe_indices(:,2);
PE_area_array = obj.PE_area_steps(ky_array);
Par_area_array = obj.Par_area_steps(kz_array);
% Add SR
seq = obj.AppendSRBlock(seq,system);
%Add GRE train
for iEcho = 1:totalEchos
% Define current RF for the 1st or 2nd GRE segment
if iEcho <= obj.scanParams.nechos(1)
GRErf = obj.RFex1;
else
GRErf = obj.RFex2;
end
%Adjust RF and ADC Phase (RF spoiling)
GRErf.phaseOffset = mod(117*(iEcho^2+iEcho+2)*pi/180,2*pi);
obj.ADC.phaseOffset = GRErf.phaseOffset;
%Add Excitation RF pulse
seq.addBlock(GRErf);
%Calculate phase and partition gradiatent from specified k-space location
GPpre = mr.makeTrapezoid('y',system,'Area',PE_area_array(iEcho),'Duration',obj.tPreph);%,'riseTime',obj.dG);
GPrew = mr.makeTrapezoid('y',system,'Area',-PE_area_array(iEcho),'Duration',obj.tPreph);%,'riseTime',obj.dG);
GSpre = mr.makeTrapezoid('z',system,'Area',Par_area_array(iEcho),'Duration',obj.tPreph);%,'riseTime',obj.dG);
GSrew = mr.makeTrapezoid('z',system,'Area',-Par_area_array(iEcho),'Duration',obj.tPreph);%,'riseTime',obj.dG);
seq.addBlock(obj.GR3, GPpre, GSpre); %Pre phase Gradients
seq.addBlock(obj.GR6,obj.ADC);
seq.addBlock(obj.GR7,GPrew,GSrew);
end
end
%Append a Saturation Recovery block to the seq object
% INPUTS:
% -(obj: reference to this TSE object itself)
% - seq : The sequence object to be appended to- pe_indices: [ky, kz] Array of kspace locations. size(pe_indices,1) should be equal to nechos
% - system: system object (struct) keeping track of heardware limitations
% OUTPUTS:
% - seq: appended seq object
function seq = AppendSRBlock(obj,seq,system)
% SR has 3 90 RF pulses. This makes 4 segments
% 90 90 90
% | | |
% | | |
% SEG 1 | SEG2 | SEG3 | SEG4
% Define gradient segments length
tSeg1 = 1000e-6;
tSeg2 = 8800e-6;
tSeg3 = 5800e-6;
tSeg4 = 2550e-6;
% Define RF
RF90 = mr.makeBlockPulse(pi/2,system,'PhaseOffset',0,'Duration',obj.tEx);
%Seg1 Gradients
GR_seg1 = mr.makeTrapezoid('x',system,'duration',tSeg1,'amplitude',system.maxGrad);%,'riseTime',obj.dG);
GS_seg1 = mr.makeTrapezoid('z',system,'duration',tSeg1,'amplitude',-1* system.maxGrad);%,'riseTime',obj.dG);
%Seg2 Gradients
GR_seg2 = mr.makeTrapezoid('x',system,'duration',tSeg2,'amplitude',system.maxGrad);%,'riseTime',obj.dG);
GS_seg2 = mr.makeTrapezoid('z',system,'duration',tSeg2,'amplitude',system.maxGrad);%,'riseTime',obj.dG);
%Seg3 Gradients
GR_seg3 = mr.makeTrapezoid('x',system,'duration',tSeg3,'amplitude',-1*system.maxGrad);%,'riseTime',obj.dG);
GS_seg3 = mr.makeTrapezoid('z',system,'duration',tSeg3,'amplitude',-1*system.maxGrad);%,'riseTime',obj.dG);
%Seg4 Gradients
GP_seg4 = mr.makeTrapezoid('y',system,'duration',tSeg4,'amplitude', system.maxGrad);%,'riseTime',obj.dG);
%Append RF and Gradients
seq.addBlock(GR_seg1,GS_seg1);
seq.addBlock(RF90); % -----------RF 1
seq.addBlock(GR_seg2,GS_seg2);
seq.addBlock(RF90); % -----------RF 2
seq.addBlock(GR_seg3,GS_seg3);
seq.addBlock(RF90); % -----------RF 3
seq.addBlock(GP_seg4);
end
end
end