Read a sequence into MATLAB

The Sequence class provides an implementation of the open file format for MR sequences described here: http://pulseq.github.io/specification.pdf

This example demonstrates parsing an MRI sequence stored in this format, accessing sequence parameters and visualising the sequence.

Contents

Read a sequence file

A sequence can be loaded from the open MR file format using the read method. seq_name='trufi.seq';

seq_name='../examples/gre.seq';
seq=mr.Sequence();
seq.read(seq_name);

sanity check to see if the reading and writing are consistent

seq.write('read_test.seq'); system(['diff -s -u ' seq_name ' read_test.seq'],'-echo');

Access sequence parameters and blocks

Parameters defined with in the [DEFINITIONS] section of the sequence file are accessed with the getDefinition method. These are user-specified definitions and do not effect the execution of the sequence.

scanId=seq.getDefinition('Scan_ID')
scanId =

     []

Sequence blocks are accessed with the getBlock method. As shown in the output the first block is a selective excitation block and contains an RF pulse and gradient and on the z-channel.

b1 = seq.getBlock(1)
b1 = 

  struct with fields:

           rf: [1×1 struct]
           gx: []
           gy: []
           gz: [1×1 struct]
          adc: []
        delay: []
    gexternal: [1×1 struct]

Further information about each event can be obtained by accessing the appropriate fields of the block struct. In particular, the complex RF signal is stored in the field signal.

rf=b1.rf

figure;
subplot(211);
plot(rf.t,abs(rf.signal));
ylabel('RF magnitude');
subplot(212);
plot(1e3*rf.t,angle(rf.signal))
ylabel('RF phase'); xlabel('t (ms)');
rf = 

  struct with fields:

            type: 'rf'
          signal: [4020×1 double]
               t: [4020×1 double]
           delay: 1.0000e-04
      freqOffset: 0
     phaseOffset: 0
        deadTime: 0
    ringdownTime: 0

The next three blocks contain: three gradient events; a delay; and readout gradient with ADC event, each with corresponding fields defining the details of the events.

b2 = seq.getBlock(2);
b3 = seq.getBlock(3);
b4 = seq.getBlock(4);
b2.gx
b3.delay
b4.adc
ans = 

  struct with fields:

         type: 'trap'
      channel: 'x'
    amplitude: -6.4748e+04
     riseTime: 2.0000e-05
     flatTime: 0.0020
     fallTime: 2.0000e-05
        delay: 0
         area: -128.2001
     flatArea: -126.9051


ans = 

  struct with fields:

     type: 'delay'
    delay: 1.2000e-04


ans = 

  struct with fields:

     numSamples: 64
          dwell: 1.0000e-04
          delay: 1.0000e-05
     freqOffset: 0
    phaseOffset: 0
       deadTime: 0
           type: 'adc'

Plot the sequence

Visualise the sequence using the plot method of the class. This creates a new figure and shows ADC, RF and gradient events. The axes are linked so zooming is consistent. In this example, a simple gradient echo sequence for MRI is displayed.

seq.plot()

The details of individual pulses are not well-represented when the entire sequence is visualised. Interactive zooming is helpful here. Alternatively, a time range can be specified. An additional parameter also allows the display units to be changed for easy reading. Further, the handle of the created figure can be returned if required.

fig=seq.plot('TimeRange',[0 16e-3],'timeDisp','ms')
fig = 

  Figure (3) with properties:

      Number: 3
        Name: ''
       Color: [0.9400 0.9400 0.9400]
    Position: [1320 1278 560 420]
       Units: 'pixels'

  Use GET to show all properties

Modifying sequence blocks

In addition to loading a sequence and accessing sequence blocks, blocks can be modified. In this example, a Hamming window is applied to the first RF pulse of the sequence and the flip angle is changed to 45 degrees. The remaining RF pulses are unchanged.

rf2=rf;
duration=rf2.t(end);
t=rf2.t-duration/2;                                 % Centre time about 0
alpha=0.5;
BW=4/duration;                                      % time bandwidth product = 4
window = (1.0-alpha+alpha*cos(2*pi*t/duration));    % Hamming window
signal = window.*sinc(BW*t);

% Normalise area to achieve 2*pi rotation
signal=signal./(seq.rfRasterTime*sum(real(signal)));

% Scale to 45 degree flip angle
rf2.signal=signal.*45/360;

b1.rf=rf2;
seq.setBlock(1,b1);

second check to see what we have changed

seq.write('read_test2.seq'); system(['diff -s -u ' seq_name ' read_test2.seq'],'-echo');

The amplitude of the first rf pulse is reduced due to the reduced flip-angle. Notice the reduction is not exactly a factor of two due to the windowing function.

amp1_in_Hz = max(abs(seq.getBlock(1).rf.signal))
amp2_in_Hz = max(abs(seq.getBlock(6).rf.signal))
amp1_in_Hz =

  122.8179


amp2_in_Hz =

   27.4293