├── 01_from_FID_to_PRESS
├── README.md
├── data
│ ├── 01_FID.dat
│ ├── 01_FID.seq
│ ├── 02_FID_MFA.dat
│ ├── 02_FID_MFA.seq
│ ├── 03_SE_noSpoil.dat
│ ├── 03_SE_noSpoil.seq
│ ├── 04_SE_BackgroundGrad.dat
│ ├── 04_SE_BackgroundGrad.seq
│ ├── 05_SE_withSpoilers.dat
│ ├── 05_SE_withSpoilers.seq
│ ├── 06_PRESS_center.dat
│ ├── 06_PRESS_center.seq
│ ├── 07_PRESS_offcenter.dat
│ └── 07_PRESS_offcenter.seq
├── doc
│ ├── 01_from_FID_to_PRESS.pdf
│ ├── 01_from_FID_to_PRESS.pptx
│ └── Fig_1.png
├── recon
│ └── r01_FID.m
└── seq
│ ├── s01_FID.m
│ ├── s02_FID_multipleFAs.m
│ ├── s03_SE.m
│ ├── s04_SE_withBackGrad.m
│ ├── s05_SE_withSpolers.m
│ └── s06_PRESS.m
├── 02_basic_gradient_echo
├── README.md
├── data
│ ├── 01_GRE_example.dat
│ ├── 01_GRE_example.seq
│ ├── 02_GRE_noSpoil_noRephase.dat
│ ├── 02_GRE_noSpoil_noRephase.seq
│ ├── 03_GRE_withSpoil_noRephase.dat
│ ├── 03_GRE_withSpoil_noRephase.seq
│ ├── 04_GRE_xSpoil_yRephase.dat
│ ├── 04_GRE_xSpoil_yRephase.seq
│ ├── 05_GRE_xSpoil_yRephase_RFspoil.dat
│ ├── 05_GRE_xSpoil_yRephase_RFspoil.seq
│ ├── 06_GRE_xSpoil_yRephase_RFspoilADC.dat
│ ├── 06_GRE_xSpoil_yRephase_RFspoilADC.seq
│ ├── 07_GRE_xSpoil_yRephase_RFspoilADC_dummy.dat
│ └── 07_GRE_xSpoil_yRephase_RFspoilADC_dummy.seq
├── doc
│ ├── 02_basic_gradient_echo.pdf
│ ├── 02_basic_gradient_echo.pptx
│ ├── Fig_1.png
│ ├── Fig_2.png
│ └── Fig_3.png
├── recon
│ └── r01_2DFFT.m
└── seq
│ ├── s01_GRE_tutorial_step0.m
│ ├── s02_GRE_tutorial_step1.m
│ ├── s03_GRE_tutorial_step2.m
│ ├── s04_GRE_tutorial_step3.m
│ ├── s05_GRE_tutorial_step4.m
│ └── s06_GRE_tutorial_step5.m
├── 11_from_GRE_to_EPI
├── README.md
├── data
│ ├── 01_GRE.dat
│ ├── 01_GRE.seq
│ ├── 02_GRE_3echoes.dat
│ ├── 02_GRE_3echoes.seq
│ ├── 03_GRE_3echoes_bipolar.dat
│ ├── 03_GRE_3echoes_bipolar.seq
│ ├── 04a_GRE_3seg.dat
│ ├── 04a_GRE_3seg.seq
│ ├── 04b_GRE_5seg_fast.dat
│ ├── 04b_GRE_5seg_fast.seq
│ ├── 04c_improvised_EPI.dat
│ ├── 04c_improvised_EPI.seq
│ ├── 04c_improvised_EPI_noPE.dat
│ ├── 04c_improvised_EPI_noPE.seq
│ ├── 04c_improvised_EPI_other_phantom_other scanner.dat
│ ├── 04c_improvised_EPI_other_phantom_other scanner.seq
│ ├── 04c_improvised_EPI_other_phantom_other scanner_noPE.dat
│ ├── 04c_improvised_EPI_other_phantom_other scanner_noPE.seq
│ ├── 05_EPI.dat
│ ├── 05_EPI.seq
│ ├── 05_EPI_noPE.dat
│ ├── 05_EPI_noPE.seq
│ ├── 06_EPI_singleTraj.dat
│ └── 06_EPI_singleTraj.seq
├── doc
│ ├── 11_from_GRE_to_EPI.pdf
│ ├── 11_from_GRE_to_EPI.pptx
│ ├── Handout.pdf
│ └── mri_together_esmrmb_banner.png
├── recon
│ ├── r01_2DFFT.m
│ ├── r02_2DEPI.m
│ └── r03_2DGD.m
└── seq
│ ├── s01_GradientEcho.m
│ ├── s02_MultiGradientEcho.m
│ ├── s03_BipolarMultiGradientEcho.m
│ ├── s04a_SegmentedGradientEcho.m
│ ├── s04b_SegmentedGradientEcho.m
│ ├── s04c_SegmentedGradientEcho.m
│ ├── s05_EchoPlanarImaging.m
│ └── s06_EPI_SingleTraj.m
├── 12_Radial_and_nonCartesian
├── README.md
├── doc
│ ├── 12_Radial_and_nonCartesian.pdf
│ └── 12_Radial_and_nonCartesian.pptx
├── recon
│ ├── r01_2DFFT.m
│ ├── r02_2D_iRadon.m
│ ├── r03_2D_Gridding.m
│ └── r04_radial_delay_calculation.m
└── seq
│ ├── s01_CartesianSE.m
│ ├── s02_RadialSE.m
│ ├── s03_CartesianGradientEcho.m
│ ├── s04_RadialGradientEcho.m
│ ├── s05_FastRadialGradientEcho.m
│ └── s06_Spiral.m
├── LICENSE
├── README.md
└── doc
├── meld_comparison.png
└── pulseq_Concepts_Nov2022.pdf
/01_from_FID_to_PRESS/README.md:
--------------------------------------------------------------------------------
1 | # Pulseq tutorial "From FID to PRESS"
2 |
3 | Welcome to the "From FID to PRESS" tutorial repository! This was initially developed for the Pulseq software demonstration and hands-on session at the **Italian Chapter of ISMRM 2022** in Pisa.
4 |
5 | The tutorial starts with the very basic first steps from an FID and non-selective spin-echo sequence and moves on toward the spectroscopic PRESS sequence. The raw data were acquired in a fat-water phantom, which explains the appearance of the off-center press spectrum, which was deliberately placed to capture both fat and water.
6 |
7 | The slide deck entitled [doc/01_from_FID_to_PRESS.pdf](doc/01_from_FID_to_PRESS.pdf) explains the basic properties of the objects from which the pulse sequence is built,
8 | such as RF and gradient pulses, and shows the sequence diagrams of all
9 | steps and visualises the changes at each step. We recommend using Meld
10 | software to highlight the source code changes between steps 01 and 05.
11 |
12 | ***s01\_FID***
13 |
14 | ***s01*** describes the simplest FID sequence with a single flip angle
15 | (FA). It is constructed with two blocks -- one with an RF event and the
16 | other with an ADC event.
17 |
18 | ***s02\_FID\_multipleFAs***
19 |
20 | ***s02*** describes an FID sequence with 9 FAs, established based on the
21 | FID sequence described in ***s01***. The TR containing two blocks is
22 | repeated 9 times. The flip angle of the RF event is changed during each
23 | TR repetition.
24 |
25 | ***s03\_SE***
26 |
27 | ***s03*** describes a spin-echo (SE) acquisition with 4 blocks. It
28 | contains an RF pulse for non-selective excitation, an RF pulse for
29 | refocusing and an ADC.
30 |
31 | ***s04\_SE\_withBackGrad***
32 |
33 | A background gradient is added to ***s03*** during the entire sequence
34 | to construct ***s04***.
35 |
36 | ***s05\_SE\_withSpoilers***
37 |
38 | ***s05*** is constructed based on ***s03***: two spoiler gradients are
39 | inserted during the delay of the refocusing RF pulse and the delay of
40 | the ADC in ***s03***, acting as a pair of crushers.
41 |
42 | ***s06\_PRESS***
43 |
44 | ***s06*** establishes a Point RESolved Spectroscopy (PRESS) sequence.
45 | The core sequence consists of three slice-selective RF pulses
46 | (90°-180°-180°) applied concurrently with three orthogonal gradients (x,
47 | y, and z) (for more details, please go to
48 | [https://mriquestions.com/press.html\#](https://mriquestions.com/press.html)).
49 |
50 | The mr.makeSincPulse function makes a sinc pulse and gradients for slice
51 | selection.
52 |
53 | The mr.makeExtendedTrapezoidArea function makes a shortest possible
54 | extended trapezoid with a given area, which starts and ends as non-zero
55 | gradient values.
56 |
57 | The mr.makeExtendedTrapezoid function creates an "arbitrary" gradient by
58 | combing a set of amplitude points and the corresponding set of time
59 | points, as shown in Figure 1.
60 |
61 |
62 |
63 | **Figure** **1** slice-selective **sinc pulse** and **gradient** with a
64 | pair of crusher gradients for selecting desired coherence pathways.
65 | **a** (**c**) and **b** (**d**) are the first and last points of the
66 | **pre-** (**post-**) crusher gradient, which are both realised as
67 | Extended Trapezoids.
68 |
69 | The sequence diagram of ***s06*** includes 7 blocks per TR. Note that the TE is
70 | defined as 2 \* delay between the centres of the two refocusing RF
71 | pulses.
72 |
73 | ## Quick links
74 |
75 | Pulseq Matlab repository:
76 | https://github.com/pulseq/pulseq
77 |
78 | ## Quick instructions
79 |
80 | The source code of the demo sequences and reconstruction scripts is the core of this repository. Please download the files to your computer and make them available to Matlab (e.g. by saving them in a subdirectory inside your Pulseq-Matlab installation and adding them to the Matlab's path). There are two sub-directories:
81 |
82 | * seq : contains example pulse sequences specifically prepared for this demo
83 | * recon : contains the reconstruction scripts tested with the above sequences
84 | * data : contains raw MR data in the Siemens TWIX format and the corresponding pulse sequences in the Pulseq format
85 |
86 | ## How to follow
87 |
88 | We strongly recommend using a text compare tool like *meld* (see this [Wikipedia page](https://en.wikipedia.org/wiki/Meld_(software)) and compare sequences from subsequent steps to visualise the respective steps.
89 |
90 | ## Further links
91 |
92 | Check out the main *Pulseq* repository at https://github.com/pulseq/pulseq and familiarising yourself with the code, example sequences and reconstruction scripts (see
93 | [pulseq/matlab/demoSeq](https://github.com/pulseq/pulseq/tree/master/matlab/demoSeq) and [pulseq/matlab/demoRecon](https://github.com/pulseq/pulseq/tree/master/matlab/demoRecon)). If you already use Pulseq, consider updating to the current version.
94 |
95 |
96 |
97 |
--------------------------------------------------------------------------------
/01_from_FID_to_PRESS/data/01_FID.dat:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pulseq/tutorials/651dcc7bb46353ceb05783acd39c603abca13ca3/01_from_FID_to_PRESS/data/01_FID.dat
--------------------------------------------------------------------------------
/01_from_FID_to_PRESS/data/01_FID.seq:
--------------------------------------------------------------------------------
1 | # Pulseq sequence file
2 | # Created by MATLAB mr toolbox
3 |
4 | [VERSION]
5 | major 1
6 | minor 4
7 | revision 0
8 |
9 | [DEFINITIONS]
10 | AdcRasterTime 1e-07
11 | BlockDurationRaster 1e-05
12 | GradientRasterTime 1e-05
13 | RadiofrequencyRasterTime 1e-06
14 | TotalDuration 5
15 |
16 | # Format of blocks:
17 | # NUM DUR RF GX GY GZ ADC EXT
18 | [BLOCKS]
19 | 1 62 1 0 0 0 0 0
20 | 2 499938 0 0 0 0 1 0
21 |
22 | # Format of RF events:
23 | # id amplitude mag_id phase_id time_shape_id delay freq phase
24 | # .. Hz .... .... .... us Hz rad
25 | [RF]
26 | 1 500 1 2 3 100 0 0
27 |
28 | # Format of ADC events:
29 | # id num dwell delay freq phase
30 | # .. .. ns us Hz rad
31 | [ADC]
32 | 1 8192 31250 29730 0 0
33 |
34 | # Sequence Shapes
35 | [SHAPES]
36 |
37 | shape_id 1
38 | num_samples 2
39 | 1
40 | 1
41 |
42 | shape_id 2
43 | num_samples 2
44 | 0
45 | 0
46 |
47 | shape_id 3
48 | num_samples 2
49 | 0
50 | 500
51 |
52 |
53 | [SIGNATURE]
54 | # This is the hash of the Pulseq file, calculated right before the [SIGNATURE] section was added
55 | # It can be reproduced/verified with md5sum if the file trimmed to the position right above [SIGNATURE]
56 | # The new line character preceding [SIGNATURE] BELONGS to the signature (and needs to be sripped away for recalculating/verification)
57 | Type md5
58 | Hash 5caad2dc77321c47fe1cf56d9ef581b9
59 |
--------------------------------------------------------------------------------
/01_from_FID_to_PRESS/data/02_FID_MFA.dat:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pulseq/tutorials/651dcc7bb46353ceb05783acd39c603abca13ca3/01_from_FID_to_PRESS/data/02_FID_MFA.dat
--------------------------------------------------------------------------------
/01_from_FID_to_PRESS/data/02_FID_MFA.seq:
--------------------------------------------------------------------------------
1 | # Pulseq sequence file
2 | # Created by MATLAB mr toolbox
3 |
4 | [VERSION]
5 | major 1
6 | minor 4
7 | revision 0
8 |
9 | [DEFINITIONS]
10 | AdcRasterTime 1e-07
11 | BlockDurationRaster 1e-05
12 | GradientRasterTime 1e-05
13 | RadiofrequencyRasterTime 1e-06
14 |
15 | # Format of blocks:
16 | # NUM DUR RF GX GY GZ ADC EXT
17 | [BLOCKS]
18 | 1 112 1 0 0 0 0 0
19 | 2 499888 0 0 0 0 1 0
20 | 3 112 2 0 0 0 0 0
21 | 4 499888 0 0 0 0 1 0
22 | 5 112 3 0 0 0 0 0
23 | 6 499888 0 0 0 0 1 0
24 | 7 112 4 0 0 0 0 0
25 | 8 499888 0 0 0 0 1 0
26 | 9 112 5 0 0 0 0 0
27 | 10 499888 0 0 0 0 1 0
28 | 11 112 6 0 0 0 0 0
29 | 12 499888 0 0 0 0 1 0
30 | 13 112 7 0 0 0 0 0
31 | 14 499888 0 0 0 0 1 0
32 | 15 112 8 0 0 0 0 0
33 | 16 499888 0 0 0 0 1 0
34 | 17 112 9 0 0 0 0 0
35 | 18 499888 0 0 0 0 1 0
36 |
37 | # Format of RF events:
38 | # id amplitude mag_id phase_id time_shape_id delay freq phase
39 | # .. Hz .... .... .... us Hz rad
40 | [RF]
41 | 1 55.5556 1 2 3 100 0 0
42 | 2 111.111 1 2 3 100 0 0
43 | 3 166.667 1 2 3 100 0 0
44 | 4 222.222 1 2 3 100 0 0
45 | 5 277.778 1 2 3 100 0 0
46 | 6 333.333 1 2 3 100 0 0
47 | 7 388.889 1 2 3 100 0 0
48 | 8 444.444 1 2 3 100 0 0
49 | 9 500 1 2 3 100 0 0
50 |
51 | # Format of ADC events:
52 | # id num dwell delay freq phase
53 | # .. .. ns us Hz rad
54 | [ADC]
55 | 1 8192 31250 29480 0 0
56 |
57 | # Sequence Shapes
58 | [SHAPES]
59 |
60 | shape_id 1
61 | num_samples 2
62 | 1
63 | 1
64 |
65 | shape_id 2
66 | num_samples 2
67 | 0
68 | 0
69 |
70 | shape_id 3
71 | num_samples 2
72 | 0
73 | 1000
74 |
75 |
76 | [SIGNATURE]
77 | # This is the hash of the Pulseq file, calculated right before the [SIGNATURE] section was added
78 | # It can be reproduced/verified with md5sum if the file trimmed to the position right above [SIGNATURE]
79 | # The new line character preceding [SIGNATURE] BELONGS to the signature (and needs to be sripped away for recalculating/verification)
80 | Type md5
81 | Hash 7e2b89c1ae6eca64fc825f6ccdc035ef
82 |
--------------------------------------------------------------------------------
/01_from_FID_to_PRESS/data/03_SE_noSpoil.dat:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pulseq/tutorials/651dcc7bb46353ceb05783acd39c603abca13ca3/01_from_FID_to_PRESS/data/03_SE_noSpoil.dat
--------------------------------------------------------------------------------
/01_from_FID_to_PRESS/data/03_SE_noSpoil.seq:
--------------------------------------------------------------------------------
1 | # Pulseq sequence file
2 | # Created by MATLAB mr toolbox
3 |
4 | [VERSION]
5 | major 1
6 | minor 4
7 | revision 0
8 |
9 | [DEFINITIONS]
10 | AdcRasterTime 1e-07
11 | BlockDurationRaster 1e-05
12 | GradientRasterTime 1e-05
13 | RadiofrequencyRasterTime 1e-06
14 | TotalDuration 0.25
15 |
16 | # Format of blocks:
17 | # NUM DUR RF GX GY GZ ADC EXT
18 | [BLOCKS]
19 | 1 112 1 0 0 0 0 0
20 | 2 5388 0 0 0 0 0 0
21 | 3 112 2 0 0 0 0 0
22 | 4 19388 0 0 0 0 1 0
23 |
24 | # Format of RF events:
25 | # id amplitude mag_id phase_id time_shape_id delay freq phase
26 | # .. Hz .... .... .... us Hz rad
27 | [RF]
28 | 1 250 1 2 3 100 0 0
29 | 2 500 1 2 3 100 0 0
30 |
31 | # Format of ADC events:
32 | # id num dwell delay freq phase
33 | # .. .. ns us Hz rad
34 | [ADC]
35 | 1 8192 12500 3280 0 0
36 |
37 | # Sequence Shapes
38 | [SHAPES]
39 |
40 | shape_id 1
41 | num_samples 2
42 | 1
43 | 1
44 |
45 | shape_id 2
46 | num_samples 2
47 | 0
48 | 0
49 |
50 | shape_id 3
51 | num_samples 2
52 | 0
53 | 1000
54 |
55 |
56 | [SIGNATURE]
57 | # This is the hash of the Pulseq file, calculated right before the [SIGNATURE] section was added
58 | # It can be reproduced/verified with md5sum if the file trimmed to the position right above [SIGNATURE]
59 | # The new line character preceding [SIGNATURE] BELONGS to the signature (and needs to be sripped away for recalculating/verification)
60 | Type md5
61 | Hash 67f9e34da9b97a9f23af82d17863462c
62 |
--------------------------------------------------------------------------------
/01_from_FID_to_PRESS/data/04_SE_BackgroundGrad.dat:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pulseq/tutorials/651dcc7bb46353ceb05783acd39c603abca13ca3/01_from_FID_to_PRESS/data/04_SE_BackgroundGrad.dat
--------------------------------------------------------------------------------
/01_from_FID_to_PRESS/data/04_SE_BackgroundGrad.seq:
--------------------------------------------------------------------------------
1 | # Pulseq sequence file
2 | # Created by MATLAB mr toolbox
3 |
4 | [VERSION]
5 | major 1
6 | minor 4
7 | revision 0
8 |
9 | [DEFINITIONS]
10 | AdcRasterTime 1e-07
11 | BlockDurationRaster 1e-05
12 | GradientRasterTime 1e-05
13 | RadiofrequencyRasterTime 1e-06
14 | TotalDuration 0.25006
15 |
16 | # Format of blocks:
17 | # NUM DUR RF GX GY GZ ADC EXT
18 | [BLOCKS]
19 | 1 3 0 1 0 0 0 0
20 | 2 112 1 2 0 0 0 0
21 | 3 5388 0 3 0 0 0 0
22 | 4 112 2 2 0 0 0 0
23 | 5 19388 0 4 0 0 1 0
24 | 6 3 0 5 0 0 0 0
25 |
26 | # Format of RF events:
27 | # id amplitude mag_id phase_id time_shape_id delay freq phase
28 | # .. Hz .... .... .... us Hz rad
29 | [RF]
30 | 1 250 3 4 5 100 0 0
31 | 2 500 3 4 5 100 0 0
32 |
33 | # Format of arbitrary gradients:
34 | # time_shape_id of 0 means default timing (stepping with grad_raster starting at 1/2 of grad_raster)
35 | # id amplitude amp_shape_id time_shape_id delay
36 | # .. Hz/m .. .. us
37 | [GRADIENTS]
38 | 1 5000 1 2 0
39 | 2 5000 3 6 0
40 | 3 5000 3 7 0
41 | 4 5000 3 8 0
42 | 5 5000 9 2 0
43 |
44 | # Format of ADC events:
45 | # id num dwell delay freq phase
46 | # .. .. ns us Hz rad
47 | [ADC]
48 | 1 8192 12500 3280 0 0
49 |
50 | # Sequence Shapes
51 | [SHAPES]
52 |
53 | shape_id 1
54 | num_samples 2
55 | 0
56 | 1
57 |
58 | shape_id 2
59 | num_samples 2
60 | 0
61 | 3
62 |
63 | shape_id 3
64 | num_samples 2
65 | 1
66 | 1
67 |
68 | shape_id 4
69 | num_samples 2
70 | 0
71 | 0
72 |
73 | shape_id 5
74 | num_samples 2
75 | 0
76 | 1000
77 |
78 | shape_id 6
79 | num_samples 2
80 | 0
81 | 112
82 |
83 | shape_id 7
84 | num_samples 2
85 | 0
86 | 5388
87 |
88 | shape_id 8
89 | num_samples 2
90 | 0
91 | 19388
92 |
93 | shape_id 9
94 | num_samples 2
95 | 1
96 | 0
97 |
98 |
99 | [SIGNATURE]
100 | # This is the hash of the Pulseq file, calculated right before the [SIGNATURE] section was added
101 | # It can be reproduced/verified with md5sum if the file trimmed to the position right above [SIGNATURE]
102 | # The new line character preceding [SIGNATURE] BELONGS to the signature (and needs to be sripped away for recalculating/verification)
103 | Type md5
104 | Hash aac1d624e8bf86af3edd1f06085c1ae2
105 |
--------------------------------------------------------------------------------
/01_from_FID_to_PRESS/data/05_SE_withSpoilers.dat:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pulseq/tutorials/651dcc7bb46353ceb05783acd39c603abca13ca3/01_from_FID_to_PRESS/data/05_SE_withSpoilers.dat
--------------------------------------------------------------------------------
/01_from_FID_to_PRESS/data/05_SE_withSpoilers.seq:
--------------------------------------------------------------------------------
1 | # Pulseq sequence file
2 | # Created by MATLAB mr toolbox
3 |
4 | [VERSION]
5 | major 1
6 | minor 4
7 | revision 0
8 |
9 | [DEFINITIONS]
10 | AdcRasterTime 1e-07
11 | BlockDurationRaster 1e-05
12 | GradientRasterTime 1e-05
13 | RadiofrequencyRasterTime 1e-06
14 | TotalDuration 0.25
15 |
16 | # Format of blocks:
17 | # NUM DUR RF GX GY GZ ADC EXT
18 | [BLOCKS]
19 | 1 112 1 0 0 0 0 0
20 | 2 5315 0 0 0 0 0 0
21 | 3 185 2 0 0 1 0 0
22 | 4 19388 0 0 0 1 1 0
23 |
24 | # Format of RF events:
25 | # id amplitude mag_id phase_id time_shape_id delay freq phase
26 | # .. Hz .... .... .... us Hz rad
27 | [RF]
28 | 1 250 1 2 3 100 0 0
29 | 2 500 1 2 3 830 0 0
30 |
31 | # Format of trapezoid gradients:
32 | # id amplitude rise flat fall delay
33 | # .. Hz/m us us us us
34 | [TRAP]
35 | 1 1.69492e+06 240 350 240 0
36 |
37 | # Format of ADC events:
38 | # id num dwell delay freq phase
39 | # .. .. ns us Hz rad
40 | [ADC]
41 | 1 8192 12500 3280 0 0
42 |
43 | # Sequence Shapes
44 | [SHAPES]
45 |
46 | shape_id 1
47 | num_samples 2
48 | 1
49 | 1
50 |
51 | shape_id 2
52 | num_samples 2
53 | 0
54 | 0
55 |
56 | shape_id 3
57 | num_samples 2
58 | 0
59 | 1000
60 |
61 |
62 | [SIGNATURE]
63 | # This is the hash of the Pulseq file, calculated right before the [SIGNATURE] section was added
64 | # It can be reproduced/verified with md5sum if the file trimmed to the position right above [SIGNATURE]
65 | # The new line character preceding [SIGNATURE] BELONGS to the signature (and needs to be sripped away for recalculating/verification)
66 | Type md5
67 | Hash 7a621205cecc36a2aadeeccf9d35399f
68 |
--------------------------------------------------------------------------------
/01_from_FID_to_PRESS/data/06_PRESS_center.dat:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pulseq/tutorials/651dcc7bb46353ceb05783acd39c603abca13ca3/01_from_FID_to_PRESS/data/06_PRESS_center.dat
--------------------------------------------------------------------------------
/01_from_FID_to_PRESS/data/07_PRESS_offcenter.dat:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pulseq/tutorials/651dcc7bb46353ceb05783acd39c603abca13ca3/01_from_FID_to_PRESS/data/07_PRESS_offcenter.dat
--------------------------------------------------------------------------------
/01_from_FID_to_PRESS/doc/01_from_FID_to_PRESS.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pulseq/tutorials/651dcc7bb46353ceb05783acd39c603abca13ca3/01_from_FID_to_PRESS/doc/01_from_FID_to_PRESS.pdf
--------------------------------------------------------------------------------
/01_from_FID_to_PRESS/doc/01_from_FID_to_PRESS.pptx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pulseq/tutorials/651dcc7bb46353ceb05783acd39c603abca13ca3/01_from_FID_to_PRESS/doc/01_from_FID_to_PRESS.pptx
--------------------------------------------------------------------------------
/01_from_FID_to_PRESS/doc/Fig_1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pulseq/tutorials/651dcc7bb46353ceb05783acd39c603abca13ca3/01_from_FID_to_PRESS/doc/Fig_1.png
--------------------------------------------------------------------------------
/01_from_FID_to_PRESS/recon/r01_FID.m:
--------------------------------------------------------------------------------
1 | % very basic FID data handling example
2 | %
3 | % needs mapVBVD in the path
4 |
5 | %% Load data from the given directory sorted by name
6 | path='../data'; % directory to be scanned for data files
7 | nF=1; % the number of the file / data set to load
8 |
9 | pattern='*.dat';
10 | D=dir([path filesep pattern]);
11 | [~,I]=sort(string({D(:).name}));
12 | data_file_path=[path filesep D(I(nF)).name];
13 |
14 | fprintf(['loading `' data_file_path '´ ...\n']);
15 | twix_obj = mapVBVD(data_file_path);
16 |
17 | %% Load the latest file from a dir
18 | %path='../IceNIH_RawSend/'; % directory to be scanned for data files
19 | %pattern='*.dat';
20 | %D=dir([path filesep pattern]);
21 | %[~,I]=sort([D(:).datenum]);
22 | %data_file_path=[path D(I(end-5)).name]; % use end-1 to reconstruct the second-last data set, etc.
23 |
24 | %% raw data preparation
25 | if iscell(twix_obj)
26 | data_unsorted = double(twix_obj{end}.image.unsorted());
27 | else
28 | data_unsorted = double(twix_obj.image.unsorted());
29 | end
30 |
31 | channels=size(data_unsorted,2);
32 | adc_len=size(data_unsorted,1);
33 | readouts=size(data_unsorted,3);
34 |
35 | rawdata = permute(data_unsorted, [1,3,2]);
36 |
37 | %% Load sequence from the file with the same name as the raw data
38 | seq_file_path = [data_file_path(1:end-3) 'seq'];
39 | fprintf(['loading `' seq_file_path '´ ...\n']);
40 | seq = mr.Sequence(); % Create a new sequence object
41 | seq.read(seq_file_path,'detectRFuse');
42 | if strcmp(seq.getDefinition('Name'),'fid-mfa')
43 | fprintf('FID-MFA sequence detected, re-loading the sequence file ...\n');
44 | seq = mr.Sequence(); % reload the sequence without detecting the RF pulse use because otherwise the code assumes that we have refocusing pulses...
45 | seq.read(seq_file_path);
46 | end
47 |
48 | %% we just want t_adc
49 | [ktraj_adc, t_adc, ktraj, t, t_excitation, t_refocusing]=seq.calculateKspacePP();
50 |
51 | t_adc=reshape(t_adc,[adc_len,readouts]);
52 | % calc TE but account for dummy scans...
53 | %[~,iND]=max(t_excitation(t_excitation1
65 | for j=2:channels
66 | plot(t_e, abs(rawdata(:,:,j)));
67 | end
68 | end
69 | plot(t_e(1),0); % trick to force Y axis scaling
70 | xlabel('time since excitation /s');
71 |
72 | if (readouts>1)
73 | figure; plot(abs(rawdata(4,:,1))); title('time evolution (4th FID point)');
74 | if channels>1
75 | hold on;
76 | for j=2:channels
77 | plot(abs(rawdata(4,:,j)));
78 | end
79 | end
80 |
81 | dataavg=squeeze(mean(rawdata,2));
82 | figure; plot(t_e, abs(dataavg(:,1))); title('averaged FIDs');
83 | if channels>1
84 | hold on;
85 | for j=2:channels
86 | plot(t_e, abs(dataavg(:,2)));
87 | end
88 | end
89 | xlabel('time since excitation /s');
90 | end
91 |
92 | %% plot spectr(um/a)
93 | data_fft=fftshift(fft(fftshift(rawdata,1)),1);
94 | w_axis=(-adc_len/2:(adc_len/2-1))/((t_adc(end,1,1)-t_adc(1,1,1))/(adc_len-1)*adc_len)/twix_obj.hdr.Meas.lFrequency*1e6';
95 | figure;
96 | plot(w_axis, abs(data_fft(:,:,1))); title('abs spectr(um/a)');
97 | xlim([-10 10]); xlabel('frequency /ppm');
98 | set(gca,'Xdir','reverse')
99 |
--------------------------------------------------------------------------------
/01_from_FID_to_PRESS/seq/s01_FID.m:
--------------------------------------------------------------------------------
1 | system = mr.opts('rfRingdownTime', 20e-6, 'rfDeadTime', 100e-6, ...
2 | 'adcDeadTime', 20e-6);
3 |
4 | seq=mr.Sequence(system); % Create a new sequence object
5 | Nx=8192;
6 | Nrep=1;
7 | adcDur=512e-3;
8 | rfDur=500e-6;
9 | TR=5000e-3;
10 | TE=30e-3; % the used coil has a very long switching time!
11 | % todo1: change flip angle to reduce SNR
12 | % todo2: change repetitions to increase SNR
13 |
14 | % Create non-selective pulse
15 | rf = mr.makeBlockPulse(pi/2,'Duration',rfDur, 'system', system, 'use', 'excitation');
16 |
17 | % Define delays and ADC events
18 | adc = mr.makeAdc(Nx,'Duration',adcDur, 'system', system, 'delay', TE-rf.shape_dur/2-system.rfRingdownTime);
19 |
20 | delayTR=TR-mr.calcDuration(rf);
21 | assert(delayTR>=0);
22 |
23 | % Loop over repetitions and define sequence blocks
24 | for i=1:Nrep
25 | seq.addBlock(rf);
26 | seq.addBlock(adc,mr.makeDelay(delayTR));
27 | end
28 |
29 | seq.plot();
30 |
31 | % check whether the timing of the sequence is compatible with the scanner
32 | [ok, error_report]=seq.checkTiming;
33 |
34 | if (ok)
35 | fprintf('Timing check passed successfully\n');
36 | else
37 | fprintf('Timing check failed! Error listing follows:\n');
38 | fprintf([error_report{:}]);
39 | fprintf('\n');
40 | end
41 |
42 | seq.setDefinition('Name', 'fid');
43 | seq.write('fid.seq') % Write to pulseq file
44 | %seq.install('siemens'); % copy to scanner
45 |
46 | % optional slow step, but useful for testing during development e.g. for the real TE, TR or for staying within slewrate limits
47 | rep = seq.testReport;
48 | fprintf([rep{:}]);
49 |
--------------------------------------------------------------------------------
/01_from_FID_to_PRESS/seq/s02_FID_multipleFAs.m:
--------------------------------------------------------------------------------
1 | system = mr.opts('rfRingdownTime', 20e-6, 'rfDeadTime', 100e-6, ...
2 | 'adcDeadTime', 20e-6);
3 |
4 | seq=mr.Sequence(system); % Create a new sequence object
5 | Nx=8192;
6 | Nrep=1;
7 | adcDur=256e-3;
8 | rfDur=1000e-6; % increased to 1ms
9 | TR=5000e-3; % increased to 5s avoid T1 saturation
10 | TE=30e-3; % the used coil has a very long switching time!
11 | flip_angles=9;
12 | % todo: change flip_angles to 18 and pi/2 to pi below
13 |
14 | % Create non-selective pulse
15 | rf = mr.makeBlockPulse(pi/2,'Duration',rfDur, 'system', system, 'use', 'excitation');
16 |
17 | % Define delays and ADC events
18 | adc = mr.makeAdc(Nx,'Duration',adcDur, 'system', system, 'delay', TE-rf.shape_dur/2-system.rfRingdownTime);
19 |
20 | delayTR=TR-mr.calcDuration(rf);
21 | assert(delayTR>=0);
22 |
23 | for f=1:flip_angles
24 | rf = mr.makeBlockPulse(pi/flip_angles*f,'Duration',rfDur, 'system', system, 'use', 'excitation');
25 | % Loop over repetitions and define sequence blocks
26 | for i=1:Nrep
27 | seq.addBlock(rf);
28 | seq.addBlock(adc,mr.makeDelay(delayTR));
29 | end
30 | end
31 |
32 | seq.setDefinition('Name', 'fid-mfa');
33 | seq.write('fid-mfa.seq') % Write to pulseq file
34 | %seq.install('siemens'); % copy to scanner
35 |
36 | seq.plot();
37 |
--------------------------------------------------------------------------------
/01_from_FID_to_PRESS/seq/s03_SE.m:
--------------------------------------------------------------------------------
1 | system = mr.opts('rfRingdownTime', 20e-6, 'rfDeadTime', 100e-6, ...
2 | 'adcDeadTime', 20e-6);
3 |
4 | seq=mr.Sequence(system); % Create a new sequence object
5 | Nx=128;
6 | Nrep=1;
7 | adcDur=6.4e-3;
8 | rfDur=1000e-6;
9 | TR=13e-3;
10 | TE=10e-3;
11 | % todo: change ADC duration
12 |
13 | % Create non-selective excitation and refocusing pulses
14 | rf_ex = mr.makeBlockPulse(pi/2,'Duration',rfDur, 'system', system, 'use', 'excitation'); % for this phantom and this coil I had to reduce flip angle to avoid receiver saturation
15 | rf_ref = mr.makeBlockPulse(pi,'Duration',rfDur, 'system', system, 'use', 'refocusing'); % needed for the proper k-space calculation
16 |
17 | % Define delays and ADC events
18 | % delayTE1=TE/2-mr.calcDuration(rf_ex)/2-mr.calcDuration(rf_ref)/2;
19 | delayTE1 = TE/2 - rf_ex.shape_dur/2 - rf_ex.ringdownTime - rf_ref.delay - rf_ref.shape_dur/2 ;
20 | % delayTE2=TE/2-mr.calcDuration(rf_ref)+rf_ref.delay+mr.calcRfCenter(rf_ref)-adcDur/2; % this is not perfect, but -adcDur/2/Nx will break the raster alignment
21 | delayTE2 = TE/2 - rf_ref.shape_dur/2 - rf_ref.ringdownTime - adcDur / 2 ;
22 | %adc = mr.makeAdc(Nx,'Duration',adcDur, 'system', system, 'delay', delayTE2);
23 | adc = mr.makeAdc(Nx,'Duration',adcDur, 'system', system);
24 |
25 | delayTR=TR-mr.calcDuration(rf_ex)-delayTE1-mr.calcDuration(rf_ref);
26 |
27 | assert(delayTE1>=0);
28 | assert(delayTE2>=0);
29 | assert(delayTR>=0);
30 |
31 | % Loop over repetitions and define sequence blocks
32 | for i=1:Nrep
33 | seq.addBlock(rf_ex);
34 | seq.addBlock(delayTE1);
35 | seq.addBlock(rf_ref);
36 | seq.addBlock(delayTE2-adc.delay);
37 | seq.addBlock(adc,mr.makeDelay(delayTR));
38 | end
39 |
40 | seq.plot();
41 |
42 | % check whether the timing of the sequence is compatible with the scanner
43 | [ok, error_report]=seq.checkTiming;
44 |
45 | if (ok)
46 | fprintf('Timing check passed successfully\n');
47 | else
48 | fprintf('Timing check failed! Error listing follows:\n');
49 | fprintf([error_report{:}]);
50 | fprintf('\n');
51 | end
52 |
53 | seq.setDefinition('Name', 'se');
54 | seq.write('se.seq') % Write to pulseq file
55 | %seq.install('siemens'); % copy to scanner
56 |
57 | %% calculate k-space but only use it to check the TE calculation
58 | [ktraj_adc, t_adc, ktraj, t_ktraj, t_excitation, t_refocusing] = seq.calculateKspacePP('gradient_offset',[1 0 0]);
59 |
60 | assert(abs(t_refocusing-t_excitation-TE/2)<1e-6); % check that the refocusing happens at the 1/2 of TE
61 | assert(abs(t_adc(Nx/2)-t_excitation-TE)=0);
29 | assert(delayTE2>=0);
30 | assert(delayTR>=0);
31 |
32 | % ramp up the background gradient
33 | ramptime=abs(bg)/system.maxSlew;
34 | ramptime=ceil(ramptime/system.gradRasterTime)*system.gradRasterTime; % round-up to gradient raster
35 | ramptime=max(ramptime, 3*system.gradRasterTime); % and limit it to 3x system.gradRasterTime
36 | seq.addBlock(mr.makeExtendedTrapezoid('x','amplitudes',[0 bg],'times',[0 ramptime]));
37 |
38 | % Loop over repetitions and define sequence blocks
39 | for i=1:Nrep
40 | seq.addBlock(rf_ex,mr.makeExtendedTrapezoid('x','amplitudes',[bg bg],'times',[0 mr.calcDuration(rf_ex)]));
41 | seq.addBlock(mr.makeDelay(delayTE1),mr.makeExtendedTrapezoid('x','amplitudes',[bg bg],'times',[0 delayTE1]));
42 | seq.addBlock(rf_ref,mr.makeExtendedTrapezoid('x','amplitudes',[bg bg],'times',[0 mr.calcDuration(rf_ref)]));
43 | seq.addBlock(adc,mr.makeDelay(delayTR),mr.makeExtendedTrapezoid('x','amplitudes',[bg bg],'times',[0 delayTR]));
44 | end
45 |
46 | % ramp down the background gradient
47 | seq.addBlock(mr.makeExtendedTrapezoid('x','amplitudes',[bg 0],'times',[0 ramptime]));
48 |
49 | seq.plot();
50 |
51 | % check whether the timing of the sequence is compatible with the scanner
52 | [ok, error_report]=seq.checkTiming;
53 |
54 | if (ok)
55 | fprintf('Timing check passed successfully\n');
56 | else
57 | fprintf('Timing check failed! Error listing follows:\n');
58 | fprintf([error_report{:}]);
59 | fprintf('\n');
60 | end
61 |
62 | seq.setDefinition('Name', 'se-bg');
63 | seq.write('se-bg.seq') % Write to pulseq file
64 | %seq.install('siemens'); % copy to scanner
65 |
66 | % calculate and plot k-spaces
67 | [ktraj_adc, t_adc, ktraj, t_ktraj, t_excitation, t_refocusing] = seq.calculateKspacePP();
68 |
69 | figure; plot(t_ktraj, ktraj'); % plot the entire k-space trajectory
70 | hold on; plot(t_adc,ktraj_adc(1,:),'.'); % and sampling points on the kx-axis
71 |
72 | % calculate real TE and TR, etc
73 | % reported TR will be slighlty higher for Nrep=1 becuse it uses TA instead
74 | rep = seq.testReport;
75 | fprintf([rep{:}]);
76 |
--------------------------------------------------------------------------------
/01_from_FID_to_PRESS/seq/s05_SE_withSpolers.m:
--------------------------------------------------------------------------------
1 | system = mr.opts('rfRingdownTime', 20e-6, 'rfDeadTime', 100e-6, ...
2 | 'adcDeadTime', 20e-6);
3 |
4 | seq=mr.Sequence(system); % Create a new sequence object
5 | Nx=8192;
6 | Nrep=1;
7 | adcDur=102.4e-3;
8 | rfDur=1000e-6;
9 | TR=250e-3;
10 | TE=110e-3;
11 | spA=1000; % spoiler area in 1/m (=Hz/m*s)
12 | % todo: change spoiler area, remove one of spoilers and observe the signal
13 |
14 | % Create non-selective excitation and refocusing pulses
15 | rf_ex = mr.makeBlockPulse(pi/2,'Duration',rfDur, 'system', system, 'use', 'excitation'); % for this phantom and this coil I had to reduce flip angle to avoid receiver saturation
16 | rf_ref = mr.makeBlockPulse(pi,'Duration',rfDur, 'system', system, 'use', 'refocusing'); % needed for the proper k-space calculation
17 |
18 | % calculate spoiler gradient
19 | g_sp=mr.makeTrapezoid('z','Area',spA,'system',system);
20 | rf_ref.delay=max(mr.calcDuration(g_sp),rf_ref.delay);
21 |
22 | % Define delays and ADC events
23 | % delayTE1=TE/2-(mr.calcDuration(rf_ex)-mr.calcRfCenter(rf_ex)-rf_ex.delay)-rf_ref.delay-mr.calcRfCenter(rf_ref);
24 | delayTE1 = TE/2 - rf_ex.shape_dur/2 - rf_ex.ringdownTime - rf_ref.delay - rf_ref.shape_dur/2 ;
25 | % delayTE2=TE/2-mr.calcDuration(rf_ref)+rf_ref.delay+mr.calcRfCenter(rf_ref)-adcDur/2; % this is not perfect, but -adcDur/2/Nx will break the raster alignment
26 | delayTE2 = TE/2 - rf_ref.shape_dur/2 - rf_ref.ringdownTime - adcDur / 2 ;
27 | assert(delayTE2>mr.calcDuration(g_sp));
28 |
29 | adc = mr.makeAdc(Nx,'Duration',adcDur, 'system', system, 'delay', delayTE2);
30 |
31 | delayTR=TR-mr.calcDuration(rf_ex)-delayTE1-mr.calcDuration(rf_ref);
32 |
33 | assert(delayTE1>=0);
34 | assert(delayTE2>=0);
35 | assert(delayTR>=0);
36 |
37 | % Loop over repetitions and define sequence blocks
38 | for i=1:Nrep
39 | seq.addBlock(rf_ex);
40 | seq.addBlock(mr.makeDelay(delayTE1));
41 | seq.addBlock(rf_ref,g_sp);
42 | seq.addBlock(adc,g_sp,mr.makeDelay(delayTR));
43 | end
44 |
45 | seq.plot();
46 |
47 | % check whether the timing of the sequence is compatible with the scanner
48 | [ok, error_report]=seq.checkTiming;
49 |
50 | if (ok)
51 | fprintf('Timing check passed successfully\n');
52 | else
53 | fprintf('Timing check failed! Error listing follows:\n');
54 | fprintf([error_report{:}]);
55 | fprintf('\n');
56 | end
57 |
58 | seq.setDefinition('Name', 'se-sp');
59 | seq.write('se-sp.seq') % Write to pulseq file
60 | %seq.install('siemens'); % copy to scanner
61 |
62 | % calculate k-space but only use it to check timing
63 | [ktraj_adc, t_adc, ktraj, t_ktraj, t_excitation, t_refocusing] = seq.calculateKspacePP();
64 |
65 | assert(abs(t_refocusing-t_excitation-TE/2)<1e-6); % check that the refocusing happens at the 1/2 of TE
66 | assert(abs(t_adc(Nx/2)-t_excitation-TE)=0);
79 | % we start the ADC object right away after the spoiler
80 | adc = mr.makeAdc(Nx,'Duration',adcDur, 'system', system);
81 |
82 | % delayTR=TR-mr.calcDuration(g_ex)-mr.calcDuration(g_refC1)-delayTE1-delayTE2-mr.calcDuration(g_refC2)-mr.calcDuration(adc);
83 | delayTR=TR-max(mr.calcDuration(g_ex), mr.calcDuration(rf_ex))-mr.calcDuration(g_refC1)-delayTE1-delayTE2-mr.calcDuration(g_refC2)-mr.calcDuration(adc);
84 | assert(delayTR>=0);
85 |
86 | %% Loop over repetitions and define sequence blocks
87 | for i=(1-Ndummy):Nrep
88 | seq.addBlock(rf_ex,g_ex);
89 | seq.addBlock(mr.makeDelay(delayTE1));
90 | seq.addBlock(rf_ref1,g_refC1,g_spAz,g_spAx);
91 | seq.addBlock(mr.makeDelay(delayTE2));
92 | seq.addBlock(rf_ref2,g_refC2,g_spBy,g_spBx);
93 | if i>0
94 | seq.addBlock(adc);
95 | else
96 | seq.addBlock(mr.makeDelay(mr.calcDuration(adc)));
97 | end
98 | seq.addBlock(mr.makeDelay(delayTR));
99 | end
100 |
101 | %%
102 | seq.plot('showBlocks',true,'timeDisp','us','stacked',true);
103 |
104 | % check whether the timing of the sequence is compatible with the scanner
105 | [ok, error_report]=seq.checkTiming;
106 |
107 | if (ok)
108 | fprintf('Timing check passed successfully\n');
109 | else
110 | fprintf('Timing check failed! Error listing follows:\n');
111 | fprintf([error_report{:}]);
112 | fprintf('\n');
113 | end
114 |
115 | seq.setDefinition('FOV', voxel);
116 | seq.setDefinition('Name', 'press');
117 | seq.write('press.seq') % Write to pulseq file
118 |
119 | %% calculate k-space but only use it to check timing
120 | [ktraj_adc, t_adc, ktraj, t_ktraj, t_excitation, t_refocusing] = seq.calculateKspacePP('gradient_offset',[0 0 1000]);
121 |
122 | figure; plot(t_ktraj,ktraj);title('k-space components as functions of time'); grid on;
123 |
124 | %% demo paperPlot
125 |
126 | seq.paperPlot();
127 |
--------------------------------------------------------------------------------
/02_basic_gradient_echo/README.md:
--------------------------------------------------------------------------------
1 | # Pulseq tutorial "Basic GRE"
2 |
3 | Welcome to the "Basic GRE" tutorial! It was initially developed for the Pulseq software demonstration and hands-on session at the **ISMRM 2019** in Montreal.
4 |
5 | This tutorial demonstrates how to expand a basic Gradient-Echo (GRE)
6 | sequence to an advanced GRE sequence with a desired steady-state
7 | magnetisation evolution needed to generate T1 contrast. It contains six
8 | steps from ***s01\_GRE\_tutorial\_step0*** to
9 | ***s06\_GRE\_tutorial\_step5***. We recommend using Meld software to
10 | highlight the changes at each step. The slide deck entitled
11 | [02_basic_gradient_echo.pdf](./doc/02_basic_gradient_echo.pdf) shows the
12 | sequence diagrams of all steps and visualises the changes at each step.
13 |
14 | The transverse magnetisation may persist from cycle to cycle in a GRE
15 | sequence with short repetition times (e.g. shorter than \~3\*T2).
16 | *Spoiling* tries to approximate a situation that the steady-state
17 | magnetisation has no transverse components immediately before each RF
18 | pulse. Three methods may be used alone or in combination to spoil
19 | transverse magnetisation.
20 |
21 | 1. Long TR spoiling. When TR\>\>T2\*, the transverse magnetisation will
22 | naturally decay to zero by the end of the cycle. Thus, any GRE
23 | sequence using TR values of several hundred milliseconds or longer
24 | will be "naturally" spoiled.
25 |
26 | 2. Gradient spoiling. In this method, spoiling is performed by applying
27 | the slice-selective (and sometimes readout) gradients at the end of
28 | each cycle just before the next RF pulse. Several gradient spoiling
29 | concepts have been tried in the MR history, with the strength of the
30 | spoiler gradient remaining constant or varied linearly or
31 | semi-randomly from TR to TR. In the present example a constant
32 | spoiler is used, and an appropriate phantom with a sufficiently long
33 | T2 demonstrates that gradient spoiling does not work unless the
34 | spoiler is so strong that the intrinsic diffusion weighting would
35 | kill the signal.
36 |
37 | 3. It is also demonstrated that it is necessary to refocus the phase
38 | encoding moment at the end of the TR cycle to avoid artefacts.
39 |
40 | 4. RF-spoiling. Here the phase of the RF carrier is changed according
41 | to a predefined formula from TR to TR. Using a completely randomised
42 | pattern of phase changes is not ideal because unintended spin
43 | clustering may occur, and the degree of spoiling may change from one
44 | interval to the next. A superior method is to increment the phase
45 | quadratically using a recursive formula. RF spoiling is always
46 | combined with a moderate constant spoiling in read and slice
47 | directions and phase-encoding refocusing. For further details about
48 | spoiling, please go to
49 | .
50 |
51 | ***s01\_GRE\_tutorial\_step0***
52 |
53 | ***s01*** is a *basic* 2D slice-selective GRE sequence. Each TR of this
54 | sequence contains 5 blocks. The corresponding k-space is shown in Figure
55 | 1.
56 |
57 |
58 |
59 | **Figure** **1** K-space of s01\_GRE\_tutorial\_step0 sequence (6\*6
60 | encodes).
61 |
62 | ***s02\_GRE\_tutorial\_step1***
63 |
64 | ***s02*** is a 2D slice-selective GRE sequence with three spoiler
65 | gradients added in slice-selective, readout and phase-encoding
66 | direction. Its k-space is shown in Figure 2.
67 |
68 |
69 |
70 | **Figure** **2** K-space of s02\_GRE\_tutorial\_step1 sequence (6\*6
71 | encodes).
72 |
73 | ***s03\_GRE\_tutorial\_step2***
74 |
75 | ***s03*** is a 2D slice-selective GRE sequence with two spoiler
76 | gradients in slice-selective and readout directions and one rewinder
77 | gradient in the phase-encoding direction. It is built by altering the
78 | gyPost gradient in ***s01*** to rephase in the phase-encoding direction
79 | for subsequent phase-encoding steps. The k-space is shown in Figure 3.
80 |
81 |
82 |
83 | **Figure** **3** K-space of s03\_GRE\_tutorial\_step2 sequence (6\*6
84 | encodes).
85 |
86 | ***s04\_GRE\_tutorial\_step3***
87 |
88 | ***s04*** is built by adding RF-spoiling to ***s03***. The RF-spoiling
89 | is achieved by quasi-randomly varying the RF phase offset in the *i*^th^
90 | phase-encoding step,
91 | $\text{φ}_{\text{i}}\text{\ =\ mod}\left( \text{117\ *\ }\left( \text{i}^{\text{2}}\text{\ +\ }\text{i}\text{\ }\text{+\ 2} \right)\text{,\ 360} \right)\text{*}\frac{\text{π}}{\text{180}}$.
92 |
93 | ***s05\_GRE\_tutorial\_step4***
94 |
95 | In ***s05***, the receiver phase offset is set to follow the transmitter
96 | phase offset, $\text{φ}_{\text{i}}$, in the *i*^th^ phase-encoding step.
97 |
98 | ***s06\_GRE\_tutorial\_step5***
99 |
100 | ***s06*** adds some *dummy* scans (i.e. plays out multiple cycles of the
101 | sequence without recording a signal) to ***s05*** to establish (near)
102 | steady-state magnetisation in the GRE sequence prior to the beginning of the
103 | ADC recording. For more details about dummy scans, please go to
104 | .
105 |
106 | ## Quick links
107 |
108 | Pulseq Matlab repository:
109 | https://github.com/pulseq/pulseq
110 |
111 | ## Quick instructions
112 |
113 | The source code of the demo sequences and reconstruction scripts is the core of this repository. Please download the files to your computer and make them available to Matlab (e.g. by saving them in a subdirectory inside your Pulseq-Matlab installation and adding them to the Matlab's path). There are two sub-directories:
114 |
115 | * seq : contains example pulse sequences specifically prepared for this demo
116 | * recon : contains the reconstruction scripts tested with the above sequences
117 | * data : contains raw MR data in the Siemens TWIX format and the corresponding pulse sequences in the Pulseq format
118 |
119 | ## How to follow
120 |
121 | We strongly recommend using a text compare tool like *meld* (see this [Wikipedia page](https://en.wikipedia.org/wiki/Meld_(software)) and compare sequences from subsequent steps to visualise the respective steps.
122 |
123 | ## Further links
124 |
125 | Check out the main *Pulseq* repository at https://github.com/pulseq/pulseq and familarising yourself with the code, example sequences, and reconstruction scripts (see
126 | [pulseq/matlab/demoSeq](https://github.com/pulseq/pulseq/tree/master/matlab/demoSeq) and [pulseq/matlab/demoRecon](https://github.com/pulseq/pulseq/tree/master/matlab/demoRecon)). If you already use Pulseq, consider updating to the current version.
127 |
--------------------------------------------------------------------------------
/02_basic_gradient_echo/data/01_GRE_example.dat:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pulseq/tutorials/651dcc7bb46353ceb05783acd39c603abca13ca3/02_basic_gradient_echo/data/01_GRE_example.dat
--------------------------------------------------------------------------------
/02_basic_gradient_echo/data/02_GRE_noSpoil_noRephase.dat:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pulseq/tutorials/651dcc7bb46353ceb05783acd39c603abca13ca3/02_basic_gradient_echo/data/02_GRE_noSpoil_noRephase.dat
--------------------------------------------------------------------------------
/02_basic_gradient_echo/data/03_GRE_withSpoil_noRephase.dat:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pulseq/tutorials/651dcc7bb46353ceb05783acd39c603abca13ca3/02_basic_gradient_echo/data/03_GRE_withSpoil_noRephase.dat
--------------------------------------------------------------------------------
/02_basic_gradient_echo/data/04_GRE_xSpoil_yRephase.dat:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pulseq/tutorials/651dcc7bb46353ceb05783acd39c603abca13ca3/02_basic_gradient_echo/data/04_GRE_xSpoil_yRephase.dat
--------------------------------------------------------------------------------
/02_basic_gradient_echo/data/05_GRE_xSpoil_yRephase_RFspoil.dat:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pulseq/tutorials/651dcc7bb46353ceb05783acd39c603abca13ca3/02_basic_gradient_echo/data/05_GRE_xSpoil_yRephase_RFspoil.dat
--------------------------------------------------------------------------------
/02_basic_gradient_echo/data/06_GRE_xSpoil_yRephase_RFspoilADC.dat:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pulseq/tutorials/651dcc7bb46353ceb05783acd39c603abca13ca3/02_basic_gradient_echo/data/06_GRE_xSpoil_yRephase_RFspoilADC.dat
--------------------------------------------------------------------------------
/02_basic_gradient_echo/data/07_GRE_xSpoil_yRephase_RFspoilADC_dummy.dat:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pulseq/tutorials/651dcc7bb46353ceb05783acd39c603abca13ca3/02_basic_gradient_echo/data/07_GRE_xSpoil_yRephase_RFspoilADC_dummy.dat
--------------------------------------------------------------------------------
/02_basic_gradient_echo/doc/02_basic_gradient_echo.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pulseq/tutorials/651dcc7bb46353ceb05783acd39c603abca13ca3/02_basic_gradient_echo/doc/02_basic_gradient_echo.pdf
--------------------------------------------------------------------------------
/02_basic_gradient_echo/doc/02_basic_gradient_echo.pptx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pulseq/tutorials/651dcc7bb46353ceb05783acd39c603abca13ca3/02_basic_gradient_echo/doc/02_basic_gradient_echo.pptx
--------------------------------------------------------------------------------
/02_basic_gradient_echo/doc/Fig_1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pulseq/tutorials/651dcc7bb46353ceb05783acd39c603abca13ca3/02_basic_gradient_echo/doc/Fig_1.png
--------------------------------------------------------------------------------
/02_basic_gradient_echo/doc/Fig_2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pulseq/tutorials/651dcc7bb46353ceb05783acd39c603abca13ca3/02_basic_gradient_echo/doc/Fig_2.png
--------------------------------------------------------------------------------
/02_basic_gradient_echo/doc/Fig_3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pulseq/tutorials/651dcc7bb46353ceb05783acd39c603abca13ca3/02_basic_gradient_echo/doc/Fig_3.png
--------------------------------------------------------------------------------
/02_basic_gradient_echo/recon/r01_2DFFT.m:
--------------------------------------------------------------------------------
1 | % Reconstruction of 2D Cartesian Pulseq data
2 | % provides an example on how data reordering can be detected from the MR
3 | % sequence with almost no additional prior knowledge
4 | %
5 | % needs mapVBVD in the path
6 |
7 | %% Load data sorted by name
8 | path='../data'; % directory to be scanned for data files
9 | nF=1; % the number of the file / data set to load
10 |
11 | pattern='*.dat';
12 | D=dir([path filesep pattern]);
13 | [~,I]=sort(string({D(:).name}));
14 | data_file_path=[path filesep D(I(nF)).name];
15 |
16 | %% Load the latest file from a dir
17 | % path='../IceNIH_RawSend/'; % directory to be scanned for data files
18 | % %path='~/Dropbox/shared/data/siemens/';
19 | % pattern='*.dat';
20 | %
21 | % D=dir([path pattern]);
22 | % [~,I]=sort([D(:).datenum]);
23 | % %
24 | % data_file_path=[path D(I(end)).name]; % use end-1 to reconstruct the second-last data set, etc.
25 | % %data_file_path='../interpreters/siemens/data_example/gre_example.dat'
26 | %%
27 | twix_obj = mapVBVD(data_file_path);
28 |
29 | %% Load sequence from file (optional, you may still have it in memory)
30 |
31 | seq_file_path = [data_file_path(1:end-3) 'seq'];
32 |
33 | [~,out_name,~] = fileparts(seq_file_path);
34 |
35 | seq = mr.Sequence(); % Create a new sequence object
36 | seq.read(seq_file_path,'detectRFuse');
37 |
38 | %TR = 20e-3;
39 | %seq.plot('timeRange', [0 2*TR]);
40 | %print( '-r150', '-dpng', [out_name '_seq.png']);
41 |
42 | [ktraj_adc, t_adc, ktraj, t_ktraj, t_excitation, t_refocusing] = seq.calculateKspacePP();
43 | figure; plot(ktraj(1,:),ktraj(2,:),'b',...
44 | ktraj_adc(1,:),ktraj_adc(2,:),'r.'); % a 2D plot
45 | axis('equal');
46 | title('k-space');
47 | xlabel('kx / m^-^1');
48 | ylabel('kx / m^-^1');
49 | print( '-r150', '-dpng', [out_name '_kspace.png']);
50 |
51 | %% Analyze the trajectory data (ktraj_adc)
52 | k_extent=max(abs(ktraj_adc),[],2);
53 | k_scale=max(k_extent);
54 | k_threshold=k_scale/5000;
55 |
56 | % detect unused dimensions and delete them
57 | if any(k_extentk_threshold)=NaN;
69 | dk_all_cnt=sum(isfinite(dk_all),2);
70 | dk_all(~isfinite(dk_all))=0;
71 | dk=sum(dk_all,2)./dk_all_cnt;
72 | [~,k0_ind]=min(sum(ktraj_adc.^2,1));
73 | kindex=round((ktraj_adc-ktraj_adc(:,k0_ind*ones(1,size(ktraj_adc,2))))./dk(:,ones(1,size(ktraj_adc,2))));
74 | kindex_min=min(kindex,[],2);
75 | kindex_mat=kindex-kindex_min(:,ones(1,size(ktraj_adc,2)))+1;
76 | kindex_end=max(kindex_mat,[],2);
77 | sampler=zeros(kindex_end');
78 | repeat=zeros(1,size(ktraj_adc,2));
79 | for i=1:size(kindex_mat,2)
80 | if (size(kindex_mat,1)==3)
81 | ind=sub2ind(kindex_end,kindex_mat(1,i),kindex_mat(2,i),kindex_mat(3,i));
82 | else
83 | ind=sub2ind(kindex_end,kindex_mat(1,i),kindex_mat(2,i));
84 | end
85 | repeat(i)=sampler(ind);
86 | sampler(ind)=repeat(i)+1;
87 | end
88 | if (max(repeat(:))>0)
89 | kindex=[kindex;(repeat+1)];
90 | kindex_mat=[kindex_mat;(repeat+1)];
91 | kindex_end=max(kindex_mat,[],2);
92 | end
93 | %figure; plot(kindex(1,:),kindex(2,:),'.-');
94 |
95 | %% sort in the k-space data
96 | if iscell(twix_obj)
97 | data_unsorted = twix_obj{end}.image.unsorted();
98 | else
99 | data_unsorted = twix_obj.image.unsorted();
100 | end
101 |
102 | % the incoming data order is [kx coils acquisitions]
103 | data_coils_last = permute(data_unsorted, [1, 3, 2]);
104 | nCoils = size(data_coils_last, 3);
105 |
106 | data_coils_last=reshape(data_coils_last, [size(data_coils_last,1)*size(data_coils_last,2), nCoils]);
107 |
108 | data=zeros([kindex_end' nCoils]);
109 | if (size(kindex,1)==3)
110 | for i=1:size(kindex,2)
111 | data(kindex_mat(1,i),kindex_mat(2,i),kindex_mat(3,i),:)=data_coils_last(i,:);
112 | end
113 | else
114 | for i=1:size(kindex,2)
115 | data(kindex_mat(1,i),kindex_mat(2,i),:)=data_coils_last(i,:);
116 | end
117 | end
118 |
119 | if size(kindex,1)==3
120 | nImages=size(data,3);
121 | else
122 | nImages=1;
123 | data=reshape(data, [size(data,1) size(data,2) 1 size(data,3)]); % we need a dummy images/slices dimension
124 | end
125 |
126 | %figure; imab(data);
127 |
128 | %% Reconstruct coil images
129 |
130 | images = zeros(size(data));
131 | %figure;
132 |
133 | for ii = 1:nCoils
134 | %images(:,:,:,ii) = fliplr(rot90(fftshift(fft2(fftshift(data(:,:,:,ii))))));
135 | images(:,:,:,ii) = fftshift(fft2(fftshift(data(end:-1:1,:,:,ii))));
136 | %subplot(2,2,ii);
137 | %imshow(abs(images(:,:,ii)), []);
138 | %title(['RF Coil ' num2str(ii)]);
139 | %for ni = 1:nImages
140 | %tmp = abs(images(:,:,ni,ii));
141 | %tmp = tmp./max(tmp(:));
142 | %imwrite(tmp, ['img_coil_' num2str(ii) '.png'])
143 | %end
144 | end
145 |
146 | % Phase images (possibly channel-by-channel and echo-by-echo)
147 | %figure;imab(angle(images));colormap('jet');
148 | %figure;imab(abs(images));colormap('gray');
149 |
150 | %% Image display with optional sum of squares combination
151 | figure;
152 | if nCoils>1
153 | sos=abs(sum(images.*conj(images),ndims(images))).^(1/2);
154 | sos=sos./max(sos(:));
155 | imab(sos);
156 | imwrite(rot90(sos), [out_name '_img_combined.png']);
157 | else
158 | imab(abs(images));
159 | imwrite(rot90(abs(images)./max(abs(images(:)))), [out_name '_img.png']);
160 | end
161 | colormap('gray');
162 |
163 | %% reconstruct field map (optional)
164 |
165 | if size(images,3)==2
166 | cmplx_diff=images(:,:,2,:).*conj(images(:,:,1,:));
167 | phase_diff_image=angle(sum(cmplx_diff,4));
168 | figure;
169 | imab(phase_diff_image);colormap('jet');
170 | end
171 |
172 | %% gif movie export
173 |
174 | scale=1.2;
175 | filename='fftReconMovie';
176 | fps=5;
177 |
178 | if size(images,3)>4
179 |
180 | clm=gray(256);
181 |
182 | if nCoils>1
183 | imex=sos;
184 | else
185 | imex=abs(images);
186 | imex=imex/max(imex(:));
187 | end
188 |
189 | imex=permute(imex(:,end:-1:1,:),[2,1,3]);
190 |
191 | imind=uint8(scale*imex*(size(clm,1)-1)+1);
192 | imwrite(imind(:,:,1),clm, [filename, '.gif'],'DelayTime',1/fps,'Loopcount',inf);
193 | for i=2:size(imind,3),
194 | imwrite(imind(:,:,i),clm, [filename, '.gif'],'DelayTime',1/fps, 'WriteMode','append');
195 | end
196 |
197 | end
198 |
--------------------------------------------------------------------------------
/02_basic_gradient_echo/seq/s01_GRE_tutorial_step0.m:
--------------------------------------------------------------------------------
1 | % Define FOV and resolution
2 | fov = 256e-3;
3 | sliceThickness = 5e-3;
4 | Nx = 256;
5 | Ny = Nx;
6 |
7 | % Define sequence parameters
8 | TE = 8e-3;
9 | TR = 22e-3;
10 | alpha=30;
11 |
12 | % set system limits
13 | sys = mr.opts('MaxGrad',12,'GradUnit','mT/m',...
14 | 'MaxSlew',100,'SlewUnit','T/m/s',...
15 | 'rfRingdownTime', 20e-6, 'rfDeadtime', 100e-6);
16 |
17 | % Create a new sequence object
18 | seq=mr.Sequence(sys);
19 |
20 | % Create slice selective alpha-pulse and corresponding gradients
21 | [rf, gz, gzReph] = mr.makeSincPulse(alpha*pi/180, 'Duration', 4e-3,...
22 | 'SliceThickness', sliceThickness, 'apodization', 0.5,'timeBwProduct', 4, ...
23 | 'system' ,sys, 'use', 'excitation');
24 |
25 | % Define other gradients and ADC events
26 | deltak = 1/fov; % Pulseq toolbox defaults to k-space units of m^-1
27 | gx = mr.makeTrapezoid('x', 'FlatArea', Nx*deltak, 'FlatTime', 6.4e-3);
28 | adc = mr.makeAdc(Nx, 'Duration', gx.flatTime, 'Delay', gx.riseTime);
29 | gxPre = mr.makeTrapezoid('x', 'Area', -gx.area/2, 'Duration', 2e-3);
30 | phaseAreas = ((0:Ny-1)-Ny/2)*deltak;
31 |
32 |
33 | % Calculate timing
34 | delayTE = round((TE - mr.calcDuration(gxPre) - mr.calcDuration(gz)/2 ...
35 | - mr.calcDuration(gx)/2)/seq.gradRasterTime)*seq.gradRasterTime;
36 | delayTR = round((TR - mr.calcDuration(gxPre) - mr.calcDuration(gz) ...
37 | - mr.calcDuration(gx) - delayTE)/seq.gradRasterTime)*seq.gradRasterTime;
38 |
39 | % Loop over phase encodes and define sequence blocks
40 | for i=1:Ny
41 | seq.addBlock(rf, gz);
42 | gyPre = mr.makeTrapezoid('y', 'Area', phaseAreas(i), 'Duration', 2e-3);
43 | seq.addBlock(gxPre, gyPre, gzReph);
44 | seq.addBlock(mr.makeDelay(delayTE));
45 | seq.addBlock(gx, adc);
46 | seq.addBlock(mr.makeDelay(delayTR));
47 | end
48 |
49 | % check whether the timing of the sequence is correct
50 | [ok, error_report]=seq.checkTiming;
51 |
52 | if (ok)
53 | fprintf('Timing check passed successfully\n');
54 | else
55 | fprintf('Timing check failed! Error listing follows:\n');
56 | fprintf([error_report{:}]);
57 | fprintf('\n');
58 | end
59 |
60 | % export definitions
61 | seq.setDefinition('FOV', [fov fov sliceThickness]);
62 | seq.setDefinition('Name', 'DEMO_gre0');
63 |
64 | seq.write(['DEMO_gre0.seq']) % Write to pulseq file
65 |
66 | seq.plot('timeRange', [0 2*TR])
67 |
68 | % do not run the rest of the script automatically
69 | return
70 |
71 | %% plot gradients to check for gaps and optimality of the timing
72 | wave_data=seq.waveforms_and_times();
73 | % plot the entire gradient shape
74 | figure; plot(wave_data{1}(1,:),wave_data{1}(2,:)); xlabel('time /s'); ylabel('gradient /(Hz/m)');
75 | hold on; plot(wave_data{2}(1,:),wave_data{2}(2,:));
76 | plot(wave_data{3}(1,:),wave_data{3}(2,:));
77 | legend('G_x', 'G_y', 'G_z');
78 |
79 | %% calculate k-space trajectory
80 |
81 | [ktraj_adc, t_adc, ktraj, t_ktraj, t_excitation, t_refocusing] = seq.calculateKspacePP();
82 |
83 | % plot k-spaces
84 | figure; plot(t_ktraj, ktraj'); % plot the entire k-space trajectory
85 | hold on; plot(t_adc,ktraj_adc(1,:),'.'); % and sampling points on the kx-axis
86 | title('k-space vector components as functions of time');
87 | legend('k_x', 'k_y', 'k_z');
88 |
89 | figure; plot(ktraj(1,:),ktraj(2,:),'b'); % a 2D plot
90 | hold on;plot(ktraj_adc(1,:),ktraj_adc(2,:),'r.'); % plot the sampling points
91 | axis('equal'); % enforce aspect ratio for the correct trajectory display
92 | title('k-space trajectory (k_x/k_y)');
93 |
94 | %%
95 |
96 | % [ktraj_adc, ktraj, t_excitation, t_refocusing, t_adc] = seq.calculateKspace();
97 |
98 | % plot k-spaces
99 | % time_axis=(1:(size(ktraj,2)))*sys.gradRasterTime;
100 | % figure; plot(time_axis, ktraj'); % plot the entire k-space trajectory
101 | % hold; plot(t_adc,ktraj_adc(1,:),'.'); % and sampling points on the kx-axis
102 | % figure; plot(ktraj(1,:),ktraj(2,:),'b'); % a 2D plot
103 | % axis('equal'); % enforce aspect ratio for the correct trajectory display
104 | % hold;plot(ktraj_adc(1,:),ktraj_adc(2,:),'r.'); % plot the sampling points
105 |
106 | %% very optional step, slow but useful for testing during development e.g. for the real TE, TR or for staying within slewrate limits
107 | rep = seq.testReport;
108 | fprintf([rep{:}]);
109 |
110 | %% listen to the sequence
111 | seq.sound();
112 |
--------------------------------------------------------------------------------
/02_basic_gradient_echo/seq/s02_GRE_tutorial_step1.m:
--------------------------------------------------------------------------------
1 | % Define FOV and resolution
2 | fov = 256e-3;
3 | sliceThickness = 5e-3;
4 | Nx = 256;
5 | Ny = Nx;
6 |
7 | % Define sequence parameters
8 | TE = 8e-3;
9 | TR = 22e-3;
10 | alpha=30;
11 |
12 | % set system limits
13 | sys = mr.opts('MaxGrad',12,'GradUnit','mT/m',...
14 | 'MaxSlew',100,'SlewUnit','T/m/s',...
15 | 'rfRingdownTime', 20e-6, 'rfDeadtime', 100e-6);
16 |
17 | % Create a new sequence object
18 | seq=mr.Sequence(sys);
19 |
20 | % Create slice selective alpha-pulse and corresponding gradients
21 | [rf, gz, gzReph] = mr.makeSincPulse(alpha*pi/180, 'Duration', 4e-3,...
22 | 'SliceThickness', sliceThickness, 'apodization', 0.5,'timeBwProduct', 4, ...
23 | 'system' ,sys, 'use', 'excitation');
24 |
25 | % Define other gradients and ADC events
26 | deltak = 1/fov; % Pulseq toolbox defaults to k-space units of m^-1
27 | gx = mr.makeTrapezoid('x', 'FlatArea', Nx*deltak, 'FlatTime', 6.4e-3);
28 | adc = mr.makeAdc(Nx, 'Duration', gx.flatTime, 'Delay', gx.riseTime);
29 | gxPre = mr.makeTrapezoid('x', 'Area', -gx.area/2, 'Duration', 2e-3);
30 | phaseAreas = ((0:Ny-1)-Ny/2)*deltak;
31 |
32 |
33 | % Calculate timing
34 | delayTE = round((TE - mr.calcDuration(gxPre) - mr.calcDuration(gz)/2 ...
35 | - mr.calcDuration(gx)/2)/seq.gradRasterTime)*seq.gradRasterTime;
36 | delayTR = round((TR - mr.calcDuration(gxPre) - mr.calcDuration(gz) ...
37 | - mr.calcDuration(gx) - delayTE)/seq.gradRasterTime)*seq.gradRasterTime;
38 |
39 | spoilArea=4*gx.area(); % 4 "looks" good
40 | % Add spoilers in read, refocus in phase and spoiler in slice
41 | gxPost = mr.makeTrapezoid('x', 'Area', spoilArea, 'system', sys); % we pass 'system' here to calculate shortest time gradient
42 | gyPost = mr.makeTrapezoid('y', 'Area', spoilArea, 'system', sys);
43 | gzPost = mr.makeTrapezoid('z', 'Area', spoilArea, 'system', sys);
44 |
45 | delayTR = delayTR - mr.calcDuration(gxPost, gyPost, gzPost);
46 |
47 | % Loop over phase encodes and define sequence blocks
48 | for i=1:Ny
49 | seq.addBlock(rf, gz);
50 | gyPre = mr.makeTrapezoid('y', 'Area', phaseAreas(i), 'Duration', 2e-3);
51 | seq.addBlock(gxPre, gyPre, gzReph);
52 | seq.addBlock(mr.makeDelay(delayTE));
53 | seq.addBlock(gx, adc);
54 | % Add spoilers in read and slice and may be in phase
55 | seq.addBlock(gxPost, gyPost, gzPost);
56 | seq.addBlock(mr.makeDelay(delayTR));
57 | end
58 |
59 | % check whether the timing of the sequence is correct
60 | [ok, error_report]=seq.checkTiming;
61 |
62 | if (ok)
63 | fprintf('Timing check passed successfully\n');
64 | else
65 | fprintf('Timing check failed! Error listing follows:\n');
66 | fprintf([error_report{:}]);
67 | fprintf('\n');
68 | end
69 |
70 | % export definitions
71 | seq.setDefinition('FOV', [fov fov sliceThickness]);
72 | seq.setDefinition('Name', 'DEMO_gre1');
73 |
74 | seq.write(['DEMO_gre1.seq']) % Write to pulseq file
75 |
76 | seq.plot('timeRange', [0 2*TR])
77 |
78 | % do not run the rest of the script automatically
79 | return
80 |
81 | %% plot gradients to check for gaps and optimality of the timing
82 | wave_data=seq.waveforms_and_times();
83 | % plot the entire gradient shape
84 | figure; plot(wave_data{1}(1,:),wave_data{1}(2,:)); xlabel('time /s'); ylabel('gradient /(Hz/m)');
85 | hold on; plot(wave_data{2}(1,:),wave_data{2}(2,:));
86 | plot(wave_data{3}(1,:),wave_data{3}(2,:));
87 | legend('G_x', 'G_y', 'G_z');
88 |
89 | %% calculate k-space trajectory
90 |
91 | [ktraj_adc, t_adc, ktraj, t_ktraj, t_excitation, t_refocusing] = seq.calculateKspacePP();
92 |
93 | % plot k-spaces
94 | figure; plot(t_ktraj, ktraj'); % plot the entire k-space trajectory
95 | hold on; plot(t_adc,ktraj_adc(1,:),'.'); % and sampling points on the kx-axis
96 | title('k-space vector components as functions of time');
97 | legend('k_x', 'k_y', 'k_z');
98 |
99 | figure; plot(ktraj(1,:),ktraj(2,:),'b'); % a 2D plot
100 | hold on;plot(ktraj_adc(1,:),ktraj_adc(2,:),'r.'); % plot the sampling points
101 | axis('equal'); % enforce aspect ratio for the correct trajectory display
102 | title('k-space trajectory (k_x/k_y)');
103 |
104 | %%
105 |
106 | % [ktraj_adc, ktraj, t_excitation, t_refocusing, t_adc] = seq.calculateKspace();
107 |
108 | % plot k-spaces
109 | % time_axis=(1:(size(ktraj,2)))*sys.gradRasterTime;
110 | % figure; plot(time_axis, ktraj'); % plot the entire k-space trajectory
111 | % hold; plot(t_adc,ktraj_adc(1,:),'.'); % and sampling points on the kx-axis
112 | % figure; plot(ktraj(1,:),ktraj(2,:),'b'); % a 2D plot
113 | % axis('equal'); % enforce aspect ratio for the correct trajectory display
114 | % hold;plot(ktraj_adc(1,:),ktraj_adc(2,:),'r.'); % plot the sampling points
115 |
116 | %% very optional step, slow but useful for testing during development e.g. for the real TE, TR or for staying within slewrate limits
117 | rep = seq.testReport;
118 | fprintf([rep{:}]);
119 |
120 | %% listen to the sequence
121 | seq.sound();
122 |
--------------------------------------------------------------------------------
/02_basic_gradient_echo/seq/s03_GRE_tutorial_step2.m:
--------------------------------------------------------------------------------
1 | % Define FOV and resolution
2 | fov = 256e-3;
3 | sliceThickness = 5e-3;
4 | Nx = 256;
5 | Ny = Nx;
6 |
7 | % Define sequence parameters
8 | TE = 8e-3;
9 | TR = 22e-3;
10 | alpha=30;
11 |
12 | % set system limits
13 | sys = mr.opts('MaxGrad',12,'GradUnit','mT/m',...
14 | 'MaxSlew',100,'SlewUnit','T/m/s',...
15 | 'rfRingdownTime', 20e-6, 'rfDeadtime', 100e-6);
16 |
17 | % Create a new sequence object
18 | seq=mr.Sequence(sys);
19 |
20 | % Create slice selective alpha-pulse and corresponding gradients
21 | [rf, gz, gzReph] = mr.makeSincPulse(alpha*pi/180, 'Duration', 4e-3,...
22 | 'SliceThickness', sliceThickness, 'apodization', 0.5,'timeBwProduct', 4, ...
23 | 'system' ,sys, 'use', 'excitation');
24 |
25 | % Define other gradients and ADC events
26 | deltak = 1/fov; % Pulseq toolbox defaults to k-space units of m^-1
27 | gx = mr.makeTrapezoid('x', 'FlatArea', Nx*deltak, 'FlatTime', 6.4e-3);
28 | adc = mr.makeAdc(Nx, 'Duration', gx.flatTime, 'Delay', gx.riseTime);
29 | gxPre = mr.makeTrapezoid('x', 'Area', -gx.area/2, 'Duration', 2e-3);
30 | phaseAreas = ((0:Ny-1)-Ny/2)*deltak;
31 |
32 |
33 | % Calculate timing
34 | delayTE = round((TE - mr.calcDuration(gxPre) - mr.calcDuration(gz)/2 ...
35 | - mr.calcDuration(gx)/2)/seq.gradRasterTime)*seq.gradRasterTime;
36 | delayTR = round((TR - mr.calcDuration(gxPre) - mr.calcDuration(gz) ...
37 | - mr.calcDuration(gx) - delayTE)/seq.gradRasterTime)*seq.gradRasterTime;
38 |
39 | spoilArea=4*gx.area(); % 4 "looks" good
40 | % Add spoilers in read, refocus in phase and spoiler in slice
41 | gxPost = mr.makeTrapezoid('x', 'Area', spoilArea, 'system', sys); % we pass 'system' here to calculate shortest time gradient
42 | gyPost = mr.makeTrapezoid('y', 'Area', -max(phaseAreas(:)), 'Duration', 2e-3);
43 | gzPost = mr.makeTrapezoid('z', 'Area', spoilArea, 'system', sys);
44 |
45 | delayTR = delayTR - mr.calcDuration(gxPost, gyPost, gzPost);
46 |
47 | % Loop over phase encodes and define sequence blocks
48 | for i=1:Ny
49 | seq.addBlock(rf, gz);
50 | gyPre = mr.makeTrapezoid('y', 'Area', phaseAreas(i), 'Duration', 2e-3);
51 | seq.addBlock(gxPre, gyPre, gzReph);
52 | seq.addBlock(mr.makeDelay(delayTE));
53 | seq.addBlock(gx, adc);
54 | gyPost = mr.makeTrapezoid('y', 'Area', -gyPre.area, 'Duration', 2e-3);
55 | % Add spoilers in read and slice and may be in phase
56 | seq.addBlock(gxPost, gyPost, gzPost);
57 | seq.addBlock(mr.makeDelay(delayTR));
58 | end
59 |
60 | % check whether the timing of the sequence is correct
61 | [ok, error_report]=seq.checkTiming;
62 |
63 | if (ok)
64 | fprintf('Timing check passed successfully\n');
65 | else
66 | fprintf('Timing check failed! Error listing follows:\n');
67 | fprintf([error_report{:}]);
68 | fprintf('\n');
69 | end
70 |
71 | % export definitions
72 | seq.setDefinition('FOV', [fov fov sliceThickness]);
73 | seq.setDefinition('Name', 'DEMO_gre2');
74 |
75 | seq.write(['DEMO_gre2.seq']) % Write to pulseq file
76 |
77 | seq.plot('timeRange', [0 2*TR])
78 |
79 | % do not run the rest of the script automatically
80 | return
81 |
82 | %% plot gradients to check for gaps and optimality of the timing
83 | wave_data=seq.waveforms_and_times();
84 | % plot the entire gradient shape
85 | figure; plot(wave_data{1}(1,:),wave_data{1}(2,:)); xlabel('time /s'); ylabel('gradient /(Hz/m)');
86 | hold on; plot(wave_data{2}(1,:),wave_data{2}(2,:));
87 | plot(wave_data{3}(1,:),wave_data{3}(2,:));
88 | legend('G_x', 'G_y', 'G_z');
89 |
90 | %% calculate k-space trajectory
91 |
92 | [ktraj_adc, t_adc, ktraj, t_ktraj, t_excitation, t_refocusing] = seq.calculateKspacePP();
93 |
94 | % plot k-spaces
95 | figure; plot(t_ktraj, ktraj'); % plot the entire k-space trajectory
96 | hold on; plot(t_adc,ktraj_adc(1,:),'.'); % and sampling points on the kx-axis
97 | title('k-space vector components as functions of time');
98 | legend('k_x', 'k_y', 'k_z');
99 |
100 | figure; plot(ktraj(1,:),ktraj(2,:),'b'); % a 2D plot
101 | hold on;plot(ktraj_adc(1,:),ktraj_adc(2,:),'r.'); % plot the sampling points
102 | axis('equal'); % enforce aspect ratio for the correct trajectory display
103 | title('k-space trajectory (k_x/k_y)');
104 |
105 | %%
106 |
107 | % [ktraj_adc, ktraj, t_excitation, t_refocusing, t_adc] = seq.calculateKspace();
108 |
109 | % plot k-spaces
110 | % time_axis=(1:(size(ktraj,2)))*sys.gradRasterTime;
111 | % figure; plot(time_axis, ktraj'); % plot the entire k-space trajectory
112 | % hold; plot(t_adc,ktraj_adc(1,:),'.'); % and sampling points on the kx-axis
113 | % figure; plot(ktraj(1,:),ktraj(2,:),'b'); % a 2D plot
114 | % axis('equal'); % enforce aspect ratio for the correct trajectory display
115 | % hold;plot(ktraj_adc(1,:),ktraj_adc(2,:),'r.'); % plot the sampling points
116 |
117 | %% very optional step, slow but useful for testing during development e.g. for the real TE, TR or for staying within slewrate limits
118 | rep = seq.testReport;
119 | fprintf([rep{:}]);
120 |
121 | %% listen to the sequence
122 | seq.sound();
123 |
--------------------------------------------------------------------------------
/02_basic_gradient_echo/seq/s04_GRE_tutorial_step3.m:
--------------------------------------------------------------------------------
1 | % Define FOV and resolution
2 | fov = 256e-3;
3 | sliceThickness = 5e-3;
4 | Nx = 256;
5 | Ny = Nx;
6 |
7 | % Define sequence parameters
8 | TE = 8e-3;
9 | TR = 22e-3;
10 | alpha=30;
11 |
12 | % set system limits
13 | sys = mr.opts('MaxGrad',12,'GradUnit','mT/m',...
14 | 'MaxSlew',100,'SlewUnit','T/m/s',...
15 | 'rfRingdownTime', 20e-6, 'rfDeadtime', 100e-6);
16 |
17 | % Create a new sequence object
18 | seq=mr.Sequence(sys);
19 |
20 | % Create slice selective alpha-pulse and corresponding gradients
21 | [rf, gz, gzReph] = mr.makeSincPulse(alpha*pi/180, 'Duration', 4e-3,...
22 | 'SliceThickness', sliceThickness, 'apodization', 0.5,'timeBwProduct', 4, ...
23 | 'system' ,sys, 'use', 'excitation');
24 |
25 | % Define other gradients and ADC events
26 | deltak = 1/fov; % Pulseq toolbox defaults to k-space units of m^-1
27 | gx = mr.makeTrapezoid('x', 'FlatArea', Nx*deltak, 'FlatTime', 6.4e-3);
28 | adc = mr.makeAdc(Nx, 'Duration', gx.flatTime, 'Delay', gx.riseTime);
29 | gxPre = mr.makeTrapezoid('x', 'Area', -gx.area/2, 'Duration', 2e-3);
30 | phaseAreas = ((0:Ny-1)-Ny/2)*deltak;
31 |
32 |
33 | % Calculate timing
34 | delayTE = round((TE - mr.calcDuration(gxPre) - mr.calcDuration(gz)/2 ...
35 | - mr.calcDuration(gx)/2)/seq.gradRasterTime)*seq.gradRasterTime;
36 | delayTR = round((TR - mr.calcDuration(gxPre) - mr.calcDuration(gz) ...
37 | - mr.calcDuration(gx) - delayTE)/seq.gradRasterTime)*seq.gradRasterTime;
38 |
39 | spoilArea=4*gx.area(); % 4 "looks" good
40 | % Add spoilers in read, refocus in phase and spoiler in slice
41 | gxPost = mr.makeTrapezoid('x', 'Area', spoilArea, 'system', sys); % we pass 'system' here to calculate shortest time gradient
42 | gyPost = mr.makeTrapezoid('y', 'Area', -max(phaseAreas(:)), 'Duration', 2e-3);
43 | gzPost = mr.makeTrapezoid('z', 'Area', spoilArea, 'system', sys);
44 |
45 | delayTR = delayTR - mr.calcDuration(gxPost, gyPost, gzPost);
46 |
47 | % Loop over phase encodes and define sequence blocks
48 | for i=1:Ny
49 | % Vary RF phase quasi-randomly
50 | rand_phase = mod(117*(i^2 + i + 2), 360)*pi/180;
51 | [rf, gz] = mr.makeSincPulse(alpha*pi/180, 'Duration', 4e-3,...
52 | 'SliceThickness', 5e-3, ...
53 | 'apodization', 0.5, ...
54 | 'timeBwProduct', 4, ...
55 | 'system', sys, ...
56 | 'phaseOffset', rand_phase, ...
57 | 'use', 'excitation');
58 | seq.addBlock(rf, gz);
59 | gyPre = mr.makeTrapezoid('y', 'Area', phaseAreas(i), 'Duration', 2e-3);
60 | seq.addBlock(gxPre, gyPre, gzReph);
61 | seq.addBlock(mr.makeDelay(delayTE));
62 | seq.addBlock(gx, adc);
63 | gyPost = mr.makeTrapezoid('y', 'Area', -gyPre.area, 'Duration', 2e-3);
64 | % Add spoilers in read and slice and may be in phase
65 | seq.addBlock(gxPost, gyPost, gzPost);
66 | seq.addBlock(mr.makeDelay(delayTR));
67 | end
68 |
69 | % check whether the timing of the sequence is correct
70 | [ok, error_report]=seq.checkTiming;
71 |
72 | if (ok)
73 | fprintf('Timing check passed successfully\n');
74 | else
75 | fprintf('Timing check failed! Error listing follows:\n');
76 | fprintf([error_report{:}]);
77 | fprintf('\n');
78 | end
79 |
80 | % export definitions
81 | seq.setDefinition('FOV', [fov fov sliceThickness]);
82 | seq.setDefinition('Name', 'DEMO_gre3');
83 |
84 | seq.write(['DEMO_gre3.seq']) % Write to pulseq file
85 |
86 | seq.plot('timeRange', [0 2*TR])
87 |
88 | % do not run the rest of the script automatically
89 | return
90 |
91 | %% plot gradients to check for gaps and optimality of the timing
92 | wave_data=seq.waveforms_and_times();
93 | % plot the entire gradient shape
94 | figure; plot(wave_data{1}(1,:),wave_data{1}(2,:)); xlabel('time /s'); ylabel('gradient /(Hz/m)');
95 | hold on; plot(wave_data{2}(1,:),wave_data{2}(2,:));
96 | plot(wave_data{3}(1,:),wave_data{3}(2,:));
97 | legend('G_x', 'G_y', 'G_z');
98 |
99 | %% calculate k-space trajectory
100 |
101 | [ktraj_adc, t_adc, ktraj, t_ktraj, t_excitation, t_refocusing] = seq.calculateKspacePP();
102 |
103 | % plot k-spaces
104 | figure; plot(t_ktraj, ktraj'); % plot the entire k-space trajectory
105 | hold on; plot(t_adc,ktraj_adc(1,:),'.'); % and sampling points on the kx-axis
106 | title('k-space vector components as functions of time');
107 | legend('k_x', 'k_y', 'k_z');
108 |
109 | figure; plot(ktraj(1,:),ktraj(2,:),'b'); % a 2D plot
110 | hold on;plot(ktraj_adc(1,:),ktraj_adc(2,:),'r.'); % plot the sampling points
111 | axis('equal'); % enforce aspect ratio for the correct trajectory display
112 | title('k-space trajectory (k_x/k_y)');
113 |
114 | %%
115 |
116 | % [ktraj_adc, ktraj, t_excitation, t_refocusing, t_adc] = seq.calculateKspace();
117 |
118 | % plot k-spaces
119 | % time_axis=(1:(size(ktraj,2)))*sys.gradRasterTime;
120 | % figure; plot(time_axis, ktraj'); % plot the entire k-space trajectory
121 | % hold; plot(t_adc,ktraj_adc(1,:),'.'); % and sampling points on the kx-axis
122 | % figure; plot(ktraj(1,:),ktraj(2,:),'b'); % a 2D plot
123 | % axis('equal'); % enforce aspect ratio for the correct trajectory display
124 | % hold;plot(ktraj_adc(1,:),ktraj_adc(2,:),'r.'); % plot the sampling points
125 |
126 | %% very optional step, slow but useful for testing during development e.g. for the real TE, TR or for staying within slewrate limits
127 | rep = seq.testReport;
128 | fprintf([rep{:}]);
129 |
130 | %% listen to the sequence
131 | seq.sound();
132 |
--------------------------------------------------------------------------------
/02_basic_gradient_echo/seq/s05_GRE_tutorial_step4.m:
--------------------------------------------------------------------------------
1 | % Define FOV and resolution
2 | fov = 256e-3;
3 | sliceThickness = 5e-3;
4 | Nx = 256;
5 | Ny = Nx;
6 |
7 | % Define sequence parameters
8 | TE = 8e-3;
9 | TR = 22e-3;
10 | alpha=30;
11 |
12 | % set system limits
13 | sys = mr.opts('MaxGrad',12,'GradUnit','mT/m',...
14 | 'MaxSlew',100,'SlewUnit','T/m/s',...
15 | 'rfRingdownTime', 20e-6, 'rfDeadtime', 100e-6);
16 |
17 | % Create a new sequence object
18 | seq=mr.Sequence(sys);
19 |
20 | % Create slice selective alpha-pulse and corresponding gradients
21 | [rf, gz, gzReph] = mr.makeSincPulse(alpha*pi/180, 'Duration', 4e-3,...
22 | 'SliceThickness', sliceThickness, 'apodization', 0.5,'timeBwProduct', 4, ...
23 | 'system' ,sys, 'use', 'excitation');
24 |
25 | % Define other gradients and ADC events
26 | deltak = 1/fov; % Pulseq toolbox defaults to k-space units of m^-1
27 | gx = mr.makeTrapezoid('x', 'FlatArea', Nx*deltak, 'FlatTime', 6.4e-3);
28 | adc = mr.makeAdc(Nx, 'Duration', gx.flatTime, 'Delay', gx.riseTime);
29 | gxPre = mr.makeTrapezoid('x', 'Area', -gx.area/2, 'Duration', 2e-3);
30 | phaseAreas = ((0:Ny-1)-Ny/2)*deltak;
31 |
32 |
33 | % Calculate timing
34 | delayTE = round((TE - mr.calcDuration(gxPre) - mr.calcDuration(gz)/2 ...
35 | - mr.calcDuration(gx)/2)/seq.gradRasterTime)*seq.gradRasterTime;
36 | delayTR = round((TR - mr.calcDuration(gxPre) - mr.calcDuration(gz) ...
37 | - mr.calcDuration(gx) - delayTE)/seq.gradRasterTime)*seq.gradRasterTime;
38 |
39 | spoilArea=4*gx.area(); % 4 "looks" good
40 | % Add spoilers in read, refocus in phase and spoiler in slice
41 | gxPost = mr.makeTrapezoid('x', 'Area', spoilArea, 'system', sys); % we pass 'system' here to calculate shortest time gradient
42 | gyPost = mr.makeTrapezoid('y', 'Area', -max(phaseAreas(:)), 'Duration', 2e-3);
43 | gzPost = mr.makeTrapezoid('z', 'Area', spoilArea, 'system', sys);
44 |
45 | delayTR = delayTR - mr.calcDuration(gxPost, gyPost, gzPost);
46 |
47 | % Loop over phase encodes and define sequence blocks
48 | for i=1:Ny
49 | % Vary RF phase quasi-randomly
50 | rand_phase = mod(117*(i^2 + i + 2), 360)*pi/180;
51 | [rf, gz] = mr.makeSincPulse(alpha*pi/180, 'Duration', 4e-3,...
52 | 'SliceThickness', 5e-3, ...
53 | 'apodization', 0.5, ...
54 | 'timeBwProduct', 4, ...
55 | 'system', sys, ...
56 | 'phaseOffset', rand_phase, ...
57 | 'use', 'excitation');
58 | seq.addBlock(rf, gz);
59 | gyPre = mr.makeTrapezoid('y', 'Area', phaseAreas(i), 'Duration', 2e-3);
60 | seq.addBlock(gxPre, gyPre, gzReph);
61 | seq.addBlock(mr.makeDelay(delayTE));
62 | % Make receiver phase follow transmitter phase
63 | adc = mr.makeAdc(Nx, 'Duration', gx.flatTime,...
64 | 'Delay', gx.riseTime,...
65 | 'phaseOffset', rand_phase);
66 | seq.addBlock(gx, adc);
67 | gyPost = mr.makeTrapezoid('y', 'Area', -gyPre.area, 'Duration', 2e-3);
68 | % Add spoilers in read and slice and may be in phase
69 | seq.addBlock(gxPost, gyPost, gzPost);
70 | seq.addBlock(mr.makeDelay(delayTR));
71 | end
72 |
73 | % check whether the timing of the sequence is correct
74 | [ok, error_report]=seq.checkTiming;
75 |
76 | if (ok)
77 | fprintf('Timing check passed successfully\n');
78 | else
79 | fprintf('Timing check failed! Error listing follows:\n');
80 | fprintf([error_report{:}]);
81 | fprintf('\n');
82 | end
83 |
84 | % export definitions
85 | seq.setDefinition('FOV', [fov fov sliceThickness]);
86 | seq.setDefinition('Name', 'DEMO_gre4');
87 |
88 | seq.write(['DEMO_gre4.seq']) % Write to pulseq file
89 |
90 | seq.plot('timeRange', [0 2*TR])
91 |
92 | % do not run the rest of the script automatically
93 | return
94 |
95 | %% plot gradients to check for gaps and optimality of the timing
96 | wave_data=seq.waveforms_and_times();
97 | % plot the entire gradient shape
98 | figure; plot(wave_data{1}(1,:),wave_data{1}(2,:)); xlabel('time /s'); ylabel('gradient /(Hz/m)');
99 | hold on; plot(wave_data{2}(1,:),wave_data{2}(2,:));
100 | plot(wave_data{3}(1,:),wave_data{3}(2,:));
101 | legend('G_x', 'G_y', 'G_z');
102 |
103 | %% calculate k-space trajectory
104 |
105 | [ktraj_adc, t_adc, ktraj, t_ktraj, t_excitation, t_refocusing] = seq.calculateKspacePP();
106 |
107 | % plot k-spaces
108 | figure; plot(t_ktraj, ktraj'); % plot the entire k-space trajectory
109 | hold on; plot(t_adc,ktraj_adc(1,:),'.'); % and sampling points on the kx-axis
110 | title('k-space vector components as functions of time');
111 | legend('k_x', 'k_y', 'k_z');
112 |
113 | figure; plot(ktraj(1,:),ktraj(2,:),'b'); % a 2D plot
114 | hold on;plot(ktraj_adc(1,:),ktraj_adc(2,:),'r.'); % plot the sampling points
115 | axis('equal'); % enforce aspect ratio for the correct trajectory display
116 | title('k-space trajectory (k_x/k_y)');
117 |
118 | %%
119 |
120 | % [ktraj_adc, ktraj, t_excitation, t_refocusing, t_adc] = seq.calculateKspace();
121 |
122 | % plot k-spaces
123 | % time_axis=(1:(size(ktraj,2)))*sys.gradRasterTime;
124 | % figure; plot(time_axis, ktraj'); % plot the entire k-space trajectory
125 | % hold; plot(t_adc,ktraj_adc(1,:),'.'); % and sampling points on the kx-axis
126 | % figure; plot(ktraj(1,:),ktraj(2,:),'b'); % a 2D plot
127 | % axis('equal'); % enforce aspect ratio for the correct trajectory display
128 | % hold;plot(ktraj_adc(1,:),ktraj_adc(2,:),'r.'); % plot the sampling points
129 |
130 | %% very optional step, slow but useful for testing during development e.g. for the real TE, TR or for staying within slewrate limits
131 | rep = seq.testReport;
132 | fprintf([rep{:}]);
133 |
134 | %% listen to the sequence
135 | seq.sound();
136 |
--------------------------------------------------------------------------------
/02_basic_gradient_echo/seq/s06_GRE_tutorial_step5.m:
--------------------------------------------------------------------------------
1 | % Define FOV and resolution
2 | fov = 256e-3;
3 | sliceThickness = 5e-3;
4 | Nx = 256;
5 | Ny = Nx;
6 |
7 | % Define sequence parameters
8 | TE = 8e-3;
9 | TR = 22e-3;
10 | alpha=30;
11 |
12 | % set system limits
13 | sys = mr.opts('MaxGrad',12,'GradUnit','mT/m',...
14 | 'MaxSlew',100,'SlewUnit','T/m/s',...
15 | 'rfRingdownTime', 20e-6, 'rfDeadtime', 100e-6);
16 |
17 | % Create a new sequence object
18 | seq=mr.Sequence(sys);
19 |
20 | % Create slice selective alpha-pulse and corresponding gradients
21 | [rf, gz, gzReph] = mr.makeSincPulse(alpha*pi/180, 'Duration', 4e-3,...
22 | 'SliceThickness', sliceThickness, 'apodization', 0.5,'timeBwProduct', 4, ...
23 | 'system' ,sys, 'use', 'excitation');
24 |
25 | % Define other gradients and ADC events
26 | deltak = 1/fov; % Pulseq toolbox defaults to k-space units of m^-1
27 | gx = mr.makeTrapezoid('x', 'FlatArea', Nx*deltak, 'FlatTime', 6.4e-3);
28 | adc = mr.makeAdc(Nx, 'Duration', gx.flatTime, 'Delay', gx.riseTime);
29 | gxPre = mr.makeTrapezoid('x', 'Area', -gx.area/2, 'Duration', 2e-3);
30 | phaseAreas = ((0:Ny-1)-Ny/2)*deltak;
31 |
32 |
33 | % Calculate timing
34 | delayTE = round((TE - mr.calcDuration(gxPre) - mr.calcDuration(gz)/2 ...
35 | - mr.calcDuration(gx)/2)/seq.gradRasterTime)*seq.gradRasterTime;
36 | delayTR = round((TR - mr.calcDuration(gxPre) - mr.calcDuration(gz) ...
37 | - mr.calcDuration(gx) - delayTE)/seq.gradRasterTime)*seq.gradRasterTime;
38 |
39 | spoilArea=4*gx.area(); % 4 "looks" good
40 | % Add spoilers in read, refocus in phase and spoiler in slice
41 | gxPost = mr.makeTrapezoid('x', 'Area', spoilArea, 'system', sys); % we pass 'system' here to calculate shortest time gradient
42 | gyPost = mr.makeTrapezoid('y', 'Area', -max(phaseAreas(:)), 'Duration', 2e-3);
43 | gzPost = mr.makeTrapezoid('z', 'Area', spoilArea, 'system', sys);
44 |
45 | delayTR = delayTR - mr.calcDuration(gxPost, gyPost, gzPost);
46 |
47 | % Loop over phase encodes and define sequence blocks
48 | for i=-30:Ny % dummy scans
49 | % Vary RF phase quasi-randomly
50 | rand_phase = mod(117*(i^2 + i + 2), 360)*pi/180;
51 | [rf, gz] = mr.makeSincPulse(alpha*pi/180, 'Duration', 4e-3,...
52 | 'SliceThickness', 5e-3, ...
53 | 'apodization', 0.5, ...
54 | 'timeBwProduct', 4, ...
55 | 'system', sys, ...
56 | 'phaseOffset', rand_phase, ...
57 | 'use', 'excitation');
58 | seq.addBlock(rf, gz);
59 | if (i>0) % negative or zero index -- dummy scans
60 | gyPre = mr.makeTrapezoid('y', 'Area', phaseAreas(i), 'Duration', 2e-3);
61 | else
62 | gyPre = mr.makeTrapezoid('y', 'Area', 0, 'Duration', 2e-3);
63 | end
64 | seq.addBlock(gxPre, gyPre, gzReph);
65 | seq.addBlock(mr.makeDelay(delayTE));
66 | % Make receiver phase follow transmitter phase
67 | adc = mr.makeAdc(Nx, 'Duration', gx.flatTime,...
68 | 'Delay', gx.riseTime,...
69 | 'phaseOffset', rand_phase);
70 | if (i>0) % negative index -- dummy scans
71 | seq.addBlock(gx, adc);
72 | else
73 | seq.addBlock(gx);
74 | end
75 | gyPost = mr.makeTrapezoid('y', 'Area', -gyPre.area, 'Duration', 2e-3);
76 | % Add spoilers in read and slice and may be in phase
77 | seq.addBlock(gxPost, gyPost, gzPost);
78 | seq.addBlock(mr.makeDelay(delayTR));
79 | end
80 |
81 | % check whether the timing of the sequence is correct
82 | [ok, error_report]=seq.checkTiming;
83 |
84 | if (ok)
85 | fprintf('Timing check passed successfully\n');
86 | else
87 | fprintf('Timing check failed! Error listing follows:\n');
88 | fprintf([error_report{:}]);
89 | fprintf('\n');
90 | end
91 |
92 | % export definitions
93 | seq.setDefinition('FOV', [fov fov sliceThickness]);
94 | seq.setDefinition('Name', 'DEMO_gre5');
95 |
96 | seq.write(['DEMO_gre5.seq']) % Write to pulseq file
97 |
98 | seq.plot('timeRange', [0 2*TR])
99 |
100 | % do not run the rest of the script automatically
101 | return
102 |
103 | %% plot gradients to check for gaps and optimality of the timing
104 | wave_data=seq.waveforms_and_times();
105 | % plot the entire gradient shape
106 | figure; plot(wave_data{1}(1,:),wave_data{1}(2,:)); xlabel('time /s'); ylabel('gradient /(Hz/m)');
107 | hold on; plot(wave_data{2}(1,:),wave_data{2}(2,:));
108 | plot(wave_data{3}(1,:),wave_data{3}(2,:));
109 | legend('G_x', 'G_y', 'G_z');
110 |
111 | %% calculate k-space trajectory
112 |
113 | [ktraj_adc, t_adc, ktraj, t_ktraj, t_excitation, t_refocusing] = seq.calculateKspacePP();
114 |
115 | % plot k-spaces
116 | figure; plot(t_ktraj, ktraj'); % plot the entire k-space trajectory
117 | hold on; plot(t_adc,ktraj_adc(1,:),'.'); % and sampling points on the kx-axis
118 | title('k-space vector components as functions of time');
119 | legend('k_x', 'k_y', 'k_z');
120 |
121 | figure; plot(ktraj(1,:),ktraj(2,:),'b'); % a 2D plot
122 | hold on;plot(ktraj_adc(1,:),ktraj_adc(2,:),'r.'); % plot the sampling points
123 | axis('equal'); % enforce aspect ratio for the correct trajectory display
124 | title('k-space trajectory (k_x/k_y)');
125 |
126 | %%
127 |
128 | % [ktraj_adc, ktraj, t_excitation, t_refocusing, t_adc] = seq.calculateKspace();
129 |
130 | % plot k-spaces
131 | % time_axis=(1:(size(ktraj,2)))*sys.gradRasterTime;
132 | % figure; plot(time_axis, ktraj'); % plot the entire k-space trajectory
133 | % hold; plot(t_adc,ktraj_adc(1,:),'.'); % and sampling points on the kx-axis
134 | % figure; plot(ktraj(1,:),ktraj(2,:),'b'); % a 2D plot
135 | % axis('equal'); % enforce aspect ratio for the correct trajectory display
136 | % hold;plot(ktraj_adc(1,:),ktraj_adc(2,:),'r.'); % plot the sampling points
137 |
138 | %% very optional step, slow but useful for testing during development e.g. for the real TE, TR or for staying within slewrate limits
139 | rep = seq.testReport;
140 | fprintf([rep{:}]);
141 |
142 | %% listen to the sequence
143 | seq.sound();
144 |
--------------------------------------------------------------------------------
/11_from_GRE_to_EPI/README.md:
--------------------------------------------------------------------------------
1 | 
2 | # Pulseq tutorial "From GRE to EPI"
3 |
4 | Welcome to the "From GRE to EPI" tutorial repository! This was initially developed for the live Pulseq software demonstration and hands-on session at **MRI Together 2021** on-line conference.
5 |
6 | This tutorial introduces the way to establish an Echo-Planar Imaging (EPI)
7 | sequence based on a GRE sequence via multi-echo and segmented GRE sequences.
8 |
9 | [Handout materials](./doc/Handout.pdf) that accompanied the original session are available in the PDF format containing the slides from the presentation with some additional material and explanations. In particular, the second part of the document (page 18 and on) describes the code examples. Please keep in mind that these examples were specifically developed for the demo, so you might find some useful information there, even if you are familiar with the previous versions of Pulseq.
10 |
11 | Additionally, the slide deck entitled [11_from_GRE_to_EPI.pdf](./doc/11_from_GRE_to_EPI.pdf) shows sequence diagrams of all steps and visualises the changes at each step.
12 |
13 | ***s01\_GradientEcho***
14 |
15 | ***s01*** is a single-echo GRE sequence with Ny\*numTE phase encodes.
16 | Its echo time (TE) cycles by the defined TEs (\[4 9 15\] \* 1e-3). numTE
17 | is the number of the defined TEs. Note: the mr.align function is very
18 | useful to set delays of events within a block to achieve desirable
19 | alignment.
20 |
21 | ***s02\_MultiGradientEcho***
22 |
23 | ***s02*** is a monopolar multi-echo GRE sequence. The timing is
24 | calculated with the aid of helperT. gxFlyBack gradient is used to
25 | rephase the readout gradient.
26 |
27 | ***s03\_BipolarMultiGradientEcho***
28 |
29 | ***s03*** is a bipolar multi-echo GRE sequence. It eliminates gxFlyBack
30 | gradients by reversing the polarity of the even readouts.
31 |
32 | ***s04a\_SegmentedGradientEcho***
33 |
34 | ***s04a*** is a readout-segmented single-echo GRE sequence. Instead of
35 | sampling the k-space for one readout during each TR, it samples the
36 | k-space with multiple readouts during each TR (i.e. segmented readouts).
37 | Bipolar readout gradients are used to avoid time loss due to additional
38 | fly-back gradients. The area of phase-encoding (PE) gradients
39 | (pre-dephasing and blip) is determined based on the number of segmented
40 | readouts (nSeg).
41 |
42 | ***s04b\_SegmentedGradientEcho***
43 |
44 | In s04b, the splitGradientAt function is used to split the gyBlip
45 | gradient of ***s04a*** into two parts, each in a separate block, which
46 | reduces the time interval between two adjacent segmented readouts. In
47 | the first segmented readout block, the first part of gyBlip is added
48 | after the readout gradient. In inner segmented readout blocks, the
49 | second and first parts of gyBlip are combined and added before and after
50 | the readout gradient. In the last segmented readout block, the second
51 | part of gyBlip is added before the readout gradient.
52 |
53 | In case the gyBlip.riseTime is shorter than the fallTime of the former
54 | block, gyBlip.delay is increased, and thus the peak of gyBlip is moved
55 | to the edge of the former block. In case gyBlip.riseTime is longer than
56 | the fallTime of the former block, a delay is added to the later readout
57 | gradient, such that the last point of gyBlip hits the last point of the
58 | ramp-up of the later readout gradient.
59 |
60 | ***s04c\_SegmentedGradientEcho***
61 |
62 | ***s04c*** increases the nSeg of ***s04b*** from 5 to Ny, such that
63 | ***s04c*** traverses the whole k-space in one segmented scan (i.e. an
64 | improvised EPI scan).
65 |
66 | ***s05\_EchoPlanarImaging***
67 |
68 | ***s05*** is a 2D EPI sequence with the shortest timing. It contains
69 | Ny+3 blocks.
70 |
71 | The mr.makeDigitalOutputPulse function defines the output trigger to
72 | play out with every slice excitation.
73 |
74 | The shortest timing is achieved by using maximum slew rate and ramp
75 | sampling. gyBlip is split into two parts and distributed to two adjacent
76 | blocks. The dead times of the readout trapezoid gradient are at the
77 | beginning and the end (align with two parts of gyBlip), each equal to
78 | half of gyBlip.Duration. The readout gradient is first calculated based
79 | on the maximum slew rate (with consideration of the dead times). Then,
80 | its amplitude is scaled down to fix the area to be Nx\*deltak.
81 |
82 | The ADC dwell time on the readout flat-top is calculated based on
83 | adcDwellNyquist=deltak/gx.amplitude. The dwell time is rounded down to
84 | system.adcRasterTime. Note that the number of ADC samples on Siemens
85 | should be divisible by 4. In addition, the ADC should be aligned with
86 | respect to the readout gradient: *both Pulseq and Siemens define the ADC
87 | samples to happen in the centre of the dwell period*.
88 |
89 | ***s06\_EPI\_SingleTraj***
90 |
91 | ***s06*** is a 2D EPI sequence constructed from a single arbitrary
92 | trajectory. It is based on ***s05*** and contains 4 blocks.
93 |
94 | A dummy sequence object (seq\_d) creates and exports the single EPI
95 | trajectory in xy-plane with Ny phase encoding steps. A single ADC object
96 | is created to sample from the start to the end of the single trajectory
97 | (excluding the ADC dead times:
98 | adcDur=seq\_d.duration-2\*sys.adcDeadTime). A real sequence object
99 | (seq\_r) is created to combine the slice-selective excitation, Gz
100 | rephasing and Gx and Gy dephasing, the single EPI trajectory and the
101 | single ADC, and Gz spoiler together, for a total of 4 blocks.
102 |
103 | ## Quick instructions
104 |
105 | Source code of the demo sequences and reconstruction scripts is the core of this repository. Please download the files to your computer and make them available to Matlab (e.g. by saving them in a subdirectory inside your Pulseq-Matlab installation and adding them to the Matlab's path). There are three sub-directories:
106 |
107 | * seq : contains example pulse sequences specifically prepared for this demo
108 | * recon : contains the reconstruction scripts tested with the above sequences
109 | * data: raw MR data and sequences in Pulseq format
110 |
111 | ## Quick links
112 |
113 | Pulseq Matlab repository:
114 | https://github.com/pulseq/pulseq
115 |
116 | ## How to follow
117 |
118 | We strongly recommend using a text compare tool like *meld* (see this [Wikipedia page](https://en.wikipedia.org/wiki/Meld_(software)) and compare sequences from subsequent steps to visualise the respective steps.
119 |
120 | ## Further links
121 |
122 | Check out the main *Pulseq* repository at https://github.com/pulseq/pulseq and familarising yourself with the code, example sequences and reconstruction scripts (see
123 | [pulseq/matlab/demoSeq](https://github.com/pulseq/pulseq/tree/master/matlab/demoSeq) and [pulseq/matlab/demoRecon](https://github.com/pulseq/pulseq/tree/master/matlab/demoRecon)). If you already use Pulseq, consider updating to the current version.
124 |
125 |
--------------------------------------------------------------------------------
/11_from_GRE_to_EPI/data/01_GRE.dat:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pulseq/tutorials/651dcc7bb46353ceb05783acd39c603abca13ca3/11_from_GRE_to_EPI/data/01_GRE.dat
--------------------------------------------------------------------------------
/11_from_GRE_to_EPI/data/02_GRE_3echoes.dat:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pulseq/tutorials/651dcc7bb46353ceb05783acd39c603abca13ca3/11_from_GRE_to_EPI/data/02_GRE_3echoes.dat
--------------------------------------------------------------------------------
/11_from_GRE_to_EPI/data/03_GRE_3echoes_bipolar.dat:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pulseq/tutorials/651dcc7bb46353ceb05783acd39c603abca13ca3/11_from_GRE_to_EPI/data/03_GRE_3echoes_bipolar.dat
--------------------------------------------------------------------------------
/11_from_GRE_to_EPI/data/04a_GRE_3seg.dat:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pulseq/tutorials/651dcc7bb46353ceb05783acd39c603abca13ca3/11_from_GRE_to_EPI/data/04a_GRE_3seg.dat
--------------------------------------------------------------------------------
/11_from_GRE_to_EPI/data/04b_GRE_5seg_fast.dat:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pulseq/tutorials/651dcc7bb46353ceb05783acd39c603abca13ca3/11_from_GRE_to_EPI/data/04b_GRE_5seg_fast.dat
--------------------------------------------------------------------------------
/11_from_GRE_to_EPI/data/04c_improvised_EPI.dat:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pulseq/tutorials/651dcc7bb46353ceb05783acd39c603abca13ca3/11_from_GRE_to_EPI/data/04c_improvised_EPI.dat
--------------------------------------------------------------------------------
/11_from_GRE_to_EPI/data/04c_improvised_EPI_noPE.dat:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pulseq/tutorials/651dcc7bb46353ceb05783acd39c603abca13ca3/11_from_GRE_to_EPI/data/04c_improvised_EPI_noPE.dat
--------------------------------------------------------------------------------
/11_from_GRE_to_EPI/data/04c_improvised_EPI_other_phantom_other scanner.dat:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pulseq/tutorials/651dcc7bb46353ceb05783acd39c603abca13ca3/11_from_GRE_to_EPI/data/04c_improvised_EPI_other_phantom_other scanner.dat
--------------------------------------------------------------------------------
/11_from_GRE_to_EPI/data/04c_improvised_EPI_other_phantom_other scanner_noPE.dat:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pulseq/tutorials/651dcc7bb46353ceb05783acd39c603abca13ca3/11_from_GRE_to_EPI/data/04c_improvised_EPI_other_phantom_other scanner_noPE.dat
--------------------------------------------------------------------------------
/11_from_GRE_to_EPI/data/05_EPI.dat:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pulseq/tutorials/651dcc7bb46353ceb05783acd39c603abca13ca3/11_from_GRE_to_EPI/data/05_EPI.dat
--------------------------------------------------------------------------------
/11_from_GRE_to_EPI/data/05_EPI_noPE.dat:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pulseq/tutorials/651dcc7bb46353ceb05783acd39c603abca13ca3/11_from_GRE_to_EPI/data/05_EPI_noPE.dat
--------------------------------------------------------------------------------
/11_from_GRE_to_EPI/data/06_EPI_singleTraj.dat:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pulseq/tutorials/651dcc7bb46353ceb05783acd39c603abca13ca3/11_from_GRE_to_EPI/data/06_EPI_singleTraj.dat
--------------------------------------------------------------------------------
/11_from_GRE_to_EPI/doc/11_from_GRE_to_EPI.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pulseq/tutorials/651dcc7bb46353ceb05783acd39c603abca13ca3/11_from_GRE_to_EPI/doc/11_from_GRE_to_EPI.pdf
--------------------------------------------------------------------------------
/11_from_GRE_to_EPI/doc/11_from_GRE_to_EPI.pptx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pulseq/tutorials/651dcc7bb46353ceb05783acd39c603abca13ca3/11_from_GRE_to_EPI/doc/11_from_GRE_to_EPI.pptx
--------------------------------------------------------------------------------
/11_from_GRE_to_EPI/doc/Handout.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pulseq/tutorials/651dcc7bb46353ceb05783acd39c603abca13ca3/11_from_GRE_to_EPI/doc/Handout.pdf
--------------------------------------------------------------------------------
/11_from_GRE_to_EPI/doc/mri_together_esmrmb_banner.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pulseq/tutorials/651dcc7bb46353ceb05783acd39c603abca13ca3/11_from_GRE_to_EPI/doc/mri_together_esmrmb_banner.png
--------------------------------------------------------------------------------
/11_from_GRE_to_EPI/recon/r01_2DFFT.m:
--------------------------------------------------------------------------------
1 | % Reconstruction of 2D Cartesian Pulseq data
2 | % provides an example on how data reordering can be detected from the MR
3 | % sequence with almost no additional prior knowledge
4 | %
5 | % it loads Matlab .mat files with the rawdata in the format
6 | % adclen x channels x readouts
7 | % if Matlab .mat file not available it attempt to load Siemens .dat (which needs mapVBVD in the path)
8 | % but first it seeks the accompanying .seq file with the same name to interpret
9 | % the data
10 |
11 | %% Load data sorted by name
12 | path='./tutorials/11_from_GRE_to_EPI/data'; % directory to be scanned for data files
13 | nF=1; % the number of the data set to load;
14 |
15 | pattern='*.seq';
16 | D=dir([path filesep pattern]);
17 | [~,I]=sort(string({D(:).name}));
18 | seq_file_path=[path filesep D(I(nF)).name];
19 |
20 | % keep basic filename without the extension
21 | [p,n,e] = fileparts(seq_file_path);
22 | basic_file_path=fullfile(p,n);
23 |
24 | %% load raw data
25 | % try loading Matlab data
26 | data_file_path=[basic_file_path '.mat'];
27 | if isfile(data_file_path)
28 | fprintf(['loading `' data_file_path '´ ...\n']);
29 | data_unsorted = load(data_file_path);
30 | if isstruct(data_unsorted)
31 | fn=fieldnames(data_unsorted);
32 | assert(length(fn)==1); % we only expect a single variable
33 | data_unsorted=data_unsorted.(fn{1});
34 | end
35 | else
36 | % revert to Siemens .dat file
37 | data_file_path=[basic_file_path '.dat'];
38 | fprintf(['loading `' data_file_path '´ ...\n']);
39 | twix_obj = mapVBVD(data_file_path);
40 | if iscell(twix_obj)
41 | data_unsorted = twix_obj{end}.image.unsorted();
42 | seqHash_twix=twix_obj{end}.hdr.Dicom.tSequenceVariant;
43 | else
44 | data_unsorted = twix_obj.image.unsorted();
45 | seqHash_twix=twix_obj.hdr.Dicom.tSequenceVariant;
46 | end
47 | if length(seqHash_twix)==32
48 | fprintf(['raw data contain pulseq-file signature ' seqHash_twix '\n']);
49 | end
50 |
51 | end
52 | [adc_len,channels,readouts]=size(data_unsorted);
53 |
54 | %% Load sequence from file
55 | fprintf(['loading `' seq_file_path '´ ...\n']);
56 | seq = mr.Sequence(); % Create a new sequence object
57 | seq.read(seq_file_path,'detectRFuse');
58 | seqName=seq.getDefinition('Name');
59 | if ~isempty(seqName), fprintf('sequence name: %s\n',seqName); end
60 | [ktraj_adc, t_adc, ktraj, t_ktraj, t_excitation, t_refocusing] = seq.calculateKspacePP();
61 | figure; plot(ktraj(1,:),ktraj(2,:),'b',...
62 | ktraj_adc(1,:),ktraj_adc(2,:),'r.'); % a 2D plot
63 | axis('equal'); title('2D k-space trajectory');
64 | xlabel('kx / m^-^1'); ylabel('kx / m^-^1');
65 | %saveas(gcf, [basic_file_path '_kspace.png']);
66 |
67 | %% Analyze the trajectory data (ktraj_adc)
68 | fprintf('analyzing the k-space trajectory ...\n');
69 | k_extent=max(abs(ktraj_adc),[],2);
70 | k_scale=max(k_extent);
71 | k_threshold=k_scale/5000;
72 |
73 | % detect unused dimensions and delete them
74 | if any(k_extentk_threshold)=NaN;
86 | dk_all_cnt=sum(isfinite(dk_all),2);
87 | dk_all(~isfinite(dk_all))=0;
88 | dk=sum(dk_all,2)./dk_all_cnt;
89 | dk(~isfinite(dk))=0;
90 | [~,k0_ind]=min(sum(ktraj_adc.^2,1));
91 | kindex=round((ktraj_adc-ktraj_adc(:,k0_ind*ones(1,size(ktraj_adc,2))))./dk(:,ones(1,size(ktraj_adc,2))));
92 | kindex(~isfinite(kindex))=0;
93 | kindex_min=min(kindex,[],2);
94 | kindex_mat=kindex-kindex_min(:,ones(1,size(ktraj_adc,2)))+1;
95 | kindex_end=max(kindex_mat,[],2);
96 | sampler=zeros(kindex_end');
97 | repeat=zeros(1,size(ktraj_adc,2));
98 | for i=1:size(kindex_mat,2)
99 | if (size(kindex_mat,1)==3)
100 | ind=sub2ind(kindex_end,kindex_mat(1,i),kindex_mat(2,i),kindex_mat(3,i));
101 | else
102 | ind=sub2ind(kindex_end,kindex_mat(1,i),kindex_mat(2,i));
103 | end
104 | repeat(i)=sampler(ind);
105 | sampler(ind)=repeat(i)+1;
106 | end
107 | if (max(repeat(:))>0)
108 | kindex=[kindex;(repeat+1)];
109 | kindex_mat=[kindex_mat;(repeat+1)];
110 | kindex_end=max(kindex_mat,[],2);
111 | end
112 | %figure; plot(kindex(1,:),kindex(2,:),'.-');
113 |
114 | %% sort the k-space data into the data matrix
115 | % the incoming data order is [kx coils acquisitions]
116 | data_coils_last = permute(data_unsorted, [1, 3, 2]);
117 | data_coils_last = reshape(data_coils_last, [adc_len*readouts, channels]);
118 |
119 | data=zeros([kindex_end' channels]);
120 | if (size(kindex,1)==3)
121 | for i=1:size(kindex,2)
122 | data(kindex_mat(1,i),kindex_mat(2,i),kindex_mat(3,i),:)=data_coils_last(i,:);
123 | end
124 | else
125 | for i=1:size(kindex,2)
126 | data(kindex_mat(1,i),kindex_mat(2,i),:)=data_coils_last(i,:);
127 | end
128 | end
129 |
130 | if size(kindex,1)==3
131 | nImages=size(data,3);
132 | else
133 | nImages=1;
134 | data=reshape(data, [size(data,1) size(data,2) 1 size(data,3)]); % we need a dummy images/slices dimension
135 | end
136 |
137 | % account for the matlab's strange convention with data dimensions
138 | data=flip(data,1); % left-right orientation, works on TRIO
139 | data=flip(data,2);
140 |
141 | %figure; imab(log(abs(data))); title('k-space data');
142 |
143 | %% Reconstruct coil images
144 |
145 | images = zeros(size(data));
146 | %figure;
147 |
148 | for ii = 1:channels
149 | images(:,:,:,ii) = ifftshift(ifft2(ifftshift(data(:,:,:,ii)))); % 1.4.0 does not need read inversion
150 | end
151 |
152 | % Phase images (possibly channel-by-channel and echo-by-echo)
153 | %figure;imab(angle(images));colormap('jet');
154 | %figure;imab(abs(images));colormap('gray');
155 |
156 | %% Image display with optional sum of squares combination
157 | figure;
158 | if channels>1
159 | sos=abs(sum(images.*conj(images),ndims(images))).^(1/2);
160 | sos=sos./max(sos(:));
161 | imab(sos); title('reconstructed image(s), sum-of-squares');
162 | %imwrite(sos, ['img_combined.png']
163 | else
164 | imab(abs(images)); title('reconstructed image(s)');
165 | end
166 | colormap('gray');
167 | saveas(gcf,[basic_file_path '_image_2dfft'],'png');
168 |
169 | %% reconstruct field map (optional)
170 |
171 | if size(images,3)>=2
172 | cmplx_diff=images(:,:,2,:).*conj(images(:,:,1,:));
173 | phase_diff_image=angle(sum(cmplx_diff,4));
174 | figure;
175 | imab(phase_diff_image);colormap('jet');
176 | title('phase difference(s) echo2-echo1');
177 | end
178 |
179 | %% gif movie export
180 |
181 | scale=1.2;
182 | filename='fftReconMovie';
183 | fps=5;
184 |
185 | if size(images,3)>4
186 |
187 | clm=gray(256);
188 |
189 | if channels>1
190 | imex=sos;
191 | else
192 | imex=abs(images);
193 | imex=imex/max(imex(:));
194 | end
195 |
196 | imex=permute(imex(:,end:-1:1,:),[2,1,3]);
197 |
198 | imind=uint8(scale*imex*(size(clm,1)-1)+1);
199 | imwrite(imind(:,:,1),clm, [filename, '.gif'],'DelayTime',1/fps,'Loopcount',inf);
200 | for i=2:size(imind,3),
201 | imwrite(imind(:,:,i),clm, [filename, '.gif'],'DelayTime',1/fps, 'WriteMode','append');
202 | end
203 |
204 | end
205 |
--------------------------------------------------------------------------------
/11_from_GRE_to_EPI/recon/r03_2DGD.m:
--------------------------------------------------------------------------------
1 | % very basic and crude non-Cartesian recon using griddata()
2 | %
3 | % needs mapVBVD in the path
4 |
5 | %% Load data sorted by name
6 | path='./tutorials/11_from_GRE_to_EPI/data'; % directory to be scanned for data files
7 | nF=12; % the number of the data set to load; you can grid any data with pulseq, but in this tutorial it is only necessary for #12
8 |
9 | pattern='*.seq';
10 | D=dir([path filesep pattern]);
11 | [~,I]=sort(string({D(:).name}));
12 | seq_file_path=[path filesep D(I(nF)).name];
13 |
14 |
15 | %% alternatively just provide the path to the .seq file
16 | %seq_file_path='../interpreters/siemens/data_example/gre_example.seq'
17 | %seq_file_path='/data/20211025-AMR/data/2021-10-26-093137.seq';
18 |
19 | %% Load sequence from the file with the same name as the raw data
20 | fprintf(['loading `' seq_file_path '´ ...\n']);
21 | seq = mr.Sequence(); % Create a new sequence object
22 | seq.read(seq_file_path,'detectRFuse');
23 |
24 | %% keep basic filename without the extension
25 | [p,n,e] = fileparts(seq_file_path);
26 | basic_file_path=fullfile(p,n);
27 |
28 | %% load the raw data file
29 | data_file_path= [basic_file_path '.mat']; % try to load a matlab file with raw data...
30 | try
31 | fprintf(['loading `' data_file_path '´ ...\n']);
32 | data_unsorted = load(data_file_path);
33 | if isstruct(data_unsorted)
34 | fn=fieldnames(data_unsorted);
35 | assert(length(fn)==1); % we only expect a single variable
36 | data_unsorted=double(data_unsorted.(fn{1}));
37 | end
38 | catch
39 | data_file_path= [basic_file_path '.dat']; % now try to load a raw data file...
40 | fprintf(['falling back to `' data_file_path '´ ...\n']);
41 | twix_obj = mapVBVD(data_file_path);
42 | if iscell(twix_obj)
43 | data_unsorted = double(twix_obj{end}.image.unsorted());
44 | seqHash_twix=twix_obj{end}.hdr.Dicom.tSequenceVariant;
45 | else
46 | data_unsorted = double(twix_obj.image.unsorted());
47 | seqHash_twix=twix_obj.hdr.Dicom.tSequenceVariant;
48 | end
49 | if length(seqHash_twix)==32
50 | fprintf(['raw data contain pulseq-file signature ' seqHash_twix '\n']);
51 | end
52 | clear twix_obj
53 | end
54 |
55 | %% calculate k-space trajectory
56 | %[3.5 4 0] % for AMR 2D-UTE
57 | traj_recon_delay=0.0e-6;% [0.527 -1.367 0]; % adjust this parameter to potentially improve resolution & geometric accuracy. It can be calibrated by inverting the spiral revolution dimension and making two images match. for our Prisma and a particular trajectory we found 1.75e-6
58 | grad_offsets=[0 0 0];
59 |
60 | seq = mr.Sequence(); % Create a new sequence object
61 | seq.read(seq_file_path,'detectRFuse');
62 | seqName=seq.getDefinition('Name');
63 | if ~isempty(seqName), fprintf('sequence name: %s\n',seqName); end
64 | %[ktraj_adc, ktraj, t_excitation, t_refocusing, t_adc] = seq.calculateKspace('trajectory_delay', traj_recon_delay);
65 | [ktraj_adc, t_adc, ktraj, t_ktraj, t_excitation, t_refocusing] = seq.calculateKspacePP('trajectory_delay', traj_recon_delay, 'gradient_offset', grad_offsets);
66 | figure; plot(ktraj(1,:),ktraj(2,:),'b',...
67 | ktraj_adc(1,:),ktraj_adc(2,:),'r.'); % a 2D plot
68 | axis('equal');
69 | title('2D k-space trajectory'); drawnow;
70 |
71 | %% Define FOV and resolution and simple off-resonance frequency correction
72 |
73 | fov=256e-3; Nx=128; Ny=Nx; % define parameters explicitly
74 | Ns=1; % this is a number of slices (or contrasts) which needs to be specified manually for now
75 | %Na=1;
76 |
77 | def_fov=seq.getDefinition('FOV'); % try to read from definitions
78 | if numel(def_fov)
79 | fov=max(def_fov);
80 | end
81 | %fov=fov*1.5;
82 | deltak=1/fov;
83 |
84 | % or estimate from the k-space trajectory
85 | k_max=max(vecnorm(ktraj_adc));
86 | Nx=round(k_max/deltak*2);
87 | Ny=Nx;
88 |
89 | os=2; % oversampling factor (we oversample both in image and k-space)
90 | offresonance=0; % global off-resonance in Hz
91 |
92 | %%
93 | rawdata = permute(data_unsorted, [1,3,2]);
94 | adc_len=size(rawdata,1);
95 | readouts=size(rawdata,2);
96 | channels=size(rawdata,3);
97 |
98 | Na=numel(rawdata(:,:,1))/Ns/numel(t_adc); % averages/acquisitions
99 | if Na>1
100 | nTRs=size(rawdata,2)/Na;
101 | rawdata = sum(permute(reshape(rawdata, [size(rawdata,1),size(rawdata,2)/Na,Na,size(rawdata,3)]),[1,2,4,3]),4);
102 | readouts=readouts/Na;
103 | end
104 |
105 | if strcmp('ute_rs',seq.getDefinition('Name'))
106 | % average every 2nd spoke because of the half-rf excitation
107 | ktraj_adc=reshape(ktraj_adc,[3,adc_len,readouts]);
108 | ktraj_adc=ktraj_adc(:,:,1:2:end-1);
109 | t_adc=reshape(t_adc,[1,adc_len,readouts]);
110 | t_adc=t_adc(:,:,1:2:end-1);
111 | %rawdata=rawdata(:,1:2:end-1,:);
112 | rawdata=rawdata(:,1:2:end-1,:)+rawdata(:,2:2:end,:);
113 | readouts=readouts/2;
114 | ktraj_adc=reshape(ktraj_adc,[3,adc_len*readouts]);
115 | t_adc=reshape(t_adc,[1,adc_len*readouts]);
116 | end
117 |
118 | rawdata = reshape(rawdata, [size(rawdata,1)*size(rawdata,2)/Ns,Ns,size(rawdata,3)]);
119 | ktraj_adc=ktraj_adc(:,1:end/Ns);
120 | t_adc=t_adc(1:end/Ns);
121 |
122 | for s=1:Ns
123 | for c=1:channels
124 | rawdata(:,s,c) = rawdata(:,s,c) .* exp(-1i*2*pi*t_adc'*offresonance);
125 | end
126 | end
127 |
128 | %% here we expect Nx, Ny, deltak to be set already
129 | % and rawdata ktraj_adc loaded (and having the same dimensions)
130 |
131 | kxm=round(os*os*Nx/2);
132 | kym=round(os*os*Ny/2);
133 |
134 | [kyy,kxx] = meshgrid(-kxm:(kxm-1), -kym:(kym-1));
135 | kyy=-kyy*deltak/os; % we swap the order and invert one sign to account for Matlab's strange column/line convention
136 | kxx=-kxx*deltak/os; % this is needed to match Localizer images on TRIO
137 |
138 | kgd=zeros([size(kxx) Ns channels]);
139 | d1=1; d2=size(rawdata,1);
140 | %d1=320*64+1; d2=320*192;
141 | %d1=1; d2=320*128;
142 | %d1=320*128+1; d2=320*256;
143 | for s=1:Ns
144 | for c=1:channels
145 | kgd(:,:,s,c)=griddata(ktraj_adc(1,d1:d2),ktraj_adc(2,d1:d2),rawdata(d1:d2,s,c),kxx,kyy,'cubic');
146 | end
147 | end
148 | kgd(isnan(kgd))=0;
149 |
150 | figure;imagesc(log(abs(kgd(:,:,1,1)))');axis('square');
151 | title('2D k-space data, ch1 as log(abs())');
152 |
153 | igd=ifftshift(ifft2(ifftshift(kgd)));
154 |
155 | Nxo=round(Nx*os);
156 | Nyo=round(Ny*os);
157 | Nxs=round((size(igd,1)-Nxo)/2);
158 | Nys=round((size(igd,2)-Nyo)/2);
159 | images = igd((Nxs+1):(Nxs+Nxo),(Nys+1):(Nys+Nyo),:,:);
160 |
161 | %% Image display with optional sum of squares combination
162 | figure;
163 | if channels>1
164 | sos=abs(sum(images.*conj(images),ndims(images))).^(1/2);
165 | sos=sos./max(sos(:));
166 | imab(sos); title('reconstructed image(s), sum-of-squares');
167 | %imwrite(sos, ['img_combined.png']
168 | else
169 | imab(abs(images)); title('reconstructed image(s)');
170 | end
171 | colormap('gray');
172 | add='';
173 | if abs(traj_recon_delay)>eps, add='_delay'; end
174 | saveas(gcf,[basic_file_path '_image_2d_gridding' add],'png');
175 |
176 |
--------------------------------------------------------------------------------
/11_from_GRE_to_EPI/seq/s01_GradientEcho.m:
--------------------------------------------------------------------------------
1 | % set system limits
2 | sys = mr.opts('MaxGrad', 28, 'GradUnit', 'mT/m', ...
3 | 'MaxSlew', 150, 'SlewUnit', 'T/m/s', ...
4 | 'rfRingdownTime', 20e-6, 'rfDeadTime', 100e-6, 'adcDeadTime', 10e-6);
5 |
6 | % basic parameters
7 | seq=mr.Sequence(sys); % Create a new sequence object
8 | fov=256e-3; Nx=256; Ny=Nx; % Define FOV and resolution
9 | alpha=10; % flip angle
10 | sliceThickness=3e-3; % slice
11 | TR=21e-3; % TR, a single value
12 | %TE=4e-3; % TE
13 | TE=[4 9 15]*1e-3; % optionally give a vector here to have multiple TEs (e.g. for field mapping)
14 | % TODO: change to multiple contrasts, change order of loops...
15 |
16 | % more in-depth parameters
17 | rfSpoilingInc=117; % RF spoiling increment
18 | rfDuration=3e-3;
19 | roDuration=3.2e-3; % not all values are possible, watch out for the checkTiming output
20 |
21 | % Create alpha-degree slice selection pulse and corresponding gradients
22 | [rf, gz, gzReph] = mr.makeSincPulse(alpha*pi/180,'Duration',rfDuration,...
23 | 'SliceThickness',sliceThickness,'apodization',0.42,'timeBwProduct',4,'use','excitation','system',sys);
24 |
25 | % Define other gradients and ADC events
26 | deltak=1/fov; % Pulseq default units for k-space are inverse meters
27 | gx = mr.makeTrapezoid('x','FlatArea',Nx*deltak,'FlatTime',roDuration,'system',sys); % Pulseq default units for gradient amplitudes are 1/Hz
28 | adc = mr.makeAdc(Nx,'Duration',gx.flatTime,'Delay',gx.riseTime,'system',sys);
29 | gxPre = mr.makeTrapezoid('x','Area',-gx.area/2,'system',sys); % if no 'Duration' is provided shortest possible duration will be used
30 | phaseAreas = ((0:Ny-1)-Ny/2)*deltak;
31 |
32 | % gradient spoiling
33 | gxSpoil=mr.makeTrapezoid('x','Area',2*Nx*deltak,'system',sys); % 2 cycles over the voxel size in X
34 | gzSpoil=mr.makeTrapezoid('z','Area',4/sliceThickness,'system',sys); % 4 cycles over the slice thickness
35 |
36 | % Calculate timing (need to decide on the block structure already)
37 | delayTE=ceil((TE - gz.fallTime - gz.flatTime/2 ...
38 | - mr.calcDuration(gx)/2)/seq.gradRasterTime)*seq.gradRasterTime;
39 | delayTR=ceil((TR - mr.calcDuration(gz) - max(mr.calcDuration(gxPre,gzReph),delayTE) ...
40 | - mr.calcDuration(gx))/seq.gradRasterTime)*seq.gradRasterTime;
41 | assert(all(delayTE>=mr.calcDuration(gxPre,gzReph)));
42 | assert(all(delayTR>=mr.calcDuration(gxSpoil,gzSpoil)));
43 |
44 | % initialize the RF spoling counters
45 | rf_phase=0;
46 | rf_inc=0;
47 |
48 | % define sequence blocks
49 | for i=1:Ny % loop over phase encodes
50 | for c=1:length(TE) % loop over TEs
51 | rf.phaseOffset=rf_phase/180*pi;
52 | adc.phaseOffset=rf_phase/180*pi;
53 | rf_inc=mod(rf_inc+rfSpoilingInc, 360.0);
54 | rf_phase=mod(rf_phase+rf_inc, 360.0);
55 | %
56 | seq.addBlock(rf,gz);
57 | gyPre = mr.makeTrapezoid('y','Area',phaseAreas(i),'Duration',mr.calcDuration(gxPre),'system',sys);
58 | %seq.addBlock(mr.makeDelay(delayTE(c)),gxPre,gyPre,gzReph);
59 | seq.addBlock(mr.align('left', mr.makeDelay(delayTE(c)),gyPre,gzReph,'right',gxPre)); % stitch the read prephase to the readout gradient
60 | seq.addBlock(gx,adc);
61 | gyPre.amplitude=-gyPre.amplitude; % better to use mr.scaleGrad(gyPre,-1);
62 | seq.addBlock(mr.makeDelay(delayTR(c)),gxSpoil,gyPre,gzSpoil)
63 | end
64 | end
65 |
66 | %% check whether the timing of the sequence is correct
67 | [ok, error_report]=seq.checkTiming;
68 |
69 | if (ok)
70 | fprintf('Timing check passed successfully\n');
71 | else
72 | fprintf('Timing check failed! Error listing follows:\n');
73 | fprintf([error_report{:}]);
74 | fprintf('\n');
75 | end
76 |
77 | %% prepare sequence export
78 | seq.setDefinition('FOV', [fov fov sliceThickness]);
79 | seq.setDefinition('Name', 'gre');
80 | seq.write('gre.seq') % Write to pulseq file
81 | %seq.install('siemens');
82 |
83 | %% plot sequence and k-space diagrams
84 |
85 | seq.plot('timeRange', [0 5]*TR);
86 |
87 | % k-space trajectory calculation
88 | [ktraj_adc, t_adc, ktraj, t_ktraj, t_excitation, t_refocusing] = seq.calculateKspacePP();
89 |
90 | % plot k-spaces
91 | figure; plot(ktraj(1,:),ktraj(2,:),'b'); % a 2D k-space plot
92 | axis('equal'); % enforce aspect ratio for the correct trajectory display
93 | hold;plot(ktraj_adc(1,:),ktraj_adc(2,:),'r.'); % plot the sampling points
94 | title('full k-space trajectory (k_x x k_y)');
95 |
96 | %% very optional slow step, but useful for testing during development e.g. for the real TE, TR or for staying within slewrate limits
97 | rep = seq.testReport;
98 | fprintf([rep{:}]);
99 |
--------------------------------------------------------------------------------
/11_from_GRE_to_EPI/seq/s02_MultiGradientEcho.m:
--------------------------------------------------------------------------------
1 | % set system limits
2 | sys = mr.opts('MaxGrad', 28, 'GradUnit', 'mT/m', ...
3 | 'MaxSlew', 150, 'SlewUnit', 'T/m/s', ...
4 | 'rfRingdownTime', 20e-6, 'rfDeadTime', 100e-6, 'adcDeadTime', 10e-6);
5 |
6 | % basic parameters
7 | seq=mr.Sequence(sys); % Create a new sequence object
8 | fov=256e-3; Nx=256; Ny=Nx; % Define FOV and resolution
9 | alpha=10; % flip angle
10 | sliceThickness=3e-3; % slice
11 | TR=21e-3; % TR, a single value
12 | TE=[4 9 15]*1e-3; % give a vector here to have multiple TEs
13 |
14 | % more in-depth parameters
15 | rfSpoilingInc=117; % RF spoiling increment
16 | rfDuration=3e-3;
17 | roDuration=3.2e-3; % not all values are possible, watch out for the checkTiming output
18 |
19 | % Create alpha-degree slice selection pulse and corresponding gradients
20 | [rf, gz, gzReph] = mr.makeSincPulse(alpha*pi/180,'Duration',rfDuration,...
21 | 'SliceThickness',sliceThickness,'apodization',0.42,'timeBwProduct',4,'use','excitation','system',sys);
22 |
23 | % Define other gradients and ADC events
24 | deltak=1/fov; % Pulseq default units for k-space are inverse meters
25 | gx = mr.makeTrapezoid('x','FlatArea',Nx*deltak,'FlatTime',roDuration,'system',sys); % Pulseq default units for gradient amplitudes are 1/Hz
26 | adc = mr.makeAdc(Nx,'Duration',gx.flatTime,'Delay',gx.riseTime,'system',sys);
27 | gxPre = mr.makeTrapezoid('x','Area',-gx.area/2,'system',sys); % if no 'Duration' is provided shortest possible duration will be used
28 | gxFlyBack = mr.makeTrapezoid('x','Area',-gx.area,'system',sys);
29 | phaseAreas = ((0:Ny-1)-Ny/2)*deltak;
30 |
31 | % gradient spoiling
32 | gxSpoil=mr.makeTrapezoid('x','Area',2*Nx*deltak,'system',sys); % 2 cycles over the voxel size in X
33 | gzSpoil=mr.makeTrapezoid('z','Area',4/sliceThickness,'system',sys); % 4 cycles over the slice thickness
34 |
35 | % Calculate timing (need to decide on the block structure already)
36 | helperT=ceil((gz.fallTime + gz.flatTime/2 + mr.calcDuration(gx)/2)/seq.gradRasterTime)*seq.gradRasterTime;
37 | delayTE = zeros(size(TE)) ;
38 | for c=1:length(TE)
39 | delayTE(c)=TE(c) - helperT;
40 | helperT=helperT+delayTE(c)+mr.calcDuration(gx);
41 | end
42 | assert(all(delayTE(1)>=mr.calcDuration(gxPre,gzReph)));
43 | assert(all(delayTE(2:end)>=mr.calcDuration(gxFlyBack)));
44 | delayTR=round((TR - mr.calcDuration(gz) - sum(delayTE) ...
45 | - mr.calcDuration(gx)*length(TE))/seq.gradRasterTime)*seq.gradRasterTime;
46 | assert(all(delayTR>=mr.calcDuration(gxSpoil,gzSpoil)));
47 |
48 | % initialize the RF spoling counters
49 | rf_phase=0;
50 | rf_inc=0;
51 |
52 | % define sequence blocks
53 | for i=1:Ny % loop over phase encodes
54 | rf.phaseOffset=rf_phase/180*pi;
55 | adc.phaseOffset=rf_phase/180*pi;
56 | rf_inc=mod(rf_inc+rfSpoilingInc, 360.0);
57 | rf_phase=mod(rf_phase+rf_inc, 360.0);
58 | %
59 | seq.addBlock(rf,gz);
60 | gyPre = mr.makeTrapezoid('y','Area',phaseAreas(i),'Duration',mr.calcDuration(gxPre),'system',sys);
61 | for c=1:length(TE) % loop over TEs
62 | if (c==1)
63 | seq.addBlock(mr.align('left', mr.makeDelay(delayTE(c)),gyPre,gzReph,'right',gxPre));
64 | else
65 | seq.addBlock(mr.align('left', mr.makeDelay(delayTE(c)),'right',gxFlyBack));
66 | end
67 | seq.addBlock(gx,adc);
68 | % to check/debug TE calculation with seq.testReport() comment out
69 | % the above line and uncommend the line below this comment block;
70 | % change 'c==3' statement to select the echo to test
71 | % if c==3, seq.addBlock(gx,adc); else, seq.addBlock(gx); end
72 | end
73 | gyPre.amplitude=-gyPre.amplitude; % better to use mr.scaleGrad(gyPre,-1);
74 | seq.addBlock(mr.makeDelay(delayTR),gxSpoil,gyPre,gzSpoil)
75 | end
76 |
77 | %% check whether the timing of the sequence is correct
78 | [ok, error_report]=seq.checkTiming;
79 |
80 | if (ok)
81 | fprintf('Timing check passed successfully\n');
82 | else
83 | fprintf('Timing check failed! Error listing follows:\n');
84 | fprintf([error_report{:}]);
85 | fprintf('\n');
86 | end
87 |
88 | %% prepare sequence export
89 | seq.setDefinition('FOV', [fov fov sliceThickness]);
90 | seq.setDefinition('Name', 'mgre');
91 | seq.write('mgre.seq') % Write to pulseq file
92 | %seq.install('siemens');
93 |
94 | %% plot sequence and k-space diagrams
95 |
96 | seq.plot('timeRange', [0 5]*TR);
97 |
98 | % k-space trajectory calculation
99 | [ktraj_adc, t_adc, ktraj, t_ktraj, t_excitation, t_refocusing] = seq.calculateKspacePP();
100 |
101 | % plot k-spaces
102 | figure; plot(ktraj(1,:),ktraj(2,:),'b'); % a 2D k-space plot
103 | axis('equal'); % enforce aspect ratio for the correct trajectory display
104 | hold;plot(ktraj_adc(1,:),ktraj_adc(2,:),'r.'); % plot the sampling points
105 | title('full k-space trajectory (k_x x k_y)');
106 |
107 | %% very optional slow step, but useful for testing during development e.g. for the real TE, TR or for staying within slewrate limits
108 | rep = seq.testReport;
109 | fprintf([rep{:}]);
110 |
--------------------------------------------------------------------------------
/11_from_GRE_to_EPI/seq/s03_BipolarMultiGradientEcho.m:
--------------------------------------------------------------------------------
1 | % set system limits
2 | sys = mr.opts('MaxGrad', 28, 'GradUnit', 'mT/m', ...
3 | 'MaxSlew', 150, 'SlewUnit', 'T/m/s', ...
4 | 'rfRingdownTime', 20e-6, 'rfDeadTime', 100e-6, 'adcDeadTime', 10e-6);
5 |
6 | % basic parameters
7 | seq=mr.Sequence(sys); % Create a new sequence object
8 | fov=256e-3; Nx=256; Ny=Nx; % Define FOV and resolution
9 | alpha=10; % flip angle
10 | sliceThickness=3e-3; % slice
11 | TR=21e-3; % TR, a single value
12 | TE=[4 9 15]*1e-3; % give a vector here to have multiple TEs % TODO: reduce TEs
13 | %TODO: play with TEs to make them really minimal
14 |
15 | % more in-depth parameters
16 | rfSpoilingInc=117; % RF spoiling increment
17 | rfDuration=3e-3;
18 | roDuration=3.2e-3; % not all values are possible, watch out for the checkTiming output
19 |
20 | % Create alpha-degree slice selection pulse and corresponding gradients
21 | [rf, gz, gzReph] = mr.makeSincPulse(alpha*pi/180,'Duration',rfDuration,...
22 | 'SliceThickness',sliceThickness,'apodization',0.42,'timeBwProduct',4,'use','excitation','system',sys);
23 |
24 | % Define other gradients and ADC events
25 | deltak=1/fov; % Pulseq default units for k-space are inverse meters
26 | gxp = mr.makeTrapezoid('x','FlatArea',Nx*deltak,'FlatTime',roDuration,'system',sys); % Pulseq default units for gradient amplitudes are 1/Hz
27 | gxm=mr.scaleGrad(gxp,-1);
28 | adc = mr.makeAdc(Nx,'Duration',gxp.flatTime,'Delay',gxp.riseTime,'system',sys);
29 | gxPre = mr.makeTrapezoid('x','Area',-gxp.area/2,'system',sys); % if no 'Duration' is provided shortest possible duration will be used
30 | phaseAreas = ((0:Ny-1)-Ny/2)*deltak;
31 |
32 | % gradient spoiling
33 | if mod(length(TE),2)==0, spSign=-1; else, spSign=1; end
34 | gxSpoil=mr.makeTrapezoid('x','Area',2*Nx*deltak*spSign,'system',sys); % 2 cycles over the voxel size in X
35 | gzSpoil=mr.makeTrapezoid('z','Area',4/sliceThickness,'system',sys); % 4 cycles over the slice thickness
36 |
37 | % Calculate timing (need to decide on the block structure already)
38 | helperT=ceil((gz.fallTime + gz.flatTime/2 + mr.calcDuration(gxp)/2)/seq.gradRasterTime)*seq.gradRasterTime;
39 | delayTE = zeros(size(TE)) ;
40 | for c=1:length(TE)
41 | delayTE(c)=TE(c) - helperT;
42 | helperT=helperT+delayTE(c)+mr.calcDuration(gxp);
43 | end
44 | assert(all(delayTE(1)>=mr.calcDuration(gxPre,gzReph)));
45 | assert(all(delayTE(2:end)>=0));
46 | delayTR=round((TR - mr.calcDuration(gz) - sum(delayTE) ...
47 | - mr.calcDuration(gxp)*length(TE))/seq.gradRasterTime)*seq.gradRasterTime;
48 | assert(all(delayTR>=mr.calcDuration(gxSpoil,gzSpoil)));
49 |
50 | % initialize the RF spoling counters
51 | rf_phase=0;
52 | rf_inc=0;
53 |
54 | % define sequence blocks
55 | for i=1:Ny % loop over phase encodes
56 | rf.phaseOffset=rf_phase/180*pi;
57 | adc.phaseOffset=rf_phase/180*pi;
58 | rf_inc=mod(rf_inc+rfSpoilingInc, 360.0);
59 | rf_phase=mod(rf_phase+rf_inc, 360.0);
60 | %
61 | seq.addBlock(rf,gz);
62 | gyPre = mr.makeTrapezoid('y','Area',phaseAreas(i),'Duration',mr.calcDuration(gxPre),'system',sys);
63 | for c=1:length(TE) % loop over TEs
64 | if (c==1)
65 | seq.addBlock(mr.align('left', mr.makeDelay(delayTE(c)),gyPre,gzReph,'right',gxPre));
66 | else
67 | seq.addBlock(mr.makeDelay(delayTE(c)));
68 | end
69 | if mod(c,2)==0, gx=gxm; else, gx=gxp; end
70 | seq.addBlock(gx,adc);
71 | % to check/debug TE calculation with seq.testReport() comment out
72 | % the above line and uncommend the line below this comment block;
73 | % change 'c==3' statement to select the echo to test
74 | %if c==2, seq.addBlock(gx,adc); else, seq.addBlock(gx); end
75 | end
76 | gyPre.amplitude=-gyPre.amplitude; % better to use mr.scaleGrad(gyPre,-1);
77 | seq.addBlock(mr.makeDelay(delayTR),gxSpoil,gyPre,gzSpoil)
78 | end
79 |
80 | %% check whether the timing of the sequence is correct
81 | [ok, error_report]=seq.checkTiming;
82 |
83 | if (ok)
84 | fprintf('Timing check passed successfully\n');
85 | else
86 | fprintf('Timing check failed! Error listing follows:\n');
87 | fprintf([error_report{:}]);
88 | fprintf('\n');
89 | end
90 |
91 | %% prepare sequence export
92 | seq.setDefinition('FOV', [fov fov sliceThickness]);
93 | seq.setDefinition('Name', 'bmgre');
94 | seq.write('bmgre.seq') % Write to pulseq file
95 | %seq.install('siemens');
96 |
97 | %% plot sequence and k-space diagrams
98 |
99 | seq.plot('timeRange', [0 5]*TR);
100 |
101 | % k-space trajectory calculation
102 | [ktraj_adc, t_adc, ktraj, t_ktraj, t_excitation, t_refocusing] = seq.calculateKspacePP();
103 |
104 | % plot k-spaces
105 | figure; plot(ktraj(1,:),ktraj(2,:),'b'); % a 2D k-space plot
106 | axis('equal'); % enforce aspect ratio for the correct trajectory display
107 | hold;plot(ktraj_adc(1,:),ktraj_adc(2,:),'r.'); % plot the sampling points
108 | title('full k-space trajectory (k_x x k_y)');
109 |
110 | %% very optional slow step, but useful for testing during development e.g. for the real TE, TR or for staying within slewrate limits
111 | rep = seq.testReport;
112 | fprintf([rep{:}]);
113 |
--------------------------------------------------------------------------------
/11_from_GRE_to_EPI/seq/s04a_SegmentedGradientEcho.m:
--------------------------------------------------------------------------------
1 | % set system limits
2 | sys = mr.opts('MaxGrad', 28, 'GradUnit', 'mT/m', ...
3 | 'MaxSlew', 150, 'SlewUnit', 'T/m/s', ...
4 | 'rfRingdownTime', 20e-6, 'rfDeadTime', 100e-6, 'adcDeadTime', 10e-6);
5 |
6 | % basic parameters
7 | seq=mr.Sequence(sys); % Create a new sequence object
8 | fov=256e-3; Nx=128; Ny=Nx; % Define FOV and resolution
9 | alpha=10; % flip angle
10 | sliceThickness=3e-3; % slice
11 | TR=21e-3; % TR, a single value
12 | TE=9*1e-3; % only a single TE is accepted now
13 | nSeg=5; % number of segments
14 | %TODO: lower matrix, increase segments, shorten RO (128x128, 5, 640us)
15 |
16 | % more in-depth parameters
17 | rfSpoilingInc=117; % RF spoiling increment
18 | rfDuration=3e-3;
19 | roDuration=640e-6; % not all values are possible, watch out for the checkTiming output
20 |
21 | % Create alpha-degree slice selection pulse and corresponding gradients
22 | [rf, gz, gzReph] = mr.makeSincPulse(alpha*pi/180,'Duration',rfDuration,...
23 | 'SliceThickness',sliceThickness,'apodization',0.42,'timeBwProduct',4,'use','excitation','system',sys);
24 |
25 | % Define other gradients and ADC events
26 | deltak=1/fov; % Pulseq default units for k-space are inverse meters
27 | gxp = mr.makeTrapezoid('x','FlatArea',Nx*deltak,'FlatTime',roDuration,'system',sys); % Pulseq default units for gradient amplitudes are 1/Hz
28 | gxm=mr.scaleGrad(gxp,-1);
29 | adc = mr.makeAdc(Nx,'Duration',gxp.flatTime,'Delay',gxp.riseTime,'system',sys);
30 | gxPre = mr.makeTrapezoid('x','Area',-gxp.area/2,'system',sys); % if no 'Duration' is provided shortest possible duration will be used
31 |
32 | % with segmentation it gets trickier
33 | if mod(nSeg,2)==0, warning('for even number of segments additional steps are required to avoid the segment edge hitting the k-space center, expect artifacts...'); end % the code will work but the images are likely to be affected by the discontinuity at the center of k-space
34 | phaseAreas = ((0:floor(Ny/nSeg)-1)-Ny/2)*deltak;
35 |
36 | % calculate the blip gradient
37 | gyBlip = mr.makeTrapezoid('y','Area',floor(Ny/nSeg)*deltak,'Delay',gxp.riseTime+gxp.flatTime,'system',sys);
38 |
39 | % gradient spoiling
40 | if mod(length(TE),2)==0, spSign=-1; else, spSign=1; end
41 | gxSpoil=mr.makeTrapezoid('x','Area',2*Nx*deltak*spSign,'system',sys); % 2 cycles over the voxel size in X
42 | gzSpoil=mr.makeTrapezoid('z','Area',4/sliceThickness,'system',sys); % 4 cycles over the slice thickness
43 |
44 | % Calculate timing (need to decide on the block structure already)
45 | delayTE=TE - ceil((gz.fallTime + gz.flatTime/2 + floor(nSeg/2)*mr.calcDuration(gxp,gyBlip)+mr.calcDuration(gxp)/2)/seq.gradRasterTime)*seq.gradRasterTime;
46 | assert(all(delayTE>=mr.calcDuration(gxPre,gzReph)));
47 | delayTR=round((TR - mr.calcDuration(gz) - delayTE ...
48 | - mr.calcDuration(gxp,gyBlip)*(nSeg-1)-mr.calcDuration(gxp))/seq.gradRasterTime)*seq.gradRasterTime;
49 | assert(all(delayTR>=mr.calcDuration(gxSpoil,gzSpoil)));
50 |
51 | % initialize the RF spoling counters
52 | rf_phase=0;
53 | rf_inc=0;
54 |
55 | % define sequence blocks
56 | for i=1:length(phaseAreas) % loop over phase encodes
57 | rf.phaseOffset=rf_phase/180*pi;
58 | adc.phaseOffset=rf_phase/180*pi;
59 | rf_inc=mod(rf_inc+rfSpoilingInc, 360.0);
60 | rf_phase=mod(rf_phase+rf_inc, 360.0);
61 | %
62 | seq.addBlock(rf,gz);
63 | gyPre = mr.makeTrapezoid('y','Area',phaseAreas(i),'Duration',mr.calcDuration(gxPre),'system',sys);
64 | seq.addBlock(mr.align('left', mr.makeDelay(delayTE),gyPre,gzReph,'right',gxPre));
65 | for s=1:nSeg % loop over segments
66 | if mod(s,2)==0, gx=gxm; else, gx=gxp; end
67 | if s~=nSeg
68 | seq.addBlock(gx,adc,gyBlip);
69 | else
70 | seq.addBlock(gx,adc);
71 | end
72 | end
73 | gyPost = mr.makeTrapezoid('y','Area',-phaseAreas(i)-gyBlip.area*(nSeg-1),'Duration',mr.calcDuration(gxPre),'system',sys);
74 | seq.addBlock(mr.makeDelay(delayTR),gxSpoil,gyPost,gzSpoil)
75 | end
76 |
77 | %% check whether the timing of the sequence is correct
78 | [ok, error_report]=seq.checkTiming;
79 |
80 | if (ok)
81 | fprintf('Timing check passed successfully\n');
82 | else
83 | fprintf('Timing check failed! Error listing follows:\n');
84 | fprintf([error_report{:}]);
85 | fprintf('\n');
86 | end
87 |
88 | %% prepare sequence export
89 | seq.setDefinition('FOV', [fov fov sliceThickness]);
90 | seq.setDefinition('Name', 'seg-gre');
91 | seq.write('seg-gre.seq') % Write to pulseq file
92 | %seq.install('siemens');
93 |
94 | %% plot sequence and k-space diagrams
95 |
96 | seq.plot('timeRange', [0 2]*TR);
97 | seq.plot('timeDisp','us','showBlocks',1,'timeRange',[0 2]*TR); %detailed view
98 |
99 | % k-space trajectory calculation
100 | [ktraj_adc, t_adc, ktraj, t_ktraj, t_excitation, t_refocusing] = seq.calculateKspacePP();
101 |
102 | % plot k-spaces
103 | figure; plot(ktraj(1,:),ktraj(2,:),'b'); % a 2D k-space plot
104 | axis('equal'); % enforce aspect ratio for the correct trajectory display
105 | hold;plot(ktraj_adc(1,:),ktraj_adc(2,:),'r.'); % plot the sampling points
106 | title('full k-space trajectory (k_x x k_y)');
107 |
108 | %% very optional slow step, but useful for testing during development e.g. for the real TE, TR or for staying within slewrate limits
109 | rep = seq.testReport;
110 | fprintf([rep{:}]);
111 |
--------------------------------------------------------------------------------
/11_from_GRE_to_EPI/seq/s04b_SegmentedGradientEcho.m:
--------------------------------------------------------------------------------
1 | % set system limits
2 | sys = mr.opts('MaxGrad', 28, 'GradUnit', 'mT/m', ...
3 | 'MaxSlew', 150, 'SlewUnit', 'T/m/s', ...
4 | 'rfRingdownTime', 20e-6, 'rfDeadTime', 100e-6, 'adcDeadTime', 10e-6);
5 |
6 | % basic parameters
7 | seq=mr.Sequence(sys); % Create a new sequence object
8 | fov=256e-3; Nx=128; Ny=128; % Define FOV and resolution
9 | alpha=10; % flip angle
10 | sliceThickness=3e-3; % slice
11 | TR=30e-3; % TR, a single value
12 | TE=20e-3; % only a single TE is accepted now
13 | nSeg=5; % number of segments
14 | %TODO: increase nSeq, (remember to increase TE), see how we hit the "blip limit"
15 |
16 | % more in-depth parameters
17 | rfSpoilingInc=117; % RF spoiling increment
18 | rfDuration=3e-3;
19 | roDuration=640e-6; % not all values are possible, watch out for the checkTiming output
20 |
21 | % Create alpha-degree slice selection pulse and corresponding gradients
22 | [rf, gz, gzReph] = mr.makeSincPulse(alpha*pi/180,'Duration',rfDuration,...
23 | 'SliceThickness',sliceThickness,'apodization',0.42,'timeBwProduct',4,'use','excitation','system',sys);
24 |
25 | % Define other gradients and ADC events
26 | deltak=1/fov; % Pulseq default units for k-space are inverse meters
27 | gxp = mr.makeTrapezoid('x','FlatArea',Nx*deltak,'FlatTime',roDuration,'system',sys); % Pulseq default units for gradient amplitudes are 1/Hz
28 | gxm=mr.scaleGrad(gxp,-1);
29 | adc = mr.makeAdc(Nx,'Duration',gxp.flatTime,'Delay',gxp.riseTime,'system',sys);
30 | gxPre = mr.makeTrapezoid('x','Area',-gxp.area/2,'system',sys); % if no 'Duration' is provided shortest possible duration will be used
31 |
32 | % with segmentation it gets trickier
33 | if mod(nSeg,2)==0, warning('for even number of segments additional steps are required to avoid the segment edge hitting the k-space center, expect artifacts...'); end % the code will work but the images are likely to be affected by the discontinuity at the center of k-space
34 | phaseAreas = ((0:floor(Ny/nSeg)-1)-Ny/2)*deltak;
35 |
36 | % calculate the blip gradient
37 | gyBlip = mr.makeTrapezoid('y','Area',floor(Ny/nSeg)*deltak,'Delay',gxp.riseTime+gxp.flatTime,'system',sys);
38 | if mr.calcDuration(gyBlip)-mr.calcDuration(gxp)gxp.riseTime
49 | % need to update the copy to allow for the split gradient to ramp down
50 | gxp.delay=mr.calcDuration(gyBlip_parts(2))-gxp.riseTime;
51 | gxm.delay=mr.calcDuration(gyBlip_parts(2))-gxm.riseTime;
52 | adc.delay=adc.delay+gxp.delay;
53 | gyBlip_part_tmp.delay=gyBlip_part_tmp.delay+gxp.delay;
54 | end
55 | % now for inner echos create a special gy gradient, that will ramp down to 0, stay at 0 for a while and ramp up again
56 | gyBlip_down_up=mr.addGradients({gyBlip_parts(2), gyBlip_part_tmp}, sys); % QC: gyBlip_parts(2) is inserted before gxp. gyBlip_part_tmp with a long delay is inserted after gxp.
57 | % copy for readability
58 | gyBlip_up=gyBlip_parts(1);
59 | gyBlip_down=gyBlip_parts(2);
60 |
61 | % gradient spoiling
62 | if mod(length(TE),2)==0, spSign=-1; else, spSign=1; end
63 | gxSpoil=mr.makeTrapezoid('x','Area',2*Nx*deltak*spSign,'system',sys); % 2 cycles over the voxel size in X
64 | gzSpoil=mr.makeTrapezoid('z','Area',4/sliceThickness,'system',sys); % 4 cycles over the slice thickness
65 |
66 | % Calculate timing (need to decide on the block structure already)
67 | delayTE=TE - ceil((gz.fallTime + gz.flatTime/2 + nSeg*mr.calcDuration(gxp0)/2 + floor((nSeg-1)/2)*gxp.delay)/seq.gradRasterTime)*seq.gradRasterTime;
68 | assert(all(delayTE>=mr.calcDuration(gxPre,gzReph)));
69 | delayTR=round((TR - mr.calcDuration(gz) - delayTE ...
70 | - nSeg*mr.calcDuration(gxp0) - floor((nSeg-1)/2)*gxp.delay)/seq.gradRasterTime)*seq.gradRasterTime;
71 | assert(all(delayTR>=mr.calcDuration(gxSpoil,gzSpoil)));
72 |
73 | % initialize the RF spoling counters
74 | rf_phase=0;
75 | rf_inc=0;
76 |
77 | % define sequence blocks
78 | for i=1:length(phaseAreas) % loop over phase encodes
79 | rf.phaseOffset=rf_phase/180*pi;
80 | adc.phaseOffset=rf_phase/180*pi;
81 | adc0.phaseOffset=rf_phase/180*pi;
82 | rf_inc=mod(rf_inc+rfSpoilingInc, 360.0);
83 | rf_phase=mod(rf_phase+rf_inc, 360.0);
84 | %
85 | seq.addBlock(rf,gz);
86 | gyPre = mr.makeTrapezoid('y','Area',phaseAreas(i),'Duration',mr.calcDuration(gxPre),'system',sys);
87 | seq.addBlock(mr.align('left', mr.makeDelay(delayTE),gyPre,gzReph,'right',gxPre));
88 | for s=1:nSeg % loop over segments
89 | if s==1
90 | seq.addBlock(gxp0,adc0,gyBlip_up);
91 | else
92 | if mod(s,2)==0, gx=gxm; else, gx=gxp; end
93 | if s~=nSeg
94 | seq.addBlock(gx,adc,gyBlip_down_up);
95 | else
96 | seq.addBlock(gx,adc,gyBlip_down);
97 | end
98 | end
99 | end
100 | gyPost = mr.makeTrapezoid('y','Area',-phaseAreas(i)-gyBlip.area*(nSeg-1),'Duration',mr.calcDuration(gxPre),'system',sys);
101 | seq.addBlock(mr.makeDelay(delayTR),gxSpoil,gyPost,gzSpoil)
102 | end
103 |
104 | %% check whether the timing of the sequence is correct
105 | [ok, error_report]=seq.checkTiming;
106 |
107 | if (ok)
108 | fprintf('Timing check passed successfully\n');
109 | else
110 | fprintf('Timing check failed! Error listing follows:\n');
111 | fprintf([error_report{:}]);
112 | fprintf('\n');
113 | end
114 |
115 | %% prepare sequence export
116 | seq.setDefinition('FOV', [fov fov sliceThickness]);
117 | seq.setDefinition('Name', 'seg-gre');
118 | seq.write('seg-gre.seq') % Write to pulseq file
119 | %seq.install('siemens');
120 |
121 | %% plot sequence and k-space diagrams
122 |
123 | %seq.plot('timeRange', [0 2]*TR);
124 | seq.plot('timeDisp','us','showBlocks',1,'timeRange',[0 2]*TR); %detailed view
125 |
126 | % k-space trajectory calculation
127 | [ktraj_adc, t_adc, ktraj, t_ktraj, t_excitation, t_refocusing] = seq.calculateKspacePP();
128 |
129 | % plot k-spaces
130 | figure; plot(ktraj(1,:),ktraj(2,:),'b'); % a 2D k-space plot
131 | axis('equal'); % enforce aspect ratio for the correct trajectory display
132 | hold;plot(ktraj_adc(1,:),ktraj_adc(2,:),'r.'); % plot the sampling points
133 | title('full k-space trajectory (k_x x k_y)');
134 |
135 | %% very optional slow step, but useful for testing during development e.g. for the real TE, TR or for staying within slewrate limits
136 | rep = seq.testReport;
137 | fprintf([rep{:}]);
138 |
--------------------------------------------------------------------------------
/11_from_GRE_to_EPI/seq/s04c_SegmentedGradientEcho.m:
--------------------------------------------------------------------------------
1 | % set system limits
2 | sys = mr.opts('MaxGrad', 28, 'GradUnit', 'mT/m', ...
3 | 'MaxSlew', 150, 'SlewUnit', 'T/m/s', ...
4 | 'rfRingdownTime', 20e-6, 'rfDeadTime', 100e-6, 'adcDeadTime', 10e-6);
5 |
6 | % basic parameters
7 | seq=mr.Sequence(sys); % Create a new sequence object
8 | fov=256e-3; Nx=128; Ny=Nx; % Define FOV and resolution
9 | alpha=90; % flip angle
10 | sliceThickness=3e-3; % slice
11 | TR=130e-3; % TR, a single value
12 | TE=61e-3; % only a single TE is accepted now
13 | nSeg=Ny; % number of segments, noy equals to the number of PE lines
14 | % run this improvised EPI sequence with and without PE, have a look a the k-space
15 |
16 | % more in-depth parameters
17 | rfSpoilingInc=117; % RF spoiling increment
18 | rfDuration=3e-3;
19 | roDuration=640e-6; % not all values are possible, watch out for the checkTiming output
20 |
21 | % Create alpha-degree slice selection pulse and corresponding gradients
22 | [rf, gz, gzReph] = mr.makeSincPulse(alpha*pi/180,'Duration',rfDuration,...
23 | 'SliceThickness',sliceThickness,'apodization',0.42,'timeBwProduct',4,'use','excitation','system',sys);
24 |
25 | % Define other gradients and ADC events
26 | deltak=1/fov; % Pulseq default units for k-space are inverse meters
27 | gxp = mr.makeTrapezoid('x','FlatArea',Nx*deltak,'FlatTime',roDuration,'system',sys); % Pulseq default units for gradient amplitudes are 1/Hz
28 | gxm=mr.scaleGrad(gxp,-1);
29 | adc = mr.makeAdc(Nx,'Duration',gxp.flatTime,'Delay',gxp.riseTime,'system',sys);
30 | gxPre = mr.makeTrapezoid('x','Area',-gxp.area/2,'system',sys); % if no 'Duration' is provided shortest possible duration will be used
31 |
32 | % with segmentation it gets trickier
33 | if mod(nSeg,2)==0, warning('for even number of segments additional steps are required to avoid the segment edge hitting the k-space center, expect artifacts...'); end % the code will work but the images are likely to be affected by the discontinuity at the center of k-space
34 | phaseAreas = ((0:floor(Ny/nSeg)-1)-Ny/2)*deltak;
35 |
36 | % calculate the blip gradient
37 | gyBlip = mr.makeTrapezoid('y','Area',floor(Ny/nSeg)*deltak,'Delay',gxp.riseTime+gxp.flatTime,'system',sys);
38 | if mr.calcDuration(gyBlip)-mr.calcDuration(gxp)gxp.riseTime
48 | % need to update the copy to allow for the split gradient to ramp down
49 | gxp.delay=mr.calcDuration(gyBlip_parts(2))-gxp.riseTime;
50 | gxm.delay=mr.calcDuration(gyBlip_parts(2))-gxm.riseTime;
51 | adc.delay=adc.delay+gxp.delay;
52 | gyBlip_part_tmp.delay=gyBlip_part_tmp.delay+gxp.delay;
53 | end
54 | % now for inner echos create a special gy gradient, that will ramp down to 0, stay at 0 for a while and ramp up again
55 | gyBlip_down_up=mr.addGradients({gyBlip_parts(2), gyBlip_part_tmp}, sys);
56 | % copy for readability
57 | gyBlip_up=gyBlip_parts(1);
58 | gyBlip_down=gyBlip_parts(2);
59 |
60 | % gradient spoiling
61 | if mod(length(TE),2)==0, spSign=-1; else, spSign=1; end
62 | gxSpoil=mr.makeTrapezoid('x','Area',2*Nx*deltak*spSign,'system',sys); % 2 cycles over the voxel size in X
63 | gzSpoil=mr.makeTrapezoid('z','Area',4/sliceThickness,'system',sys); % 4 cycles over the slice thickness
64 |
65 | % Calculate timing (need to decide on the block structure already)
66 | delayTE=TE - ceil((gz.fallTime + gz.flatTime/2 + (floor(nSeg/2)+0.5)*mr.calcDuration(gxp0) + floor((nSeg-1)/2)*gxp.delay)/seq.gradRasterTime)*seq.gradRasterTime;
67 | assert(all(delayTE>=mr.calcDuration(gxPre,gzReph)));
68 | delayTR=round((TR - mr.calcDuration(gz) - delayTE ...
69 | - nSeg*mr.calcDuration(gxp0) - floor((nSeg-1)/2)*gxp.delay)/seq.gradRasterTime)*seq.gradRasterTime;
70 | assert(all(delayTR>=mr.calcDuration(gxSpoil,gzSpoil)));
71 |
72 | % initialize the RF spoling counters
73 | rf_phase=0;
74 | rf_inc=0;
75 |
76 | % define sequence blocks
77 | for i=1:length(phaseAreas) % loop over phase encodes
78 | rf.phaseOffset=rf_phase/180*pi;
79 | adc.phaseOffset=rf_phase/180*pi;
80 | adc0.phaseOffset=rf_phase/180*pi;
81 | rf_inc=mod(rf_inc+rfSpoilingInc, 360.0);
82 | rf_phase=mod(rf_phase+rf_inc, 360.0);
83 | %
84 | seq.addBlock(rf,gz);
85 | gyPre = mr.makeTrapezoid('y','Area',phaseAreas(i),'Duration',mr.calcDuration(gxPre),'system',sys);
86 | seq.addBlock(mr.align('left', mr.makeDelay(delayTE),gyPre,gzReph,'right',gxPre));
87 | for s=1:nSeg % loop over segments
88 | if s==1
89 | seq.addBlock(gxp0,adc0,gyBlip_up);
90 | else
91 | if mod(s,2)==0, gx=gxm; else, gx=gxp; end
92 | if s~=nSeg
93 | seq.addBlock(gx,adc,gyBlip_down_up);
94 | else
95 | seq.addBlock(gx,adc,gyBlip_down);
96 | end
97 | end
98 | end
99 | gyPost = mr.makeTrapezoid('y','Area',-phaseAreas(i)-gyBlip.area*(nSeg-1),'Duration',mr.calcDuration(gxPre),'system',sys);
100 | seq.addBlock(mr.makeDelay(delayTR),gxSpoil,gyPost,gzSpoil)
101 | end
102 |
103 | %% check whether the timing of the sequence is correct
104 | [ok, error_report]=seq.checkTiming;
105 |
106 | if (ok)
107 | fprintf('Timing check passed successfully\n');
108 | else
109 | fprintf('Timing check failed! Error listing follows:\n');
110 | fprintf([error_report{:}]);
111 | fprintf('\n');
112 | end
113 |
114 | %% prepare sequence export
115 | seq.setDefinition('FOV', [fov fov sliceThickness]);
116 | seq.setDefinition('Name', 'seg-gre');
117 | seq.write('seg-gre.seq') % Write to pulseq file
118 | %seq.install('siemens');
119 |
120 | %% plot sequence and k-space diagrams
121 |
122 | %seq.plot('timeRange', [0 2]*TR);
123 | seq.plot('timeDisp','us','showBlocks',1,'timeRange',[0 2]*TR); %detailed view
124 |
125 | % k-space trajectory calculation
126 | [ktraj_adc, t_adc, ktraj, t_ktraj, t_excitation, t_refocusing] = seq.calculateKspacePP();
127 |
128 | % plot k-spaces
129 | figure; plot(ktraj(1,:),ktraj(2,:),'b'); % a 2D k-space plot
130 | axis('equal'); % enforce aspect ratio for the correct trajectory display
131 | hold;plot(ktraj_adc(1,:),ktraj_adc(2,:),'r.'); % plot the sampling points
132 | title('full k-space trajectory (k_x x k_y)');
133 |
134 | %% very optional slow step, but useful for testing during development e.g. for the real TE, TR or for staying within slewrate limits
135 | rep = seq.testReport;
136 | fprintf([rep{:}]);
137 |
--------------------------------------------------------------------------------
/11_from_GRE_to_EPI/seq/s05_EchoPlanarImaging.m:
--------------------------------------------------------------------------------
1 | % set system limits
2 | sys = mr.opts('MaxGrad', 28, 'GradUnit', 'mT/m', ...
3 | 'MaxSlew', 150, 'SlewUnit', 'T/m/s', ...
4 | 'rfRingdownTime', 20e-6, 'rfDeadTime', 100e-6, 'adcDeadTime', 10e-6);
5 |
6 | % basic parameters
7 | seq=mr.Sequence(sys); % Create a new sequence object
8 | fov=256e-3; Nx=128; Ny=Nx; % Define FOV and resolution
9 | alpha=90; % flip angle
10 | sliceThickness=3e-3; % slice
11 | %TR=21e-3; % ignore TR, go as fast as possible
12 | %TE=60e-3; % ignore TE, go as fast as possible
13 | % TODO: run this EPI sequence with and without PE, to calibrate the delay
14 | % TODO: change MaxGrad/MaxSlew/roDuration to see what happends to the
15 | % stimulation (e.g. 80/200/500)
16 |
17 | % more in-depth parameters
18 | pe_enable=1; % a flag to quickly disable phase encoding (1/0) as needed for the delay calibration
19 | rfDuration=3e-3;
20 | roDuration=640e-6; % not all values are possible, watch out for the checkTiming output
21 |
22 | % Create alpha-degree slice selection pulse and corresponding gradients
23 | [rf, gz, gzReph] = mr.makeSincPulse(alpha*pi/180,'Duration',rfDuration,...
24 | 'SliceThickness',sliceThickness,'apodization',0.42,'timeBwProduct',4,'use','excitation','system',sys);
25 |
26 | % define the output trigger to play out with every slice excitation
27 | trig=mr.makeDigitalOutputPulse('ext1','duration', 100e-6,'delay', rf.delay+mr.calcRfCenter(rf)); % possible channels: 'osc0','osc1','ext1'
28 |
29 | % Define other gradients and ADC events
30 | deltak=1/fov; % Pulseq default units for k-space are inverse meters
31 | kWidth = Nx*deltak;
32 |
33 | % start with the blip
34 | blip_dur = ceil(2*sqrt(deltak/sys.maxSlew)/sys.gradRasterTime/2)*sys.gradRasterTime*2; % we round-up the duration to 2x the gradient raster time
35 | gyBlip = mr.makeTrapezoid('y',sys,'Area',-deltak,'Duration',blip_dur); % we use negative blips to save one k-space line on our way towards the k-space center
36 |
37 | % readout gradient is a truncated trapezoid with dead times at the beginnig
38 | % and at the end, each equal to a half of blip_dur
39 | % the area between the blips should be equal to kWidth
40 | % we do a two-step calculation: we first increase the area assuming maximum
41 | % slew rate and then scale down the amlitude to fix the area
42 | extra_area=blip_dur/2*blip_dur/2*sys.maxSlew;
43 | gx = mr.makeTrapezoid('x',sys,'Area',kWidth+extra_area,'duration',roDuration+blip_dur);
44 | actual_area=gx.area-gx.amplitude/gx.riseTime*blip_dur/2*blip_dur/2/2-gx.amplitude/gx.fallTime*blip_dur/2*blip_dur/2/2;
45 | gx=mr.scaleGrad(gx,kWidth/actual_area);
46 | %adc = mr.makeAdc(Nx,'Duration',roDurtion,'Delay',blip_dur/2,'system',sys);
47 | gxPre = mr.makeTrapezoid('x','Area',-gx.area/2,'system',sys); % if no 'Duration' is provided shortest possible duration will be used
48 | gyPre = mr.makeTrapezoid('y','Area',(Ny/2-1)*deltak,'system',sys);
49 |
50 | % calculate ADC - it is quite trickly
51 | % we use ramp sampling, so we have to calculate the dwell time and the
52 | % number of samples, which are will be quite different from Nx and
53 | % readoutTime/Nx, respectively.
54 | adcDwellNyquist=deltak/gx.amplitude; % dwell time on the top of the plato
55 | % round-down dwell time to sys.adcRasterTime (100 ns)
56 | adcDwell=floor(adcDwellNyquist/sys.adcRasterTime)*sys.adcRasterTime;
57 | adcSamples=floor(roDuration/adcDwell/4)*4; % on Siemens the number of ADC samples need to be divisible by 4
58 | adc = mr.makeAdc(adcSamples,'Dwell',adcDwell);
59 | % realign the ADC with respect to the gradient
60 | time_to_center=adc.dwell*((adcSamples-1)/2+0.5); % Pulseq (and Siemens) define the samples to happen in the center of the dwell period
61 | adc.delay=round((gx.riseTime+gx.flatTime/2-time_to_center)/sys.rfRasterTime)*sys.rfRasterTime;
62 | % above we adjust the delay to align the trajectory with the gradient.
63 | % We have to aligh the delay to seq.rfRasterTime (1us)
64 | % this rounding actually makes the sampling points on odd and even readouts
65 | % to appear misalligned. However, on the real hardware this misalignment is
66 | % much stronger anyways due to the grdient delays
67 |
68 | % finish the blip gradient calculation
69 | % split the blip into two halves and produce a combined synthetic gradient
70 | gyBlip_parts = mr.splitGradientAt(gyBlip, blip_dur/2, sys);
71 | [gyBlip_up,gyBlip_down,~]=mr.align('right',gyBlip_parts(1),'left',gyBlip_parts(2),gx);
72 | % now for inner echos create a special gy gradient, that will ramp down to 0, stay at 0 for a while and ramp up again
73 | gyBlip_down_up=mr.addGradients({gyBlip_down, gyBlip_up}, sys);
74 |
75 | % pe_enable support
76 | gyBlip_up=mr.scaleGrad(gyBlip_up,pe_enable);
77 | gyBlip_down=mr.scaleGrad(gyBlip_down,pe_enable);
78 | gyBlip_down_up=mr.scaleGrad(gyBlip_down_up,pe_enable);
79 | gyPre=mr.scaleGrad(gyPre,pe_enable);
80 |
81 | % gradient spoiling
82 | gzSpoil=mr.makeTrapezoid('z','Area',4/sliceThickness,'system',sys); % 4 cycles over the slice thickness
83 |
84 | % skip timing (TE/TR calculation), we'll accept the shortest TE/TR
85 |
86 | % define sequence blocks
87 | seq.addBlock(rf,gz,trig);
88 | seq.addBlock(mr.align('left',gyPre,gzReph,'right',gxPre));
89 | for i=1:Ny % loop over phase encodes
90 | if i==1
91 | seq.addBlock(gx,gyBlip_up,adc); % Read the first line of k-space with a single half-blip at the end
92 | elseif i==Ny
93 | seq.addBlock(gx,gyBlip_down,adc); % Read the last line of k-space with a single half-blip at the beginning
94 | else
95 | seq.addBlock(gx,gyBlip_down_up,adc); % Read an intermediate line of k-space with a half-blip at the beginning and a half-blip at the end
96 | end
97 | gx = mr.scaleGrad(gx,-1); % Reverse polarity of read gradient),'Duration',mr.calcDuration(gxPre),'system',sys);
98 | end
99 | seq.addBlock(gzSpoil);
100 |
101 | %% check whether the timing of the sequence is correct
102 | [ok, error_report]=seq.checkTiming;
103 |
104 | if (ok)
105 | fprintf('Timing check passed successfully\n');
106 | else
107 | fprintf('Timing check failed! Error listing follows:\n');
108 | fprintf([error_report{:}]);
109 | fprintf('\n');
110 | end
111 |
112 | %% prepare sequence export
113 | seq.setDefinition('FOV', [fov fov sliceThickness]);
114 | seq.setDefinition('Name', 'epi');
115 | seq.write('epi.seq') % Write to pulseq file
116 | %seq.install('siemens');
117 |
118 | %% plot sequence and k-space diagrams
119 |
120 | %seq.plot();
121 | seq.plot('timeDisp','us','showBlocks',1); %detailed view
122 |
123 | % k-space trajectory calculation
124 | [ktraj_adc, t_adc, ktraj, t_ktraj, t_excitation, t_refocusing] = seq.calculateKspacePP();
125 |
126 | % plot k-spaces
127 | figure; plot(ktraj(1,:),ktraj(2,:),'b'); % a 2D k-space plot
128 | axis('equal'); % enforce aspect ratio for the correct trajectory display
129 | hold;plot(ktraj_adc(1,:),ktraj_adc(2,:),'r.'); % plot the sampling points
130 | title('full k-space trajectory (k_x x k_y)');
131 |
132 | % nice plot for a paper (looks nicer if you reduce the number of phase encoding steps)
133 | seq.paperPlot();
134 |
135 | %% PNS calc
136 |
137 | [pns_ok, pns_n, pns_c, tpns]=seq.calcPNS('~/range_software/pulseq/matlab/idea/asc/MP_GPA_K2309_2250V_951A_AS82.asc'); % prisma
138 | %[pns_ok, pns_n, pns_c, tpns]=seq.calcPNS('idea/asc/MP_GPA_K2309_2250V_951A_GC98SQ.asc'); % aera-xq
139 | %[pns_ok, pns_n, pns_c, tpns]=seq.calcPNS('idea/asc/MP_GPA_K2298_2250V_793A_SC72CD_EGA.asc'); % TERRA-XR
140 |
141 | if (pns_ok)
142 | fprintf('PNS check passed successfully\n');
143 | else
144 | fprintf('PNS check failed! The sequence will probably be stopped by the Gradient Watchdog\n');
145 | end
146 |
147 | %% very optional slow step, but useful for testing during development e.g. for the real TE, TR or for staying within slewrate limits
148 | rep = seq.testReport;
149 | fprintf([rep{:}]);
150 |
--------------------------------------------------------------------------------
/12_Radial_and_nonCartesian/README.md:
--------------------------------------------------------------------------------
1 | # Pulseq tutorial "Radial and Non-Cartesian"
2 |
3 | Welcome to the "Radial and Non-Cartesian" tutorial repository! This was initially developed as a part of a live demo at the virtual ISMRM meeting in 2020 and was extended somewhat further thereafter.
4 |
5 | This tutorial presents two simple radial sequences that are derived from the corresponding Cartesian counterparts. Additionally, a fast radial GRE sequence is presented, and a very basic 2D spiral is introduced. The slide deck entitled [12_Radial_and_nonCartesian.pdf](./doc/12_Radial_and_nonCartesian.pdf) shows sequence diagrams of all steps and visualises the changes at each step.
6 |
7 | ***s01\_CartesianSE***
8 |
9 | ***s01*** describes a 2D slice-selective SE sequence with 7 blocks in
10 | each TR. Note that the polarity of the gy gradient in deltaTR (that is
11 | the polarity of both phase encoding and rephrasing gradients) remains
12 | unchanged due to the 180° refocusing RF pulse.
13 |
14 | ***s02\_RadialSE***
15 |
16 | ***s02*** describes a 2D slice-selective radial sequence, built based on
17 | ***s01*** using the mr.rotate function. The mr.rotate function rotates
18 | the readout gradient and its pre-phaser around the *z*-axis to a certain
19 | angle, which projects the gradient into the *x*- and *y*-axis and thus
20 | produces two components.
21 |
22 | ***s03\_CartesianGradientEcho***
23 |
24 | ***s03*** describes a 2D slice-selective Cartesian GRE sequence, similar
25 | as ***s01*** from the Tutorial 11\_from\_GRE\_to\_EPI.
26 |
27 | ***s04\_RadialGradientEcho***
28 |
29 | ***s04*** describes a 2D slice-selective radial GRE sequence built based
30 | on ***s03***.
31 |
32 | ***s05\_FastRadialGradientEcho***
33 |
34 | ***s05*** describes a 2D slice-selective radial GRE sequence with the
35 | shortest timing. The mr.addGradients function combines slice-selective
36 | gradient and slice-refocusing gradient into a single "ExtendedTrapezoid"
37 | gradient. The mr.align function is used to position the gxPre gradient
38 | right after the RF pulse and before the end of the slice-refocusing
39 | gradient. The flat time of the readout gradient is extended for
40 | spoiling. The whole sequence has a total of 2 blocks per TR.
41 |
42 | ***s06\_Spiral***
43 |
44 | ***s06*** describes a 2D slice-selective spiral sequence with fat
45 | saturation. A Gaussian RF pulse followed by a spoiling gradient in the
46 | *z*-axis is used for fat saturation. A raw Archimedean spiral is
47 | generated and then resampled to stay directly under the slew rate and
48 | maximum gradient limits. The mr.traj2grad function calculates gradient
49 | strength and slew rate based on k-space trajectory. The
50 | mr.makeArbitraryGrad function generates an arbitrary gradient (e.g.
51 | spiral).
52 |
53 | ## Quick instructions
54 |
55 | Source code of the demo sequences and reconstruction scripts is the core of this repository. Please download the files to your computer and make them available to Matlab (e.g. by saving them in a subdirectory inside your Pulseq-Matlab installation and adding them to the Matlab's path). There are two sub-directories:
56 |
57 | * seq : contains example pulse sequences specifically prepared for this demo
58 | * recon : contains the reconstruction scripts tested with the above sequences
59 |
60 | ## Quick links
61 |
62 | Pulseq Matlab repository:
63 | https://github.com/pulseq/pulseq
64 |
65 | ## How to follow
66 |
67 | We strongly recommend using a text compare tool like *meld* (see this [Wikipedia page](https://en.wikipedia.org/wiki/Meld_(software)) and compare sequences from subsequent steps to visualise the respective steps.
68 |
69 | ## Further links
70 |
71 | Check out the main *Pulseq* repository at https://github.com/pulseq/pulseq and familarising yourself with the code, example sequences and reconstruction scripts (see
72 | [pulseq/matlab/demoSeq](https://github.com/pulseq/pulseq/tree/master/matlab/demoSeq) and [pulseq/matlab/demoRecon](https://github.com/pulseq/pulseq/tree/master/matlab/demoRecon)). If you already use Pulseq, consider updating to the current version.
73 |
74 |
--------------------------------------------------------------------------------
/12_Radial_and_nonCartesian/doc/12_Radial_and_nonCartesian.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pulseq/tutorials/651dcc7bb46353ceb05783acd39c603abca13ca3/12_Radial_and_nonCartesian/doc/12_Radial_and_nonCartesian.pdf
--------------------------------------------------------------------------------
/12_Radial_and_nonCartesian/doc/12_Radial_and_nonCartesian.pptx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pulseq/tutorials/651dcc7bb46353ceb05783acd39c603abca13ca3/12_Radial_and_nonCartesian/doc/12_Radial_and_nonCartesian.pptx
--------------------------------------------------------------------------------
/12_Radial_and_nonCartesian/recon/r01_2DFFT.m:
--------------------------------------------------------------------------------
1 | % Reconstruction of 2D Cartesian Pulseq data
2 | % provides an example on how data reordering can be detected from the MR
3 | % sequence with almost no additional prior knowledge
4 | %
5 | % it loads Matlab .mat files with the rawdata in the format
6 | % adclen x channels x readouts
7 | % it also seeks an accompanying .seq file with the same name to interpret
8 | % the data
9 |
10 | %% Load the latest file from the specified directory
11 | path='/data/Dropbox/ismrm2021pulseq_liveDemo/dataLive/Vienna_7T_Siemens'; % directory to be scanned for data files
12 |
13 | pattern='*.mat';
14 | D=dir([path filesep pattern]);
15 | [~,I]=sort([D(:).datenum]);
16 | data_file_path=[path filesep D(I(end-0)).name]; % use end-1 to reconstruct the second-last data set, etc...
17 | % or replace I(end-0) with I(1) to process the first dataset, I(2) for the second, etc...
18 | % load data
19 | fprintf(['loading `' data_file_path '´ ...\n']);
20 | data_unsorted = load(data_file_path);
21 | if isstruct(data_unsorted)
22 | fn=fieldnames(data_unsorted);
23 | assert(length(fn)==1); % we only expect a single variable
24 | data_unsorted=data_unsorted.(fn{1});
25 | end
26 | % keep basic filename without the extension
27 | [p,n,e] = fileparts(data_file_path);
28 | basic_file_path=fullfile(p,n);
29 |
30 | %% Load sequence from file
31 | seq = mr.Sequence(); % Create a new sequence object
32 | seq_file_path = [basic_file_path '.seq'];
33 | seq.read(seq_file_path,'detectRFuse'); % detectRFuse is an important option for SE sequences
34 | fprintf(['loaded sequence `' seq.getDefinition('Name') '´\n']);
35 |
36 | %% raw data preparation
37 |
38 | if ndims(data_unsorted)<3 && size(data_unsorted,1)==1
39 | % OCRA data may need some fixing
40 | [~, ~, eventCount]=seq.duration();
41 | readouts=eventCount(6);
42 | data_unsorted=reshape(data_unsorted,[length(data_unsorted)/readouts,1,readouts]);
43 | end
44 |
45 | [adc_len,channels,readouts]=size(data_unsorted);
46 | % the incoming data order is [kx coils acquisitions]
47 | data_coils_last = permute(data_unsorted, [1, 3, 2]);
48 |
49 | %% Plot and analyze the trajectory data (ktraj_adc)
50 |
51 | [ktraj_adc, ktraj, t_excitation, t_refocusing, t_adc] = seq.calculateKspace();
52 | figure; plot(ktraj(1,:),ktraj(2,:),'b',...
53 | ktraj_adc(1,:),ktraj_adc(2,:),'r.'); % a 2D plot
54 | axis('equal'); title('2D k-space trajectory');
55 |
56 | % try to detect the data ordering
57 | k_extent=max(abs(ktraj_adc),[],2);
58 | k_scale=max(k_extent);
59 | k_threshold=k_scale/5000;
60 |
61 | % detect unused dimensions and delete them
62 | if any(k_extentk_threshold)=NaN;
74 | dk_all_cnt=sum(isfinite(dk_all),2);
75 | dk_all(~isfinite(dk_all))=0;
76 | dk=sum(dk_all,2)./dk_all_cnt;
77 | [~,k0_ind]=min(sum(ktraj_adc.^2,1));
78 | kindex=round((ktraj_adc-ktraj_adc(:,k0_ind*ones(1,size(ktraj_adc,2))))./dk(:,ones(1,size(ktraj_adc,2))));
79 | kindex_min=min(kindex,[],2);
80 | kindex_mat=kindex-kindex_min(:,ones(1,size(ktraj_adc,2)))+1;
81 | kindex_end=max(kindex_mat,[],2);
82 | sampler=zeros(kindex_end');
83 | repeat=zeros(1,size(ktraj_adc,2));
84 | for i=1:size(kindex_mat,2)
85 | if (size(kindex_mat,1)==3)
86 | ind=sub2ind(kindex_end,kindex_mat(1,i),kindex_mat(2,i),kindex_mat(3,i));
87 | else
88 | ind=sub2ind(kindex_end,kindex_mat(1,i),kindex_mat(2,i));
89 | end
90 | repeat(i)=sampler(ind);
91 | sampler(ind)=repeat(i)+1;
92 | end
93 | if (max(repeat(:))>0)
94 | kindex=[kindex;(repeat+1)];
95 | kindex_mat=[kindex_mat;(repeat+1)];
96 | kindex_end=max(kindex_mat,[],2);
97 | end
98 | %figure; plot(kindex(1,:),kindex(2,:),'.-');
99 |
100 | %% sort the k-space data into the data matrix
101 | data_coils_last=reshape(data_coils_last, [adc_len*readouts, channels]);
102 |
103 | data=zeros([kindex_end' channels]);
104 | if (size(kindex,1)==3)
105 | for i=1:size(kindex,2)
106 | data(kindex_mat(1,i),kindex_mat(2,i),kindex_mat(3,i),:)=data_coils_last(i,:);
107 | end
108 | else
109 | for i=1:size(kindex,2)
110 | data(kindex_mat(1,i),kindex_mat(2,i),:)=data_coils_last(i,:);
111 | end
112 | end
113 |
114 | if size(kindex,1)==3
115 | nImages=size(data,3);
116 | else
117 | nImages=1;
118 | data=reshape(data, [size(data,1) size(data,2) 1 size(data,3)]); % we need a dummy images/slices dimension
119 | end
120 |
121 | %figure; imab(data);
122 |
123 | %% Reconstruct coil images
124 |
125 | images = zeros(size(data));
126 | %figure;
127 |
128 | for ii = 1:channels
129 | %images(:,:,:,ii) = fliplr(rot90(fftshift(fft2(fftshift(data(:,:,:,ii))))));
130 | images(:,:,:,ii) = fftshift(fft2(fftshift(data(end:-1:1,:,:,ii))));
131 | end
132 |
133 | %% Image display with optional sum of squares combination
134 | figure;
135 | if channels>1
136 | sos=abs(sum(images.*conj(images),ndims(images))).^(1/2);
137 | imab(sos); title('reconstructed image(s), sum-of-squares');
138 | %sos=sos./max(sos(:));
139 | %imwrite(sos, ['img_combined.png']
140 | else
141 | imab(abs(images));title('reconstructed image(s)');
142 | end
143 | colormap('gray');
144 | saveas(gcf,[basic_file_path '_image_2dfft'],'png');
145 |
--------------------------------------------------------------------------------
/12_Radial_and_nonCartesian/recon/r02_2D_iRadon.m:
--------------------------------------------------------------------------------
1 | % very basic inverse Radon radial reconstruction
2 | %
3 | % it loads Matlab .mat files with the rawdata in the format
4 | % adclen x channels x readouts
5 | % it also seeks an accompanying .seq file with the same name to interpret
6 | % the data
7 |
8 | %% Load the latest file from the specified directory
9 | %path='/data/Dropbox/ismrm2021pulseq_liveDemo/dataLive/Vienna_7T_Siemens'; % directory to be scanned for data files
10 |
11 | path='~/20211025-AMR/data';
12 |
13 | pattern='*.seq';
14 | D=dir([path filesep pattern]);
15 | [~,I]=sort([D(:).datenum]);
16 | data_file_path=[path filesep D(I(18)).name]; % use end-1 to reconstruct the second-last data set, etc...
17 | % or replace I(end-0) with I(1) to process the first dataset, I(2) for the second, etc...
18 |
19 | % basic path and filename without the extension
20 | [p,n,e] = fileparts(data_file_path);
21 | basic_file_path=fullfile(p,n);
22 |
23 | % load data
24 | data_file_path=[basic_file_path '.mat'];
25 | fprintf(['loading `' data_file_path '´ ...\n']);
26 | data_unsorted = load(data_file_path);
27 | if isstruct(data_unsorted)
28 | fn=fieldnames(data_unsorted);
29 | assert(length(fn)==1); % we only expect a single variable
30 | data_unsorted=data_unsorted.(fn{1});
31 | end
32 | % keep basic filename without the extension
33 | [p,n,e] = fileparts(data_file_path);
34 | basic_file_path=fullfile(p,n);
35 |
36 | %% Load sequence from file
37 | seq = mr.Sequence(); % Create a new sequence object
38 | seq_file_path = [basic_file_path '.seq'];
39 | seq.read(seq_file_path,'detectRFuse'); % detectRFuse is an important option for SE sequences
40 | fprintf(['loaded sequence `' seq.getDefinition('Name') '´\n']);
41 |
42 | %% raw data preparation
43 |
44 | if ndims(data_unsorted)<3 && size(data_unsorted,1)==1
45 | % OCRA data may need some fixing
46 | [~, ~, eventCount]=seq.duration();
47 | readouts=eventCount(6);
48 | data_unsorted=reshape(data_unsorted,[length(data_unsorted)/readouts,1,readouts]);
49 | elseif ndims(data_unsorted)==2 && size(data_unsorted,2)>1
50 | data_unsorted=reshape(data_unsorted,[size(data_unsorted,1),1,size(data_unsorted,2)]);
51 | end
52 |
53 | [adc_len,channels,readouts]=size(data_unsorted);
54 | rawdata = permute(data_unsorted, [1,3,2]); % channels last
55 |
56 | %% Analyze the nominal trajectory
57 |
58 | [ktraj_adc_nom,t_adc] = seq.calculateKspacePP('trajectory_delay',0);
59 |
60 | % detect slice dimension
61 | max_abs_ktraj_adc=max(abs(ktraj_adc_nom'));
62 | [~, slcDim]=min(max_abs_ktraj_adc);
63 | encDim=find([1 2 3]~=slcDim);
64 |
65 | ktraj_adc_nom = reshape(ktraj_adc_nom, [3, adc_len, size(ktraj_adc_nom,2)/adc_len]);
66 |
67 | prg_angle=unwrap(squeeze(atan2(ktraj_adc_nom(encDim(2),2,:)-ktraj_adc_nom(encDim(2),1,:),...
68 | ktraj_adc_nom(encDim(1),2,:)-ktraj_adc_nom(encDim(1),1,:))));
69 | nproj=length(prg_angle);
70 |
71 | %% from k-space to projections (1D FFTs)
72 |
73 | data_fft1=ifftshift(ifft(ifftshift(rawdata,1)),1);
74 |
75 | figure; imab(abs(squeeze(data_fft1))); title('sinogramm view')
76 |
77 | %% crop the data to remove oversampling and adapt to the iradon
78 | target_matrix_size=256;
79 | shift=0; % 12 was found experimentally for the first Benjamin's data set
80 | cropLeft=(adc_len-target_matrix_size)/2+shift;
81 | cropRight=(adc_len-target_matrix_size)/2-shift;
82 | data_fft1c=data_fft1(2+cropLeft:end-cropRight,:,:);
83 |
84 | % visualize the matching of positive and negative directions
85 | p1=1;
86 | [~,p2]=min(abs(mod(prg_angle-prg_angle(p1)+2*pi,2*pi)-pi)); % look for the projection closest to the opposite to p1
87 | figure;plot(abs(data_fft1c(1:end,p1,1)));hold on;plot(abs(data_fft1c(end:-1:1,p2,1))); title('comparing opposite projections');
88 |
89 | %% the actuall iRadon transform
90 | theta=270-prg_angle/pi*180;
91 | for c=1:channels
92 | % the classical (absolute value) transform
93 | irad_a=iradon(abs(data_fft1c(:,:,c)),theta,'linear','Hann');
94 | %irad_a=iradon(abs(data_fft1c(:,:,c)),theta);
95 | %irad_a=iradon(abs(data_fft1c(:,:,c)),theta,'linear','Shepp-Logan');
96 | if (c==1)
97 | irad_abs=zeros([size(irad_a) channels]);
98 | irad_cmpx=zeros([size(irad_a) channels]);
99 | end
100 | irad_abs(:,:,c)=irad_a;
101 | % MR-specific complex-valued transform
102 | irad_r=iradon(real(data_fft1c(:,:,c)),theta,'linear','Hann');
103 | irad_i=iradon(imag(data_fft1c(:,:,c)),theta,'linear','Hann');
104 | irad_cmpx(:,:,c)=irad_r + 1i*irad_i;
105 | end
106 |
107 | figure;imab(abs(irad_abs));colormap('gray'); title('abs iRadon recon')
108 | saveas(gcf,[basic_file_path '_image_iradon'],'png');
109 |
110 | figure;imab(abs(irad_cmpx));colormap('gray'); title('complex iRadon recon')
111 | %axis('equal');
112 |
113 | %% Sum of squares combination
114 | if channels>1
115 | sos=abs(sum(irad_cmpx.^2,ndims(irad_cmpx)).^(1/2));
116 | sos=sos./max(sos(:));
117 | figure;imab(sos);colormap('gray'); title('SOS complex iRadon recon')
118 | %imwrite(sos, ['img_combined.png'])
119 | end
120 |
--------------------------------------------------------------------------------
/12_Radial_and_nonCartesian/recon/r03_2D_Gridding.m:
--------------------------------------------------------------------------------
1 | % very basic and crude non-Cartesian recon using griddata()
2 | %
3 | % it loads Matlab .mat files with the rawdata in the format
4 | % adclen x channels x readouts
5 | % it also seeks an accompanying .seq file with the same name to interpret
6 | % the data
7 |
8 | %% Load the latest file from the specified directory
9 | path='/data/Dropbox/ismrm2021pulseq_liveDemo/dataLive/Vienna_7T_Siemens'; % directory to be scanned for data files
10 |
11 | pattern='*.mat';
12 | D=dir([path filesep pattern]);
13 | [~,I]=sort([D(:).datenum]);
14 | data_file_path=[path filesep D(I(end-0)).name]; % use end-1 to reconstruct the second-last data set, etc...
15 | % or replace I(end-0) with I(1) to process the first dataset, I(2) for the second, etc...
16 | % load data
17 | fprintf(['loading `' data_file_path '´ ...\n']);
18 | data_unsorted = load(data_file_path);
19 | if isstruct(data_unsorted)
20 | fn=fieldnames(data_unsorted);
21 | assert(length(fn)==1); % we only expect a single variable
22 | data_unsorted=data_unsorted.(fn{1});
23 | end
24 | % keep basic filename without the extension
25 | [p,n,e] = fileparts(data_file_path);
26 | basic_file_path=fullfile(p,n);
27 |
28 | %% Load sequence from file
29 | seq = mr.Sequence(); % Create a new sequence object
30 | seq_file_path = [basic_file_path '.seq'];
31 | seq.read(seq_file_path,'detectRFuse'); % detectRFuse is an important option for SE sequences
32 | fprintf(['loaded sequence `' seq.getDefinition('Name') '´\n']);
33 |
34 | %% raw data preparation
35 |
36 | if ndims(data_unsorted)<3 && size(data_unsorted,1)==1
37 | % OCRA data may need some fixing
38 | [~, ~, eventCount]=seq.duration();
39 | readouts=eventCount(6);
40 | data_unsorted=reshape(data_unsorted,[length(data_unsorted)/readouts,1,readouts]);
41 | elseif ndims(data_unsorted)==2 && size(data_unsorted,2)>1
42 | data_unsorted=reshape(data_unsorted,[size(data_unsorted,1),1,size(data_unsorted,2)]);
43 | end
44 |
45 | [adc_len,channels,readouts]=size(data_unsorted);
46 | rawdata = double(permute(data_unsorted, [1,3,2])); % channels last
47 | rawdata = reshape(rawdata, [adc_len*readouts,channels]);
48 |
49 | %% reconstruct the trajectory
50 |
51 | traj_recon_delay=[0 0 0]*1e-6; % adjust this parameter to potentially improve resolution & geometric accuracy.
52 | % It can be calibrated by inverting the spiral revolution dimension and making
53 | % two images match. for our Prisma and a particular trajectory we found 1.75e-6
54 | % it is also possisible to provide a vector of 3 delays (varying per axis)
55 |
56 | [ktraj_adc, t_adc, ktraj, t_ktraj, t_excitation, t_refocusing] = seq.calculateKspacePP('trajectory_delay',traj_recon_delay);
57 |
58 | % detect slice dimension
59 | max_abs_ktraj_adc=max(abs(ktraj_adc'));
60 | [~, slcDim]=min(max_abs_ktraj_adc);
61 | encDim=find([1 2 3]~=slcDim);
62 |
63 | % figure; plot(t_ktraj, ktraj'); % plot the entire k-space trajectory
64 | %
65 | figure; plot(ktraj(encDim(1),:),ktraj(encDim(2),:),'b',...
66 | ktraj_adc(encDim(1),:),ktraj_adc(encDim(2),:),'r.'); % a 2D plot
67 | axis('equal'); title('2D k-space trajectory');
68 |
69 | %% Define FOV and resolution and simple off-resonance frequency correction
70 |
71 | %fov=30e-3; Nx=128; Ny=Nx; % OCRA
72 | fov=256e-3; Nx=256; Ny=Nx; % whole-body scanners
73 | deltak=1/fov;
74 | os=2; % oversampling factor (we oversample both in image and k-space)
75 | offresonance=0; % global off-resonance in Hz
76 |
77 | %% rudimentary off-resonance correction
78 | nex=length(t_excitation);
79 | t_adc_ex=t_adc;
80 | if nex>1
81 | for e=2:nex
82 | i1=find(t_adc>t_excitation(e),1);
83 | if e1
129 | sos=abs(sum(igdc.^2,ndims(igdc)).^(1/2));
130 | sos=sos./max(sos(:));
131 | figure;imab(sos);colormap('gray');
132 | %imwrite(sos, ['img_combined.png'])
133 | end
134 |
135 | saveas(gcf,[basic_file_path '_image_2dgrd'],'png');
136 |
--------------------------------------------------------------------------------
/12_Radial_and_nonCartesian/recon/r04_radial_delay_calculation.m:
--------------------------------------------------------------------------------
1 | % very basic and crude non-Cartesian recon using griddata()
2 | %
3 | % it loads Matlab .mat files with the rawdata in the format
4 | % adclen x channels x readouts
5 | % it also seeks an accompanzing .seq file with the same name to interpret
6 | % the data
7 |
8 | %% Load the latest file from the specified directory
9 | %path='../IceNIH_RawSend/'; % directory to be scanned for data files
10 | %path='/data/Dropbox/ismrm2021pulseq_liveDemo/dataLive/Vienna_7T_Siemens'; % directory to be scanned for data files
11 | %path='/data/Dropbox/ismrm2021pulseq_liveDemo/dataPrerecorded/Vienna_7T_Siemens'
12 | path='~/20211025-AMR/data';
13 |
14 | pattern='*.seq';
15 | D=dir([path filesep pattern]);
16 | [~,I]=sort([D(:).datenum]);
17 | data_file_path=[path filesep D(I(18)).name]; % use end-1 to reconstruct the second-last data set, etc...
18 | % or replace I(end-0) with I(1) to process the first dataset, I(2) for the second, etc...
19 |
20 | % basic path and filename without the extension
21 | [p,n,e] = fileparts(data_file_path);
22 | basic_file_path=fullfile(p,n);
23 |
24 | % load data
25 | data_file_path=[basic_file_path '.mat'];
26 | fprintf(['loading `' data_file_path '´ ...\n']);
27 | data_unsorted = load(data_file_path);
28 | if isstruct(data_unsorted)
29 | fn=fieldnames(data_unsorted);
30 | assert(length(fn)==1); % we only expect a single variable
31 | data_unsorted=data_unsorted.(fn{1});
32 | end
33 |
34 | %% Load sequence from file
35 | seq = mr.Sequence(); % Create a new sequence object
36 | seq_file_path = [basic_file_path '.seq'];
37 | seq.read(seq_file_path,'detectRFuse'); % detectRFuse is an important option for SE sequences
38 | fprintf(['loaded sequence `' seq.getDefinition('Name') '´\n']);
39 |
40 | %% raw data preparation
41 |
42 | if ndims(data_unsorted)<3 && size(data_unsorted,1)==1
43 | % OCRA data may need some fixing
44 | [~, ~, eventCount]=seq.duration();
45 | readouts=eventCount(6);
46 | data_unsorted=reshape(data_unsorted,[length(data_unsorted)/readouts,1,readouts]);
47 | elseif ndims(data_unsorted)==2 && size(data_unsorted,2)>1
48 | data_unsorted=reshape(data_unsorted,[size(data_unsorted,1),1,size(data_unsorted,2)]);
49 | end
50 |
51 | [adc_len,channels,readouts]=size(data_unsorted);
52 | rawdata = permute(data_unsorted, [1,3,2]); % channels last
53 |
54 | %% Analyze the nominal trajectory
55 |
56 | [ktraj_adc_nom,t_adc] = seq.calculateKspacePP('trajectory_delay',0e-6);
57 |
58 | % detect slice dimension
59 | max_abs_ktraj_adc=max(abs(ktraj_adc_nom'));
60 | [~, slcDim]=min(max_abs_ktraj_adc);
61 | encDim=find([1 2 3]~=slcDim);
62 |
63 | ktraj_adc_nom = reshape(ktraj_adc_nom, [3, adc_len, size(ktraj_adc_nom,2)/adc_len]);
64 |
65 | prg_angle=squeeze(atan2(ktraj_adc_nom(encDim(2),2,:)-ktraj_adc_nom(encDim(2),1,:),ktraj_adc_nom(encDim(2),2,:)-ktraj_adc_nom(encDim(2),1,:)));
66 | nproj=length(prg_angle);
67 |
68 | i_pure2=find(abs(ktraj_adc_nom(encDim(2),1,:))=0);
46 | assert(delayTE2>mr.calcDuration(g_sp2));
47 | assert(delayTR>=0);
48 |
49 | % Loop over repetitions and define sequence blocks
50 | for i=(1-Ndummy):Ny
51 | seq.addBlock(rf_ex,gs);
52 | if (i>0) % semi-negative index -- dummy scans
53 | gy = mr.makeTrapezoid('y', 'Area', phaseAreas(i), 'Duration', grPredur);
54 | else
55 | gy = mr.makeTrapezoid('y', 'Area', 0, 'Duration', grPredur);
56 | end
57 | seq.addBlock(grPre,gy);
58 | seq.addBlock(mr.makeDelay(delayTE1));
59 | seq.addBlock(rf_ref,g_sp1);
60 | seq.addBlock(g_sp2, mr.makeDelay(delayTE2));
61 | if (i>0) % semi-negative index -- dummy scans
62 | seq.addBlock(adc,gr);
63 | else
64 | seq.addBlock(gr);
65 | end
66 | seq.addBlock(gy,mr.makeDelay(delayTR));
67 | end
68 |
69 | % show the first non-dummy TR with the block structure
70 | seq.plot('showBlocks',1,'timeRange',TR*(Ndummy+[0 1]),'timeDisp','us');
71 |
72 | % check whether the timing of the sequence is compatible with the scanner
73 | [ok, error_report]=seq.checkTiming;
74 |
75 | if (ok)
76 | fprintf('Timing check passed successfully\n');
77 | else
78 | fprintf('Timing check failed! Error listing follows:\n');
79 | fprintf([error_report{:}]);
80 | fprintf('\n');
81 | end
82 |
83 | seq.setDefinition('FOV', [fov fov sliceThickness]);
84 | seq.setDefinition('Name', 'se_2d');
85 |
86 | seq.write('se_2d.seq') % Write to pulseq file
87 | %seq.install('siemens'); % copy to scanner
88 |
89 | % calculate k-space but only use it to check timing
90 | [ktraj_adc, t_adc, ktraj, t_ktraj, t_excitation, t_refocusing] = seq.calculateKspacePP();
91 | %[ktraj_adc, t_adc, ktraj, t_ktraj, t_excitation, t_refocusing] = seq.calculateKspacePP('trajectory_delay',[0 0 0]*1e-6); % play with anisotropic trajectory delays -- zoom in to see the trouble ;-)
92 |
93 | if Ndummy==0 % if nDummy equals 0 we can do additional checks
94 | assert(abs(t_refocusing(1)-t_excitation(1)-TE/2)<1e-6); % check that the refocusing happens at the 1/2 of TE
95 | assert(abs(t_adc(Nx/2)-t_excitation(1)-TE)=0);
47 | assert(delayTE2>mr.calcDuration(g_sp2));
48 | assert(delayTR>=0);
49 |
50 | % Loop over repetitions and define sequence blocks
51 | for i=(1-Ndummy):Nr
52 | seq.addBlock(rf_ex,gs);
53 | seq.addBlock(mr.rotate('z',delta*(i-1),grPre));
54 | seq.addBlock(mr.makeDelay(delayTE1));
55 | seq.addBlock(rf_ref,g_sp1);
56 | seq.addBlock(g_sp2, mr.makeDelay(delayTE2));
57 | if (i>0)
58 | seq.addBlock(mr.rotate('z',delta*(i-1),adc,gr));
59 | else
60 | seq.addBlock(mr.rotate('z',delta*(i-1),gr));
61 | end
62 | seq.addBlock(mr.makeDelay(delayTR));
63 | end
64 |
65 | seq.plot();
66 |
67 | % check whether the timing of the sequence is compatible with the scanner
68 | [ok, error_report]=seq.checkTiming;
69 |
70 | if (ok)
71 | fprintf('Timing check passed successfully\n');
72 | else
73 | fprintf('Timing check failed! Error listing follows:\n');
74 | fprintf([error_report{:}]);
75 | fprintf('\n');
76 | end
77 |
78 | seq.setDefinition('FOV', [fov fov sliceThickness]);
79 | seq.setDefinition('Name', 'se_rad');
80 |
81 | seq.write('se_radial.seq') % Write to pulseq file
82 | %seq.install('siemens'); % copy to scanner
83 |
84 | % calculate k-space but only use it to check timing
85 | [ktraj_adc, t_adc, ktraj, t_ktraj, t_excitation, t_refocusing] = seq.calculateKspacePP();
86 | %[ktraj_adc, t_adc, ktraj, t_ktraj, t_excitation, t_refocusing] = seq.calculateKspacePP('trajectory_delay',[0 0 0]*1e-6); % play with anisotropic trajectory delays -- zoom in to see the trouble ;-)
87 |
88 | if Ndummy==0
89 | assert(abs(t_refocusing(1)-t_excitation(1)-TE/2)<1e-6); % check that the refocusing happens at the 1/2 of TE
90 | assert(abs(t_adc(Nx/2)-t_excitation(1)-TE)=0));
40 | assert(all(delayTR>=mr.calcDuration(gxSpoil,gzSpoil)));
41 |
42 | rf_phase=0;
43 | rf_inc=0;
44 |
45 | for i=(-Ndummy+1):Ny
46 | for c=1:length(TE)
47 | rf.phaseOffset=rf_phase/180*pi;
48 | adc.phaseOffset=rf_phase/180*pi;
49 | rf_inc=mod(rf_inc+rfSpoilingInc, 360.0);
50 | rf_phase=mod(rf_phase+rf_inc, 360.0);
51 | %
52 | seq.addBlock(rf,gz);
53 | if (i>0)
54 | gyPre = mr.makeTrapezoid('y','Area',phaseAreas(i),'Duration',mr.calcDuration(gxPre),'system',sys);
55 | seq.addBlock(gxPre,gyPre,gzReph);
56 | else
57 | seq.addBlock(gxPre,gzReph);
58 | end
59 | seq.addBlock(mr.makeDelay(delayTE(c)));
60 | if (i>0)
61 | seq.addBlock(gx,adc);
62 | seq.addBlock(mr.makeDelay(delayTR(c)),gxSpoil,mr.scaleGrad(gyPre,-1),gzSpoil)
63 | else
64 | seq.addBlock(gx);
65 | seq.addBlock(mr.makeDelay(delayTR(c)),gxSpoil,gzSpoil)
66 | end
67 | end
68 | end
69 |
70 | % check whether the timing of the sequence is compatible with the scanner
71 | [ok, error_report]=seq.checkTiming;
72 |
73 | if (ok)
74 | fprintf('Timing check passed successfully\n');
75 | else
76 | fprintf('Timing check failed! Error listing follows:\n');
77 | fprintf([error_report{:}]);
78 | fprintf('\n');
79 | end
80 |
81 | %
82 | seq.setDefinition('FOV', [fov fov sliceThickness]);
83 | seq.setDefinition('Name', 'gre_rad');
84 |
85 | seq.write('gre_rad.seq') % Write to pulseq file
86 | %seq.install('siemens');
87 |
88 | %% plot sequence and k-space diagrams
89 |
90 | %seq.plot('timeRange',[0 5]*TR);
91 | seq.plot();
92 |
93 | % trajectory calculation
94 | [ktraj_adc, t_adc, ktraj, t_ktraj, t_excitation, t_refocusing1] = seq.calculateKspacePP();
95 | %[ktraj_adc, t_adc, ktraj, t_ktraj, t_excitation, t_refocusing] = seq.calculateKspacePP('trajectory_delay',[0 0 0]*1e-6); % play with anisotropic trajectory delays -- zoom in to see the trouble ;-)
96 |
97 | % plot k-spaces
98 | figure; plot(t_ktraj, ktraj'); % plot the entire k-space trajectory
99 | hold on; plot(t_adc,ktraj_adc(1,:),'.'); % and sampling points on the kx-axis
100 | title('k-vector components as functions of time'); xlabel('time /s'); ylabel('k-component /m^-^1');
101 | figure; plot(ktraj(1,:),ktraj(2,:),'b'); % a 2D plot
102 | axis('equal'); % enforce aspect ratio for the correct trajectory display
103 | hold on;plot(ktraj_adc(1,:),ktraj_adc(2,:),'r.'); % plot the sampling points
104 | title('2D k-space trajectory'); xlabel('k_x /m^-^1'); ylabel('k_y /m^-^1');
105 |
--------------------------------------------------------------------------------
/12_Radial_and_nonCartesian/seq/s04_RadialGradientEcho.m:
--------------------------------------------------------------------------------
1 | % set system limits
2 | sys = mr.opts('MaxGrad', 28, 'GradUnit', 'mT/m', ...
3 | 'MaxSlew', 80, 'SlewUnit', 'T/m/s', ...
4 | 'rfRingdownTime', 20e-6, 'rfDeadTime', 100e-6, 'adcDeadTime', 10e-6);
5 |
6 | seq=mr.Sequence(sys); % Create a new sequence object
7 | fov=256e-3; Nx=256; % Define FOV and resolution
8 | alpha=30; % flip angle
9 | sliceThickness=3e-3; % slice
10 | TE=8e-3; % TE; give a vector here to have multiple TEs (e.g. for field mapping)
11 | TR=20e-3; % only a single value for now
12 | Nr=256; % number of radial spokes
13 | Ndummy=10; % number of dummy scans
14 | delta=pi/Nr; % angular increment; try golden angle pi*(3-5^0.5) or 0.5 of it
15 |
16 | % more in-depth parameters
17 | rfSpoilingInc=117; % RF spoiling increment
18 |
19 | % Create alpha-degree slice selection pulse and gradient
20 | [rf, gz] = mr.makeSincPulse(alpha*pi/180,'Duration',4e-3,...
21 | 'SliceThickness',sliceThickness,'apodization',0.5,'timeBwProduct',4,'system',sys);
22 |
23 | % Define other gradients and ADC events
24 | deltak=1/fov;
25 | gx = mr.makeTrapezoid('x','FlatArea',Nx*deltak,'FlatTime',3.2e-3,'system',sys);
26 | adc = mr.makeAdc(Nx,'Duration',gx.flatTime,'Delay',gx.riseTime,'system',sys);
27 | gxPre = mr.makeTrapezoid('x','Area',-gx.area/2-deltak/2,'Duration',2e-3,'system',sys); % we need this "deltak/2" because of the ADC sampling taking place in the middle of the dwell time
28 | gzReph = mr.makeTrapezoid('z','Area',-gz.area/2,'Duration',2e-3,'system',sys);
29 |
30 | % gradient spoiling
31 | gxSpoil=mr.makeTrapezoid('x','Area',1*Nx*deltak,'system',sys);
32 | gzSpoil=mr.makeTrapezoid('z','Area',4/sliceThickness,'system',sys);
33 |
34 | % Calculate timing
35 | delayTE=ceil((TE - mr.calcDuration(gxPre) - gz.fallTime - gz.flatTime/2 ...
36 | - mr.calcDuration(gx)/2)/seq.gradRasterTime)*seq.gradRasterTime;
37 | delayTR=ceil((TR - mr.calcDuration(gxPre) - mr.calcDuration(gz) ...
38 | - mr.calcDuration(gx) - delayTE)/seq.gradRasterTime)*seq.gradRasterTime;
39 | assert(all(delayTE>=0));
40 | assert(all(delayTR>=mr.calcDuration(gxSpoil,gzSpoil)));
41 |
42 | % start the sequence
43 | rf_phase=0;
44 | rf_inc=0;
45 |
46 | for i=(-Ndummy+1):Nr
47 | for c=1:length(TE)
48 | rf.phaseOffset=rf_phase/180*pi;
49 | adc.phaseOffset=rf_phase/180*pi;
50 | rf_inc=mod(rf_inc+rfSpoilingInc, 360.0);
51 | rf_phase=mod(rf_phase+rf_inc, 360.0);
52 | %
53 | seq.addBlock(rf,gz);
54 | phi=delta*(i-1);
55 | seq.addBlock(mr.rotate('z',phi,gxPre,gzReph));
56 | seq.addBlock(mr.makeDelay(delayTE(c)));
57 | if i>0
58 | seq.addBlock(mr.rotate('z',phi,gx,adc));
59 | else
60 | seq.addBlock(mr.rotate('z',phi,gx));
61 | end
62 | seq.addBlock(mr.rotate('z',phi,gxSpoil,gzSpoil,mr.makeDelay(delayTR)));
63 | end
64 | end
65 |
66 | % check whether the timing of the sequence is compatible with the scanner
67 | [ok, error_report]=seq.checkTiming;
68 |
69 | if (ok)
70 | fprintf('Timing check passed successfully\n');
71 | else
72 | fprintf('Timing check failed! Error listing follows:\n');
73 | fprintf([error_report{:}]);
74 | fprintf('\n');
75 | end
76 |
77 | %
78 | seq.setDefinition('FOV', [fov fov sliceThickness]);
79 | seq.setDefinition('Name', 'gre_rad');
80 |
81 | seq.write('gre_rad.seq') % Write to pulseq file
82 | %seq.install('siemens');
83 |
84 | %% plot sequence and k-space diagrams
85 |
86 | %seq.plot('timeRange',[0 5]*TR);
87 | seq.plot();
88 |
89 | % trajectory calculation
90 | [ktraj_adc, t_adc, ktraj, t_ktraj, t_excitation, t_refocusing1] = seq.calculateKspacePP();
91 | %[ktraj_adc, t_adc, ktraj, t_ktraj, t_excitation, t_refocusing] = seq.calculateKspacePP('trajectory_delay',[0 0 0]*1e-6); % play with anisotropic trajectory delays -- zoom in to see the trouble ;-)
92 |
93 | % plot k-spaces
94 | figure; plot(t_ktraj, ktraj'); % plot the entire k-space trajectory
95 | hold on; plot(t_adc,ktraj_adc(1,:),'.'); % and sampling points on the kx-axis
96 | title('k-vector components as functions of time'); xlabel('time /s'); ylabel('k-component /m^-^1');
97 | figure; plot(ktraj(1,:),ktraj(2,:),'b'); % a 2D plot
98 | axis('equal'); % enforce aspect ratio for the correct trajectory display
99 | hold on;plot(ktraj_adc(1,:),ktraj_adc(2,:),'r.'); % plot the sampling points
100 | title('2D k-space trajectory'); xlabel('k_x /m^-^1'); ylabel('k_y /m^-^1');
101 |
--------------------------------------------------------------------------------
/12_Radial_and_nonCartesian/seq/s05_FastRadialGradientEcho.m:
--------------------------------------------------------------------------------
1 | % set system limits (slew rate 130 and max_grad 30 work on Prisma)
2 | sys = mr.opts('MaxGrad', 28, 'GradUnit', 'mT/m', ...
3 | 'MaxSlew', 120, 'SlewUnit', 'T/m/s', ...
4 | 'rfRingdownTime', 10e-6,'rfDeadTime', 100e-6, 'adcDeadTime', 10e-6);
5 |
6 | seq=mr.Sequence(sys); % Create a new sequence object
7 | fov=240e-3; Nx=240; % Define FOV and resolution
8 | alpha=5; % flip angle
9 | sliceThickness=3e-3; % slice
10 | % TE & TR are as short as possible derived from the above parameters and
11 | % the system specs above
12 | Nr=30; % number of radial spokes
13 | Ndummy=1; % number of dummy scans
14 | delta= pi / Nr; % angular increment; try golden angle pi*(3-5^0.5) or 0.5 of it
15 | % more in-depth parameters
16 | ro_dur=1200e-6; % RO duration
17 | ro_os=2; % readout oversampling
18 | ro_spoil=0.5; % additional k-max excursion for RO spoiling
19 | rf_dur = 600e-6;
20 | sl_spoil=2; % spoil area compared to the slice thickness
21 |
22 |
23 |
24 | % more in-depth parameters
25 | rfSpoilingInc=117; % RF spoiling increment
26 |
27 | % Create alpha-degree slice selection pulse and gradient
28 | [rf, gz, gzReph] = mr.makeSincPulse(alpha*pi/180,'Duration',rf_dur,...
29 | 'SliceThickness',sliceThickness,'apodization',0.5,'timeBwProduct',2,'system',sys);
30 | gzReph.delay=mr.calcDuration(gz);
31 | gzComb=mr.addGradients({gz, gzReph}, 'system', sys);
32 |
33 | % Define other gradients and ADC events
34 | deltak=1/fov;
35 | gx = mr.makeTrapezoid('x','Amplitude',Nx*deltak/ro_dur,'FlatTime',ceil(ro_dur/sys.gradRasterTime)*sys.gradRasterTime,'system',sys);
36 | adc = mr.makeAdc(Nx*ro_os,'Duration',ro_dur,'Delay',gx.riseTime,'system',sys);
37 | gxPre = mr.makeTrapezoid('x','Area',-gx.amplitude*(ro_dur/Nx/ro_os*(Nx*ro_os/2-0.5)+0.5*gx.riseTime),'system',sys); % 0.5 is necessary to acount for the Siemens sampling in the center of the dwell periods
38 | % start gxPre at least right after the RF pulse and when possible end it at the same time as the end of the slice refocusing gradient
39 | [gxPre,~,~]=mr.align('right', gxPre, 'right', gzComb, 'left', mr.makeDelay(mr.calcDuration(rf)+mr.calcDuration(gxPre)));
40 |
41 | % gradient spoiling
42 | if sl_spoil>0
43 | sp_area_needed=sl_spoil/sliceThickness-gz.area/2;
44 | gzSpoil=mr.makeTrapezoid('z','Area',sp_area_needed,'system',sys,'Delay',gx.riseTime+gx.flatTime);
45 | else
46 | gzSpoil=[];
47 | end
48 |
49 | if ro_spoil>0
50 | ro_add_time=ceil(((gx.area/Nx*(Nx/2+1)*ro_spoil)/gx.amplitude)/sys.gradRasterTime)*sys.gradRasterTime;
51 | gx.flatTime=gx.flatTime+ro_add_time; % careful, areas stored in the object are now wrong
52 | end
53 |
54 |
55 | % we don't calculate timing but just accept what is achievable
56 | TR=0;
57 | TE=0;
58 |
59 | % start the sequence
60 | rf_phase=0;
61 | rf_inc=0;
62 | for i=(1-Ndummy):3
63 | rf.phaseOffset=rf_phase/180*pi;
64 | adc.phaseOffset=rf_phase/180*pi;
65 | rf_inc=mod(rf_inc+rfSpoilingInc, 360.0);
66 | rf_phase=mod(rf_phase+rf_inc, 360.0);
67 | %
68 | phi=delta*(i-1);
69 | seq.addBlock(mr.rotate('z',phi,rf,gzComb,gxPre));
70 | if TE<=0, TE=seq.duration+adc.delay+adc.dwell*(adc.numSamples/2+0.5); end
71 | if i>0
72 | seq.addBlock(mr.rotate('z',phi,gx,adc,gzSpoil));
73 | else
74 | seq.addBlock(mr.rotate('z',phi,gx,gzSpoil));
75 | end
76 | if TR<=0, TR=seq.duration; end
77 | end
78 |
79 | % check whether the timing of the sequence is compatible with the scanner
80 | [ok, error_report]=seq.checkTiming;
81 |
82 | if (ok)
83 | fprintf('Timing check passed successfully\n');
84 | else
85 | fprintf('Timing check failed! Error listing follows:\n');
86 | fprintf([error_report{:}]);
87 | fprintf('\n');
88 | end
89 |
90 | %
91 | seq.setDefinition('FOV', [fov fov sliceThickness]);
92 | seq.setDefinition('Name', 'gre_rad');
93 |
94 | seq.write('gre_rad.seq') % Write to pulseq file
95 | %seq.install('siemens');
96 |
97 | %% plot sequence and k-space diagrams
98 |
99 | %seq.plot('timeRange',[0 5]*TR);
100 | seq.plot();
101 |
102 | % trajectory calculation
103 | [ktraj_adc, t_adc, ktraj, t_ktraj, t_excitation, t_refocusing1] = seq.calculateKspacePP();
104 | %[ktraj_adc, t_adc, ktraj, t_ktraj, t_excitation, t_refocusing] = seq.calculateKspacePP('trajectory_delay',[0 0 0]*1e-6); % play with anisotropic trajectory delays -- zoom in to see the trouble ;-)
105 |
106 | % plot k-spaces
107 | figure; plot(t_ktraj, ktraj'); % plot the entire k-space trajectory
108 | hold on; plot(t_adc,ktraj_adc(1,:),'.'); % and sampling points on the kx-axis
109 | title('k-vector components as functions of time'); xlabel('time /s'); ylabel('k-component /m^-^1');
110 | figure; plot(ktraj(1,:),ktraj(2,:),'b'); % a 2D plot
111 | axis('equal'); % enforce aspect ratio for the correct trajectory display
112 | hold on;plot(ktraj_adc(1,:),ktraj_adc(2,:),'r.'); % plot the sampling points
113 | title('2D k-space trajectory'); xlabel('k_x /m^-^1'); ylabel('k_y /m^-^1');
114 |
115 | return;
116 |
117 | %% very optional slow step, but useful for testing during development e.g. for the real TE, TR or for staying within slew rate limits
118 |
119 | rep = seq.testReport;
120 | fprintf([rep{:}]);
121 |
122 |
--------------------------------------------------------------------------------
/12_Radial_and_nonCartesian/seq/s06_Spiral.m:
--------------------------------------------------------------------------------
1 | % this is an experimental spiral sequence
2 |
3 | fov=256e-3; Nx=96; Ny=Nx; % Define FOV and resolution
4 | sliceThickness=3e-3; % slice thinckness
5 | Nslices=1;
6 | Oversampling=2; % oversampling along the readout (trajectory) dimension; I would say it needs to be at least 2
7 | phi=pi/2; % orientation of the readout e.g. for interleaving
8 |
9 | % Set system limits
10 | sys = mr.opts('MaxGrad',22,'GradUnit','mT/m',...
11 | 'MaxSlew',160,'SlewUnit','T/m/s',...
12 | 'rfRingdownTime', 30e-6, 'rfDeadtime', 100e-6, 'adcDeadTime', 10e-6,...
13 | 'adcSamplesLimit', 8192); % adcSamplesLimit is important on many Siemens platforms
14 |
15 | seq=mr.Sequence(sys); % Create a new sequence object
16 | warning('OFF', 'mr:restoreShape'); % restore shape is not compatible with spirals and will throw a warning from each plot() or calcKspace() call
17 |
18 | % Create fat-sat pulse
19 | % (in Siemens interpreter from January 2019 duration is limited to 8.192 ms, and although product EPI uses 10.24 ms, 8 ms seems to be sufficient)
20 | B0=2.89; % 1.5 2.89 3.0
21 | sat_ppm=-3.45;
22 | sat_freq=sat_ppm*1e-6*B0*sys.gamma;
23 | rf_fs = mr.makeGaussPulse(110*pi/180,'system',sys,'Duration',8e-3,...
24 | 'bandwidth',abs(sat_freq),'freqOffset',sat_freq);
25 | gz_fs = mr.makeTrapezoid('z',sys,'delay',mr.calcDuration(rf_fs),'Area',1/1e-4); % spoil up to 0.1mm
26 |
27 | % Create 90 degree slice selection pulse and gradient
28 | [rf, gz] = mr.makeSincPulse(pi/2,'system',sys,'Duration',3e-3,...
29 | 'SliceThickness',sliceThickness,'apodization',0.5,'timeBwProduct',4);
30 |
31 | % define k-space parameters
32 | deltak=1/fov;
33 | kRadius = round(Nx/2);
34 | kSamples=round(2*pi*kRadius)*Oversampling;
35 |
36 | % calculate a raw Archimedian spiral trajectory
37 | clear ka;
38 | ka(kRadius*kSamples+1)=1i; % init as complex
39 | for c=0:kRadius*kSamples
40 | r=deltak*c/kSamples;
41 | a=mod(c,kSamples)*2*pi/kSamples;
42 | ka(c+1)=r*exp(1i*a);
43 | end
44 | ka=[real(ka); imag(ka)];
45 | % calculate gradients and slew rates
46 | [ga, sa]=mr.traj2grad(ka);
47 |
48 | % limit analysis
49 | safety_magrin=0.94; % we need that otherwise we just about violate the slew rate due to the rounding errors
50 | dt_gabs=abs(ga(1,:)+1i*ga(2,:))/(sys.maxGrad*safety_magrin)*sys.gradRasterTime;
51 | dt_sabs=sqrt(abs(sa(1,:)+1i*sa(2,:))/(sys.maxSlew*safety_magrin))*sys.gradRasterTime;
52 | dt_smooth=max([dt_gabs;dt_sabs]);
53 |
54 | % apply the lower limit not to lose the trajectory detail
55 | dt_min=4*sys.gradRasterTime/kSamples; % we want at least 4 points per revolution
56 | dt_smooth0=dt_smooth;
57 | dt_smooth(dt_smooth