Minimum and Maximum Phase RF Pulses

The SLR designs so far have been linear phase:

  • They refocus (almost) perfectly
  • They can be used as spin echo pulses

If you don’t care about phase, there are much more selective pulses:

  • Saturation pulses
  • Inversion pulses
  • Slab select pulses

These can be almost twice as selective as linear phase pulses.

You can design a minimum phase pulse by first designing a mimimum phase $\beta$, scaling it to $\sin(\phi/2)$, where $\phi$ is your flip angle.

The function dzmp(n,tb,d1,d2) designs a minimum phase waveform with n samples, a time-bandwdith product of 8, and a passband ripple of d1, and a stopband ripple of d2. These are equalripple designs, which can end up with spikes (“Conolly wings”) at the ends. The output of dzmp is scaled to 1, so this corresponds to an inversion pulse.

%use octave

try
    cd rf_tools_octave
catch
    try
        cd ../rf_tools_octave
    catch
        try
            cd ../rf_tools_octave
        catch
            cd ../../rf_tools_octave
        end
    end
end

pkg load signal
%use octave

bm = dzmp(256,8,0.001,0.001);
rfm = b2rf(bm);
t = [1:256]/32;

re_rfscale_rfm = real(rfscale(rfm,8));
%use sos
%get t --from Octave
%get re_rfscale_rfm --from Octave

import matplotlib.pyplot as plt
import plotly.plotly as py
import plotly.graph_objs as go
import numpy as np
from plotly import __version__
from plotly.offline import download_plotlyjs, init_notebook_mode, plot, iplot
config={'showLink': False, 'displayModeBar': False}

init_notebook_mode(connected=True)

from IPython.core.display import display, HTML


mag = go.Scatter(
    x = t,
    y = re_rfscale_rfm,
    name = 'Slice profile',
    text = 'Slice profile',
    hoverinfo = 'x+y+text',
    line = dict(
        color = ('rgb(22, 96, 167)'),
        ),
)

data = [mag]

layout = go.Layout(
    width=600,
    height=350,
    margin=go.layout.Margin(
        l=100,
        r=50,
        b=60,
        t=40,
    ),
    xaxis=dict(
        showgrid=False,
        linecolor='black',
        linewidth=2
    ),
    yaxis=dict(
        showgrid=False,
        linecolor='black',
        linewidth=2
    ),
    annotations=[
        dict(
            x=0.5004254919715793,
            y=-0.18,
            showarrow=False,
            text='time, ms',
            font=dict(
                family='Times New Roman',
                size=22
            ),
            xref='paper',
            yref='paper'
        ),
        dict(
            x=-0.15,
            y=0.5,
            showarrow=False,
            text='amplitude, kHz',
            font=dict(
                family='Times New Roman',
                size=22
            ),
            textangle=-90,
            xref='paper',
            yref='paper'
        ),
    ],
    legend=dict(
        x=0.70,
        y=0.55,
        traceorder='normal',
        font=dict(
            family='Times New Roman',
            size=12,
            color='#000'
        ),
        bordercolor='#000000',
        borderwidth=2
    )
)

fig = dict(data=data, layout=layout)

plot(fig, filename = 'mmp_fig_1.html', config = config)

display(HTML('mmp_fig_1.html'))

(View plot code)

The linear phase inversion with the same time bandwidth product from the pevious demos is

%use octave

bl = msinc(256,2);
rfl = b2rf(bl);

re_rfscale_rfl = real(rfscale(rfl,8));

Comparing the two rf pulses

%use sos
%get re_rfscale_rfl --from Octave

plot_1 = go.Scatter(
    x = t,
    y = re_rfscale_rfm,
    name = 'minimum phase',
    text = 'minimum phase',
    hoverinfo = 'x+y+text',
    line = dict(
        color = ('rgb(22, 96, 167)'),
        ),
)

plot_2 = go.Scatter(
    x = t,
    y = re_rfscale_rfl,
    name = 'linear phase',
    text = 'linear phase',
    hoverinfo = 'x+y+text',
    line = dict(
        color = ('rgb(205, 12, 24)'),
        ),
)

data = [plot_1, plot_2]

layout = go.Layout(
    width=600,
    height=350,
    margin=go.layout.Margin(
        l=100,
        r=50,
        b=60,
        t=40,
    ),
    xaxis=dict(
        showgrid=False,
        linecolor='black',
        linewidth=2
    ),
    yaxis=dict(
        showgrid=False,
        linecolor='black',
        linewidth=2
    ),
    annotations=[
        dict(
            x=0.5004254919715793,
            y=-0.18,
            showarrow=False,
            text='time, ms',
            font=dict(
                family='Times New Roman',
                size=22
            ),
            xref='paper',
            yref='paper'
        ),
        dict(
            x=-0.15,
            y=0.5,
            showarrow=False,
            text='amplitude, kHz',
            font=dict(
                family='Times New Roman',
                size=22
            ),
            textangle=-90,
            xref='paper',
            yref='paper'
        ),
    ],
    legend=dict(
        x=0.65,
        y=0.65,
        traceorder='normal',
        font=dict(
            family='Times New Roman',
            size=12,
            color='#000'
        ),
        bordercolor='#000000',
        borderwidth=2
    )
)

fig = dict(data=data, layout=layout)

plot(fig, filename = 'mmp_fig_2.html', config = config)

display(HTML('mmp_fig_2.html'))

(View plot code)

We can compare the profiles as

%use octave

x = [-64:64]/4;
mzm = ab2inv(abr(rfm,x));
mzl = ab2inv(abr(rfl,x));
%use sos
%get x --from Octave
%get mzm --from Octave
%get mzl --from Octave

plot_1 = go.Scatter(
    x = x,
    y = mzm,
    name = 'minimum phase',
    text = 'minimum phase',
    hoverinfo = 'x+y+text',
    line = dict(
        color = ('rgb(22, 96, 167)'),
        ),
)

plot_2 = go.Scatter(
    x = x,
    y = mzl,
    name = 'linear phase',
    text = 'linear phase',
    hoverinfo = 'x+y+text',
    line = dict(
        color = ('rgb(205, 12, 24)'),
        ),
)

data = [plot_1, plot_2]

layout = go.Layout(
    width=600,
    height=350,
    margin=go.layout.Margin(
        l=100,
        r=50,
        b=60,
        t=40,
    ),
    xaxis=dict(
        showgrid=False,
        linecolor='black',
        linewidth=2
    ),
    yaxis=dict(
        showgrid=False,
        linecolor='black',
        linewidth=2
    ),
    annotations=[
        dict(
            x=0.5004254919715793,
            y=-0.18,
            showarrow=False,
            text='time, ms',
            font=dict(
                family='Times New Roman',
                size=22
            ),
            xref='paper',
            yref='paper'
        ),
        dict(
            x=-0.15,
            y=0.5,
            showarrow=False,
            text='amplitude, kHz',
            font=dict(
                family='Times New Roman',
                size=22
            ),
            textangle=-90,
            xref='paper',
            yref='paper'
        ),
    ],
    legend=dict(
        x=0.65,
        y=0.65,
        traceorder='normal',
        font=dict(
            family='Times New Roman',
            size=12,
            color='#000'
        ),
        bordercolor='#000000',
        borderwidth=2
    )
)

fig = dict(data=data, layout=layout)

plot(fig, filename = 'mmp_fig_3.html', config = config)

display(HTML('mmp_fig_3.html'))

(View plot code)

Click and drag to zoom in between the transition bands (time = 0 to 5 ms)

This can be useful for other pulses, such as slab select pulses

Something to try:

  • Design a minimum phase excitation pulse (scale $\beta$ to $\sqrt{2}/2$)
  • Compare the complex excitation profile of the min phase pulse, and a max phase pulse (min phase reversed in time).