├── Copyright_Information ├── LICENSE ├── README.md ├── create_freq_specs.m ├── examples ├── demo_7T.m ├── demo_C13_multiband.m ├── demo_C13_singleband.m ├── demo_general.m ├── demo_mband_phs.m ├── demo_multiband_spinecho.m ├── demo_ss_shifting.m └── singleband_C1_Pyr_demo.m ├── fftf.m ├── fftr.m ├── fir_expand.m ├── fir_linprog.m ├── fir_min_order.m ├── fir_min_order_linprog.m ├── fir_min_order_qprog.m ├── fir_min_order_qprog_phs.m ├── fir_minphase.m ├── fir_minphase_power.m ├── fir_pm.m ├── fir_pm_minpow.m ├── fir_qprog.m ├── fir_qprog_phs.m ├── grad_min_bridge.m ├── grad_mintrap.m ├── grad_ss.m ├── plot_spec.m ├── plot_spec_phs.m ├── rf_ripple.m ├── rf_tools ├── README ├── ab2ex.m ├── ab2inv.m ├── ab2sat.m ├── ab2se.m ├── ab2st.m ├── abr.m ├── cplot.m ├── csg.m ├── csg2.m ├── dimp.m ├── dinf.m ├── dz2d.m ├── dzbeta.m ├── dzepse.m ├── dzlp.m ├── dzls.m ├── dzmp.m ├── dzrf.m ├── dzsr.m ├── fftc.m ├── fmp.m ├── gt2cm.m ├── ifftc.m ├── iq2ap.m ├── ktog.m ├── ktos.m ├── loadwave.m ├── mag2mp.m ├── mex_files │ ├── abrx.c │ ├── abrx.m │ ├── abrx.mexmaci64 │ ├── abrx.mexw64 │ ├── b2a.c │ ├── b2a.code.c │ ├── b2rf.c │ ├── b2rf.m │ ├── b2rf.mexmaci64 │ ├── b2rf.mexw64 │ ├── cabc2rf.c │ ├── cabc2rf.code.c │ ├── four1.c │ └── minpeakrf.c ├── msinc.m ├── remod.m ├── rf_tools manual.pdf ├── rfscale.m ├── signa.m ├── t2hz.m ├── verse.m ├── versec.m └── zpl.m ├── signa.m ├── spec_interp.m ├── spec_interp_nonuniform.m ├── spectral_fact.m ├── ss_alias.m ├── ss_b1verse.m ├── ss_band_plot.m ├── ss_band_plot_phs.m ├── ss_design.m ├── ss_design_dyn.m ├── ss_design_phs.m ├── ss_ep.m ├── ss_ep_phs.m ├── ss_flyback.m ├── ss_flyback_phs.m ├── ss_globals.m ├── ss_opt.m ├── ss_plot.m ├── ss_response_mxy.m ├── ss_save.m ├── ss_save_dyn.m ├── ss_shift.m ├── ss_spect_correct.m ├── ss_verse.m └── ss_xlatebin.m /Copyright_Information: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LarsonLab/Spectral-Spatial-RF-Pulse-Design/59f4f3a2f404dad147ef292a16cc00be63a77cd7/Copyright_Information -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2011-2015, Board of Trustees, Leland Stanford Junior University and The Regents of the University of California. 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | * Neither the name of Spectral-Spatial-RF-Pulse-Design nor the names of its 15 | contributors may be used to endorse or promote products derived from 16 | this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 22 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | 29 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Spectral-Spatial-RF-Pulse-Design 2 | 3 | Spectral-Spatial RF Pulse Design for MRI and MRSI MATLAB Package 4 | Originally Written by: Adam B. Kerr and Peder E. Z. Larson 5 | 6 | ====================== 7 | 8 | This package includes Matlab functions to design spectral-spatial RF pulses (also known as spatial-spectral RF pulses) for application to magnetic resonance spectroscopy and imaging. See the "examples/" folder for sample code and pulse designs. 9 | 10 | Examples include excitation and refocusing pulses, as well as pulses for hyperpolarized carbon-13 MRI. 11 | 12 | ![image](https://github.com/LarsonLab/Spectral-Spatial-RF-Pulse-Design/assets/8160868/35f17e08-7885-4465-a5c1-7b6b9f864d3c) 13 | 14 | Spectral-spatial RF excitation pulse for MRI with hyperpolarized 15 | hyperpolarized [2-13C]dihydroxyacetone metabolism studies, https://doi.org/10.1002/mrm.26226 16 | 17 | Requirements: 18 | - Signal Processing Toolbox (MathWorks) 19 | - Optimization Toolbox (MathWorks) 20 | - rf_tools package : This is included in the package, and also available for download at http://rsl.stanford.edu/research/software.html 21 | 22 | ====================== 23 | 24 | All works deriving from this package must be properly cited. Please include the following sources of support: 25 | - Center for Advanced Magnetic Resonance Technology at Stanford (NIH P41-RR009784) 26 | - Hyperpolarized MRI Technology Resource Center at UCSF (NIH P41-EB013598) 27 | 28 | The following references describe the design methods used: 29 | - Kerr, A.B., Larson,P. E., Lustig, M., Cunningham, C. H., Chen, A. P., Vigneron, D. B., and Pauly, 30 | J. M. Multiband spectral-spatial design for high-field and hyperpolarized C-13 31 | applications. In Proceedings of the 16th Annual Meeting of ISMRM (Toronto, 32 | 2008), p. 226. 33 | - Larson, P. E. Z., Kerr, A. B., Chen, A. P., Lustig, M., 34 | Zierhut, M. L., Hu, S., Cunningham, C. H., Pauly, J. M., Kurhanewicz, J., and 35 | Vigneron, D. B. Multiband excitation pulses for hyperpolarized 13C dynamic 36 | chemical shift imaging. J Magn Reson 194, 1 (Sept. 2008), 121--127. 37 | 38 | ====================== 39 | 40 | For additional information, please contact Peder Larson (peder.larson@ucsf.edu) or Adam Kerr (akerr@stanford.edu) 41 | -------------------------------------------------------------------------------- /create_freq_specs.m: -------------------------------------------------------------------------------- 1 | function [fspec, a_angs, d] = create_freq_specs(mets, fmid) 2 | % CREATE_FREQ_SPECS - Helper function for creating spectral-spatial 3 | % frequency specifications 4 | % 5 | % [fspec, a_angs, d] = create_freq_specs(mets, fmid); 6 | % 7 | % INPUT 8 | % mets - structure containing: 9 | % mets(i).f - center frequency of bands (Hz) 10 | % mets(i).df - bandwidth of bands (Hz) 11 | % mets(i).ang - flip angle of bands (degress) 12 | % mets(i).d - ripple of bands (Mxy, default = .01) 13 | % fmid [optional] - center frequency spec around fmid 14 | % 15 | % OUTPUT 16 | % spec, a_angs, d - inputs for ss_design() 17 | 18 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 19 | % 20 | % Spectral-Spatial RF Pulse Design for MRI and MRSI MATLAB Package 21 | % 22 | % Authors: Adam B. Kerr and Peder E. Z. Larson 23 | % 24 | % (c)2007-2014 Board of Trustees, Leland Stanford Junior University and 25 | % The Regents of the University of California. 26 | % All Rights Reserved. 27 | % 28 | % Please see the Copyright_Information and README files included with this 29 | % package. All works derived from this package must be properly cited. 30 | % 31 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 32 | % 33 | 34 | for n = 1:length(mets) 35 | a_angs(n) = mets(n).ang*pi/180; 36 | fspec(2*n-1) = mets(n).f - mets(n).df; 37 | fspec(2*n) = mets(n).f + mets(n).df; 38 | if isfield(mets(n), 'd') && ~isempty(mets(n).d) 39 | d(n) = mets(n).d; 40 | else 41 | d(n) = .01; 42 | end 43 | end 44 | 45 | if nargin < 2 || isempty(fmid) 46 | % by default, center frequency specification 47 | fmid = (min(fspec)+max(fspec))/2; 48 | end 49 | 50 | fspec = fspec - fmid; 51 | -------------------------------------------------------------------------------- /examples/demo_7T.m: -------------------------------------------------------------------------------- 1 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 2 | % 3 | % Spectral-Spatial RF Pulse Design for MRI and MRSI MATLAB Package 4 | % 5 | % Authors: Adam B. Kerr and Peder E. Z. Larson 6 | % 7 | % (c)2007-2011 Board of Trustees, Leland Stanford Junior University and 8 | % The Regents of the University of California. 9 | % All Rights Reserved. 10 | % 11 | % Please see the Copyright_Information and README files included with this 12 | % package. All works derived from this package must be properly cited. 13 | % 14 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 15 | 16 | 17 | %% Reset SS package globals 18 | % 19 | 20 | ss_globals; 21 | fprintf(1, '************************************************************\n') 22 | fprintf(1, 'Here are some parameters that can be set:\n'); 23 | fprintf(1, 'You can type "help ss_globals" for more information on\n'); 24 | fprintf(1, 'each of them.\n'); 25 | fprintf(1, '************************************************************\n') 26 | fprintf(1, '\n'); 27 | ss_opt([]) 28 | 29 | 30 | %% Set up small-tip water/fat spectral/spatial 31 | % 32 | 33 | % Water/fat chemical shifts 34 | % 35 | df = 0.5e-6; % Conservative shim requirement 36 | water = 4.7e-6; 37 | fat2 = 1.3e-6; 38 | fat1 = 0.9e-6; 39 | 40 | % Convert to frequency 41 | % 42 | B0 = 70000; 43 | gamma_h = 4258; 44 | fspec = B0 * ([(fat1-df) (fat2+df) (water-df) (water+df)]-water) * gamma_h; 45 | 46 | water_ctr = (fspec(3) + fspec(4))/2; 47 | fat_ctr = (fspec(1) + fspec(2))/2; 48 | 49 | 50 | % Set up pulse parameters 51 | % 52 | ang = pi/6; 53 | z_thk = 1; 54 | z_tb = 4; 55 | 56 | % Set up spectral/spatial specifications 57 | % 58 | a = [0 1]; 59 | d = [0.01 0.01]; 60 | ptype = 'ex'; 61 | z_ftype='ls'; % Use this to get rid of "Conolly Wings" 62 | z_d1 = 0.01; 63 | z_d2 = 0.01; 64 | f_ctr = []; 65 | 66 | s_ftype = 'min'; % min-phase spectral 67 | ss_type = 'EP Whole'; 68 | dbg = 0; % dbg level 69 | % 0 -none, 1 - little, 2 -lots, ... 70 | 71 | default_opt = {'Max Duration', 20e-3, ... 72 | 'Max Grad', 4, 'Max Slew', 20, ... % 40 mT/m, 200 mT/m/ms 73 | 'Num Lobe Iters', 5, ... 74 | 'Min Order', 1, ... 75 | 'Spect Correct', 0, ... 76 | 'SLR', 0, ... 77 | 'Verse Fraction', 0.9}; 78 | 79 | opt = ss_opt(default_opt); 80 | 81 | % spectral correction 82 | ss_opt({'Spect Correct', 1, ... 83 | 'Spect Correct Reg', 0.001}); % small amount of regularization added to better condition spectral correction inversion 84 | 85 | 86 | 87 | fprintf(1, '\n************************************************************\n') 88 | fprintf(1, 'Here''s an example of a water/fat spectral spatial pulse for 7T\n\n'); 89 | fprintf(1, 'The pulse is a echo-planar (''EP'') design to accomodate the large\n'); 90 | fprintf(1, 'spectral bandwidth at ultra-high field.\n'); 91 | fprintf(1, 'However, EP pulses are more sensitive to eddy currents and timing errors.\n'); 92 | fprintf(1, 'The frequency spec includes a passband at [%3f,%3f]\n',fspec(3),fspec(4)); 93 | fprintf(1, 'and stopbands at [%3f,%3f]\n',fspec(1),fspec(2)); 94 | fprintf(1, 'This design also reduces stopband ripple using the ''Spect Correct'' option.\n'); 95 | fprintf(1, 'Here is an example of a fat-water EP trajectory design\n'); 96 | fprintf(1, '************************************************************\n'); 97 | 98 | [g_ew,rf_ew,fs,z,f,mxy] = ... 99 | ss_design(z_thk, z_tb, [z_d1 z_d2], fspec, a*ang, d, ptype, ... 100 | z_ftype, s_ftype, ss_type, f_ctr, dbg); 101 | 102 | set(gcf,'Name', 'Water/Fat EP Whole - Spect Correct'); 103 | 104 | 105 | 106 | 107 | -------------------------------------------------------------------------------- /examples/demo_C13_singleband.m: -------------------------------------------------------------------------------- 1 | % Demonstration of single-band hyperpolarized C-13 pulse designs for metabolite specific MRI 2 | 3 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 4 | % 5 | % Spectral-Spatial RF Pulse Design for MRI and MRSI MATLAB Package 6 | % 7 | % Authors: Adam B. Kerr and Peder E. Z. Larson 8 | % 9 | % (c)2007-2014 Board of Trustees, Leland Stanford Junior University and 10 | % The Regents of the University of California. 11 | % All Rights Reserved. 12 | % 13 | % Please see the Copyright_Information and README files included with this 14 | % package. All works derived from this package must be properly cited. 15 | % 16 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 17 | 18 | 19 | % Reset SS package globals 20 | % 21 | clear all 22 | ss_opt([]); 23 | ss_globals; 24 | clc 25 | 26 | %% lactate-only pulse 27 | fprintf(1, '\nHere''s a C13 single-band excitation pulse to excite only [1-13C]lactate\n'); 28 | fprintf(1, 'while not exciting pyruvate, alanine and pyruvate-hydrate, for a 3T clinical system\n\n'); 29 | 30 | % GENERAL PULSE PARAMETERS 31 | ss_type = 'EP Whole'; 32 | ptype = 'ex'; % excitation pulse 33 | opt = ss_opt({'Nucleus', 'Carbon', ... 34 | 'Max Duration', 25e-3, ... 35 | 'Spect Correct', 1}); 36 | 37 | % SPECTRAL PULSE PARAMETERS - large pass/stop bands chosen for wide 38 | % supression regions 39 | B0 = 3e4; % G 40 | df = 0.5e-6 * B0 * SS_GAMMA; % 0.5 ppm = gamma_C13 * B0 * 0.5e-6 41 | % metabolite frequency (Hz) freq bandwidth (Hz) flip angle (deg) 42 | mets(1).name = 'pyr'; mets(1).f = -230; mets(1).df = 2*df; mets(1).ang = 0; 43 | mets(2).name = 'ala'; mets(2).f = -45; mets(2).df = 3*df; mets(2).ang = 0; 44 | mets(3).name = 'pyrh'; mets(3).f = 40; mets(3).df = 2*df; mets(3).ang = 0; 45 | mets(4).name = 'lac'; mets(4).f = 165; mets(4).df = 2*df; mets(4).ang = 90; 46 | 47 | % create vectors of angles, ripples, and band edges for input to pulse design 48 | [fspec, a_angs, d] = create_freq_specs(mets); 49 | fctr = 0; % force pulse design to optimize for center of frequency specification 50 | s_ftype = 'min'; % minimum-phase spectral filter 51 | 52 | % SPATIAL PULSE PARAMETERS 53 | z_thk = .5; % thickness (cm) 54 | z_tb = 4; % time-bandwidth, proportional to profile sharpness 55 | z_ftype='ls'; % least-squares filter design 56 | z_d1 = 0.01; z_d2 = 0.01; % slice profile pass and stop-band ripples, respectively 57 | 58 | % DESIGN THE PULSE! 59 | [g,rf,fs,z,f,mxy] = ... 60 | ss_design(z_thk, z_tb, [z_d1 z_d2], fspec, a_angs, d, ptype, ... 61 | z_ftype, s_ftype, ss_type, fctr); 62 | set(gcf,'Name', '[1-13C]lac only for 3T clinical system'); 63 | 64 | fprintf(1,'Hit any key to continue:\n'); 65 | pause; 66 | 67 | %% lactate-only for high-field, small animal system 68 | fprintf(1, '\nHere is a similar pulse design, but for a 14T small-animal system\n\n'); 69 | 70 | clear all; ss_opt([]); ss_globals; % Reset all options 71 | 72 | % GENERAL PULSE PARAMETERS 73 | ss_type = 'EP Whole'; 74 | ptype = 'ex'; % excitation pulse 75 | opt = ss_opt({'Nucleus', 'Carbon', ... 76 | 'Max Duration', 10e-3, ... 77 | 'Max Grad', 50, ... % G/cm 78 | 'Max Slew', 200, ... % G/cm/ms 79 | 'Verse Fraction', 0.5, ... 80 | 'Spect Correct', 1}); 81 | 82 | % SPECTRAL PULSE PARAMETERS - large pass/stop bands chosen for wide 83 | % supression regions 84 | B0 = 14.1e4; % G 85 | df = 0.5e-6 * B0 * SS_GAMMA; % 0.5 ppm = gamma_C13 * B0 * 0.5e-6 86 | % metabolite frequency (Hz) freq bandwidth (Hz) flip angle (deg) 87 | mets(1).name = 'pyr'; mets(1).f = -1080; mets(1).df = 2*df; mets(1).ang = 0; 88 | mets(2).name = 'ala'; mets(2).f = -210; mets(2).df = 2*df; mets(2).ang = 0; 89 | mets(3).name = 'pyrh'; mets(3).f = 190; mets(3).df = 2*df; mets(3).ang = 0; 90 | mets(4).name = 'lac'; mets(4).f = 775; mets(4).df = 2*df; mets(4).ang = 90; 91 | 92 | % create vectors of angles, ripples, and band edges for input to pulse design 93 | [fspec, a_angs, d] = create_freq_specs(mets); 94 | fctr = 0; % force pulse design to optimize for center of frequency specification 95 | s_ftype = 'min'; % minimum-phase spectral filter 96 | 97 | % SPATIAL PULSE PARAMETERS 98 | z_thk = .5; % thickness (cm) 99 | z_tb = 3.5; % time-bandwidth, proportional to profile sharpness 100 | z_ftype='ls'; % least-squares filter design 101 | z_d1 = 0.01; z_d2 = 0.01; % slice profile pass and stop-band ripples, respectively 102 | 103 | % DESIGN THE PULSE! 104 | [g,rf,fs,z,f,mxy] = ... 105 | ss_design(z_thk, z_tb, [z_d1 z_d2], fspec, a_angs, d, ptype, ... 106 | z_ftype, s_ftype, ss_type, fctr); 107 | set(gcf,'Name', '[1-13C]lac only for 14T animal system'); 108 | 109 | fprintf(1,'Hit any key to continue:\n'); 110 | pause; 111 | 112 | 113 | %% lactate-only pulse 114 | fprintf(1, '\nFor studies including copolarized 13C-urea or a 13C-urea phantom\n'); 115 | fprintf(1, 'Here''s a C13 single-band excitation pulse to excite only [1-13C]lactate\n'); 116 | fprintf(1, 'while not exciting pyruvate, alanine and pyruvate-hydrate AND urea, for a 3T clinical system\n\n'); 117 | 118 | clear all; ss_opt([]); ss_globals; % Reset all options 119 | 120 | % GENERAL PULSE PARAMETERS 121 | ss_type = 'EP Whole'; 122 | ptype = 'ex'; % excitation pulse 123 | opt = ss_opt({'Nucleus', 'Carbon', ... 124 | 'Max Duration', 25e-3, ... 125 | 'Spect Correct', 1}); 126 | 127 | % SPECTRAL PULSE PARAMETERS - large pass/stop bands chosen for wide 128 | % supression regions 129 | B0 = 3e4; % G 130 | df = 0.5e-6 * B0 * SS_GAMMA; % 0.5 ppm = gamma_C13 * B0 * 0.5e-6 131 | % metabolite frequency (Hz) freq bandwidth (Hz) flip angle (deg) 132 | mets(1).name = 'urea'; mets(1).f = -470; mets(1).df = 2*df; mets(1).ang = 0; 133 | mets(2).name = 'pyr'; mets(2).f = -230; mets(2).df = 2*df; mets(2).ang = 0; 134 | mets(3).name = 'ala'; mets(3).f = -45; mets(3).df = 3*df; mets(3).ang = 0; 135 | mets(4).name = 'pyrh'; mets(4).f = 40; mets(4).df = 2*df; mets(4).ang = 0; 136 | mets(5).name = 'lac'; mets(5).f = 165; mets(5).df = 2*df; mets(5).ang = 90; 137 | 138 | % create vectors of angles, ripples, and band edges for input to pulse design 139 | [fspec, a_angs, d] = create_freq_specs(mets); 140 | fctr = 0; % force pulse design to optimize for center of frequency specification 141 | s_ftype = 'min'; % minimum-phase spectral filter 142 | 143 | % SPATIAL PULSE PARAMETERS 144 | z_thk = 1; % thickness (cm) 145 | z_tb = 4; % time-bandwidth, proportional to profile sharpness 146 | z_ftype='ls'; % least-squares filter design 147 | z_d1 = 0.01; z_d2 = 0.01; % slice profile pass and stop-band ripples, respectively 148 | 149 | % DESIGN THE PULSE! 150 | [g,rf,fs,z,f,mxy] = ... 151 | ss_design(z_thk, z_tb, [z_d1 z_d2], fspec, a_angs, d, ptype, ... 152 | z_ftype, s_ftype, ss_type, fctr); 153 | set(gcf,'Name', '[1-13C]lac only for 3T clinical system, avoiding urea'); 154 | -------------------------------------------------------------------------------- /examples/demo_mband_phs.m: -------------------------------------------------------------------------------- 1 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 2 | % 3 | % Spectral-Spatial RF Pulse Design for MRI and MRSI MATLAB Package 4 | % 5 | % Authors: Adam B. Kerr and Peder E. Z. Larson 6 | % 7 | % (c)2007-2011 Board of Trustees, Leland Stanford Junior University and 8 | % The Regents of the University of California. 9 | % All Rights Reserved. 10 | % 11 | % Please see the Copyright_Information and README files included with this 12 | % package. All works derived from this package must be properly cited. 13 | % 14 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 15 | 16 | 17 | % Reset SS package globals 18 | % 19 | ss_opt([]); 20 | ss_globals; 21 | 22 | 23 | %% 24 | clc 25 | 26 | fprintf(1, '\nHere is a demo of the multiband phase specification capability of the toolbox\n'); 27 | fprintf(1, '\nCurrently not working, check back later!\n'); 28 | return 29 | fprintf(1, 'This example is a C13 multiband excitation pulse example, for [1-13C]pyr+13C-urea\n'); 30 | fprintf(1, 'dynamic MR spectroscopic or chemical shift imaging on a 3T clinical system\n'); 31 | fprintf(1, 'Pyruvate-hydrate and alanine are specified to have a 90-degree phase shift\n'); 32 | fprintf(1, '(Be patient since this filter design is much slower)\n\n'); 33 | 34 | % setup flip angles and bandwidths, and ripple 35 | df = 0.5e-6 * 30000 * 1070;% 0.5 ppm = gamma_C13 * 30000 * 0.5e-6 36 | 37 | phs_spec = 1 * pi / 180; 38 | 39 | mets(5).name = 'lac'; mets(5).f = 165; mets(5).df = 1.5*df; mets(5).ang = 12;mets(5).d = .005; 40 | mets(5).phs = pi/2;mets(5).dphs=phs_spec; 41 | 42 | mets(4).name = 'pyrh'; mets(4).f = 40; mets(4).df = 1*df; mets(4).ang = 2.5; mets(4).d = .015; 43 | mets(4).phs = 0;mets(4).dphs=phs_spec; 44 | 45 | mets(3).name = 'ala'; mets(3).f = -45; mets(3).df = 1.5*df; mets(3).ang = 12; mets(3).d = .005; 46 | mets(3).phs = 0;mets(3).dphs=phs_spec; 47 | 48 | mets(2).name = 'pyr'; mets(2).f = -230; mets(2).df = 2*df; mets(2).ang = 6; mets(2).d = .002; 49 | mets(2).phs = 0;mets(2).dphs=phs_spec; 50 | 51 | mets(1).name = 'urea'; mets(1).f = -465; mets(1).df = 2*df; mets(1).ang = 6; mets(1).d = .002; 52 | mets(1).phs = 0;mets(1).dphs=phs_spec; 53 | 54 | 55 | 56 | Npe = 8; % number of phase encode steps 57 | 58 | % create vectors of angles, ripples, and band edges 59 | clear a_angs d fspec a_phs d_phs 60 | for n = 1:length(mets) 61 | a_angs(n) = mets(n).ang*pi/180; 62 | d(n) = mets(n).d; 63 | a_phs(n) = mets(n).phs; 64 | d_phs(n) = mets(n).dphs; 65 | fspec(2*n-1) = mets(n).f - mets(n).df; 66 | fspec(2*n) = mets(n).f + mets(n).df; 67 | % disp([mets(n).name ' metab fraction remaining after Npe = ' int2str(Npe) ' encodes: ' num2str(cos(a_angs(n))^Npe)]) 68 | end 69 | disp(''); 70 | 71 | ang = max(a_angs); 72 | 73 | fmid = (mets(1).f+mets(end).f)/2; 74 | fspec = fspec - fmid; 75 | fctr = 0; 76 | 77 | z_thk = 6; z_tb = 4; ss_type = 'Flyback Whole'; 78 | 79 | ptype = 'ex'; 80 | z_ftype='ls'; 81 | z_d1 = 0.002; 82 | z_d2 = 0.01; 83 | 84 | s_ftype = 'lin'; 85 | 86 | ss_opt([]); % Reset all options 87 | opt = ss_opt({'Nucleus', 'Carbon', ... 88 | 'Max Duration', 24e-3, ... 89 | 'Num Lobe Iters', 5, ... 90 | 'Num Fs Test', 100, ... 91 | 'Verse Fraction', 0.2, ... 92 | 'Min Order', 1,... 93 | 'Spect Correct', 1}); 94 | 95 | [g,rf,fs,z,f,mxy,isodelay] = ... 96 | ss_design_phs(z_thk, z_tb, [z_d1 z_d2], fspec, a_angs, d, a_phs, d_phs, ptype, ... 97 | z_ftype, s_ftype, ss_type, fctr, 0); 98 | set(gcf,'Name', '[1-13C]pyr+13C-urea Multiband - defined phase'); 99 | 100 | g_opt = g; 101 | rf_opt = rf; 102 | 103 | return 104 | %% Extra plots and pulse saving 105 | % Now compare spectral profiles at z = 0 106 | ftest = [min([fspec -fs/2]):max([fs/2 fspec])]; 107 | mxy_new = ss_response_mxy(g_opt, rf_opt, 0, ftest, SS_TS, SS_GAMMA); 108 | 109 | figure; 110 | hold on; 111 | nband = length(fspec)/2; 112 | for band = 1:nband, 113 | if a_angs(band) > 0, 114 | plot(fspec(band*2-1:band*2), ones(1,2)*(sin(a_angs(band))+d(band)+z_d1), 'k--'); 115 | plot(fspec(band*2-1:band*2), ones(1,2)*(sin(a_angs(band))-d(band)-z_d1), ... 116 | 'k--'); 117 | else 118 | plot(fspec(band*2-1:band*2), ones(1,2)*(sin(a_angs(band))+d(band)), 'k--'); 119 | plot(fspec(band*2-1:band*2), ones(1,2)*(sin(a_angs(band))-d(band)), ... 120 | 'k--'); 121 | end; 122 | end; 123 | plot(ftest, abs(mxy_new)); 124 | plot([-fs/2 -fs/2], [0 sin(ang)], 'g--'); 125 | plot([fs/2 fs/2], [0 sin(ang)], 'g--'); 126 | hold off 127 | 128 | figure; 129 | subplot(211) 130 | t_opt = [0:length(g_opt)-1] * SS_TS * 1e3; 131 | plot(t_opt, g_opt); 132 | title('Gradient Waveforms'); 133 | 134 | subplot(212) 135 | plot(t_opt, real(rf_opt),t_opt, imag(rf_opt)); 136 | title('RF Waveforms'); 137 | 138 | ss_save(g,rf,pi/2,z_thk,isodelay); 139 | 140 | 141 | -------------------------------------------------------------------------------- /examples/demo_multiband_spinecho.m: -------------------------------------------------------------------------------- 1 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 2 | % 3 | % Spectral-Spatial RF Pulse Design for MRI and MRSI MATLAB Package 4 | % 5 | % Authors: Adam B. Kerr and Peder E. Z. Larson 6 | % 7 | % (c)2007-2011 Board of Trustees, Leland Stanford Junior University and 8 | % The Regents of the University of California. 9 | % All Rights Reserved. 10 | % 11 | % Please see the Copyright_Information and README files included with this 12 | % package. All works derived from this package must be properly cited. 13 | % 14 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 15 | 16 | 17 | % Reset SS package globals 18 | % 19 | ss_opt([]); 20 | ss_globals; 21 | 22 | 23 | %% 24 | clc 25 | fprintf(1, '\nHere''s a multiband spin-echo pulse for MR spectroscopy at 1.5T\n'); 26 | fprintf(1, 'It includes fat suppression and partial water suppression\n'); 27 | fprintf(1, 'The specification includes: \n'); 28 | fprintf(1, '\t-no refocusing of fat (0-degree flip at ~ -220 Hz)\n'); 29 | fprintf(1, '\t-full refocusing of choline and citrate (180-degree flip from -75 to -140 Hz)\n'); 30 | fprintf(1, '\t-partial refocusing of water (~37-degree flip at 0 Hz)\n'); 31 | fprintf(1, '(similar to Schricker et al, Magn Reson Med 46: 1079-1087 (2001), DOI: 10.1002/mrm.1302 )\n'); 32 | fprintf(1,'Hit any key to continue:\n'); 33 | pause 34 | 35 | % Water/fat chemical shifts (ppm) 36 | % 37 | df = 0.5e-6; % Conservative +-0.5 ppm shim requirement 38 | 39 | fat1 = 0.9e-6; 40 | fat2 = 1.3e-6; 41 | cho = 3e-6; 42 | water = 4.7e-6; 43 | 44 | % Convert to frequency specification 45 | % 46 | B0 = 15000; 47 | gamma_h = 4258; 48 | % bands: fat, cho, water 49 | fspec = B0 * ([(fat1-df) (fat2+df) (cho-df) (cho+df) (water-df) (water+df)]-water) * gamma_h; 50 | mxy = [0 1 0.1]; % refocused mxy for a single 180 pulse 51 | d = [0.005 0.01 0.005]; % mxy ripple values 52 | 53 | fat_ctr = ((fat1+fat2)/2-water)*B0 * gamma_h; 54 | cho_ctr = (cho-water)*B0 * gamma_h; 55 | water_ctr = 0; 56 | 57 | % Set up pulse parameters 58 | % 59 | z_thk = 1; % cm 60 | z_tb = 5; % time-bandwidth 61 | 62 | % Set up spectral/spatial specifications 63 | % 64 | 65 | a_ang = 2*asin(sqrt(mxy)); % convert Mxy amplitude to flip angle 66 | ptype = 'se'; % pulse type: 'se' spin-echo 67 | z_ftype='ls'; % Use this to get rid of "Conolly Wings" 68 | z_d1 = 0.01; 69 | z_d2 = 0.01; 70 | f_ctr = cho_ctr; 71 | 72 | s_ftype = 'max'; % max-phase spectral 73 | ss_type = 'Flyback Whole'; 74 | dbg = 0; % dbg level: 0 -none, 1 - little, 2 -lots, ... 75 | 76 | ss_opt([]); 77 | opt = ss_opt({'Max Duration', 30e-3, ... % ms 78 | 'Num Lobe Iters', 5, ... 79 | 'Spect Correct', 0, ... 80 | 'SLR', 1, ... 81 | 'Verse Fraction', 0.8, ... 82 | 'Max Grad', 4, ... % these gradient specifications could be updated for modern systems 83 | 'Max Slew', 15, ... 84 | 'Min Order', 1}) 85 | 86 | 87 | [g,rf,z,f,mxy] = ss_design(z_thk, z_tb, [z_d1 z_d2], fspec, a_ang, d, ptype, ... 88 | z_ftype, s_ftype, ss_type, f_ctr, dbg); 89 | set(gcf,'Name', 'MRS Dual-band spin-echo pulse'); 90 | 91 | fprintf(1,'Hit any key to continue:\n'); 92 | pause 93 | 94 | %% 95 | fprintf(1, '\nBy default the spectral-spatial package uses the shortest possible RF pulse,\n'); 96 | fprintf(1, 'but this can cause large ripples outside of the specified bands, as seen in this pulse.\n'); 97 | fprintf(1, 'The ripples, and also the peak and total power, are reduced by turning off the\n'); 98 | fprintf(1, 'Min Order option, in which case the resulting pulses are the Max Duration:\n'); 99 | 100 | opt = ss_opt({'Min Order', 0}) 101 | [g,rf,z,f,mxy] = ss_design(z_thk, z_tb, [z_d1 z_d2], fspec, a_ang, d, ptype, ... 102 | z_ftype, s_ftype, ss_type, f_ctr, dbg); 103 | set(gcf,'Name', 'MRS Dual-band spin-echo pulse - Reduced power, longer duration'); 104 | -------------------------------------------------------------------------------- /examples/demo_ss_shifting.m: -------------------------------------------------------------------------------- 1 | % Script to test slice and frequency shifting 2 | % Uses a singleband SPSP RF pulse, suitable for imaging 3 | % pyruvate, lactate, and bicarbonate in an HP 1-13C pyruvate experiment 4 | 5 | close all, clearvars; clc 6 | ss_globals; 7 | ss_opt([]); % Reset all options 8 | opt = ss_opt({'Nucleus', 'Carbon', ... 9 | 'Max Duration', 30e-3, ... 10 | 'Num Lobe Iters', 10, ... 11 | 'Max B1', 1.6, ... 12 | 'Num Fs Test', 100, ... 13 | 'Verse Fraction', 0.7, ... 14 | 'SLR', 0, ... 15 | 'B1 Verse', 0, ... 16 | 'Min Order', 1,... 17 | 'Spect Correct', 1,... 18 | 'Max Grad',5,... 19 | 'Max Slew',20}); 20 | 21 | % SPECTRAL PULSE PARAMETERS 22 | % Design a pulse suitable for imaging C1 pyruvate, lactate, and bicarbonate 23 | % at 3 T 24 | % 25 | % All frequencies are relative to C1 pyruvate 26 | % Note that freq bandwidth is +/-, NOT TOTAL. Need to double to get total bandwidth. 27 | % To generate a pulse suitable for imaging all metabolites, for best 28 | % results place a broad symmetric stopband around the central resonance 29 | B0 = 3e4; % G 30 | c13ppm = 1e-6 * B0 * SS_GAMMA; % 1 ppm = gamma_C13 * B0 * 1e-6 31 | 32 | % 3T frequency shifts for C1-pyruvate studies 33 | freq_c1 = [395 270 180 0 -322]; %Lac, Pyr-Hydrate, Ala, Pyr, Bic 34 | metabolite_c1 = {'Lactate','Pyruvate-hydrate','Alanine','Pyruvate','Bicarbonate'}; 35 | 36 | % metabolite frequency (Hz) freq bandwidth (Hz) flip angle (deg) allowed ripple 37 | mets(1).name = 'passband'; mets(1).f = 0; mets(1).df = 1*c13ppm; mets(1).ang = 90; mets(1).d = .01; 38 | 39 | fmin_stopband = freq_c1(1) - freq_c1(2); 40 | fmax_stopband = freq_c1(1) - freq_c1(5) + 1*c13ppm; 41 | fcenter_stopband = (fmin_stopband + fmax_stopband)/2; 42 | df_stopband = (fmax_stopband - fmin_stopband)/2; 43 | 44 | mets(2).name = 'lac_bic_stopband1'; mets(2).f = fcenter_stopband; mets(2).df = df_stopband; mets(2).ang = 0; mets(2).d = .005; % low ripple here important to not excite the large pyruvate when exciting other metabolites 45 | mets(3).name = 'bic_lac_stopband2'; mets(3).f = -fcenter_stopband; mets(3).df = df_stopband; mets(3).ang = 0; mets(3).d = .005; % low ripple here important to not excite the large pyruvate when exciting other metabolites 46 | 47 | % create vectors of angles, ripples, and band edges for input to pulse design 48 | [fspec, a_angs, d] = create_freq_specs(mets, 0); 49 | fctr = 0; % force pulse design to optimize for center of frequency specification 50 | s_ftype = 'min'; % minimimum-phase spectral filter 51 | 52 | % SPATIAL PULSE PARAMETERS 53 | % The SPSP type has the biggest impact on the overall response. 54 | % Echoplanar designs will have a larger Fs and can provide thinner slices, 55 | % but they are more sensitive to RF/gradient timing errors and the opposed 56 | % null at Fs/2 can make it difficult to find a pulse suitable for imaging 57 | % all metabolites 58 | z_thk = 1.25; %slice thickness (cm) 59 | z_tb = 2; %spatial time bandwidth 60 | ss_type = 'Flyback Whole'; %spsp type 61 | % ss_type = 'EP Whole'; %spsp type - this should also work for echo-planar design 62 | ptype = 'ex'; %excitation 63 | z_ftype = 'ls'; %spatial filter 64 | z_d1 = 0.01; %passband ripple 65 | z_d2 = 0.01; %stopband ripple 66 | 67 | % 1) Fs: 868.1 B1: 0.223G Power: 3.051e-05 G^2 ms Dur: 20.6m 68 | [g,rf,fs] = ... 69 | ss_design(z_thk, z_tb, [z_d1 z_d2], fspec, a_angs, d, ptype, ... 70 | z_ftype, [], ss_type, fctr, 0); 71 | 72 | %% test frequency shifing 73 | 74 | close all; 75 | 76 | for Ifreq = 1:length(freq_c1) 77 | rf_shift = ss_shift(g,rf,0,freq_c1(Ifreq)); 78 | ss_plot(g, rf_shift, SS_TS, ptype, z_thk*3, 2.5*[min(fspec) max(fspec)], SS_GAMMA, freq_c1); 79 | set(gcf,'Name',[metabolite_c1{Ifreq} ' Excitation'],'NumberTitle','Off') 80 | 81 | end 82 | 83 | %% test slice shifting 84 | num_slices = 5; 85 | slice_gap = 0; 86 | z_shift = (z_thk+slice_gap)*([1:num_slices] - (num_slices+1)/2); 87 | 88 | fplot = freq_c1 - freq_c1(4); 89 | for Islice = 1:num_slices 90 | rf_shift = ss_shift(g, rf, z_shift(Islice)); 91 | ss_plot(g, rf_shift, SS_TS, ptype, z_thk*num_slices, 2.5*[min(fspec) max(fspec)], SS_GAMMA, fplot); 92 | end 93 | 94 | -------------------------------------------------------------------------------- /examples/singleband_C1_Pyr_demo.m: -------------------------------------------------------------------------------- 1 | % Script to generate a singleband SPSP RF pulse, suitable for imaging 2 | % pyruvate, lactate, and bicarbonate in an HP 1-13C pyruvate experiment 3 | 4 | close all, clearvars; clc 5 | ss_globals; 6 | ss_opt([]); % Reset all options 7 | opt = ss_opt({'Nucleus', 'Carbon', ... 8 | 'Max Duration', 20.5e-3, ... 9 | 'Num Lobe Iters', 15, ... 10 | 'Max B1', 1.6, ... 11 | 'Num Fs Test', 100, ... 12 | 'Verse Fraction', 0.7, ... 13 | 'SLR', 0, ... 14 | 'B1 Verse', 0, ... 15 | 'Min Order', 0,... 16 | 'Spect Correct', 1,... 17 | 'Max Grad',5,... 18 | 'Max Slew',20}); 19 | 20 | % SPECTRAL PULSE PARAMETERS 21 | % Design a pulse suitable for imaging C1 pyruvate, lactate, and bicarbonate 22 | % at 3 T 23 | % 24 | % All frequencies are relative to C1 pyruvate 25 | % Note that freq bandwidth is +/-, NOT TOTAL. Need to double to get total bandwidth. 26 | % To generate a pulse suitable for imaging all metabolites, for best 27 | % results place a broad symmetric stopband around the central resonance 28 | B0 = 3e4; % G 29 | c13ppm = 1e-6 * B0 * SS_GAMMA; % 1 ppm = gamma_C13 * B0 * 1e-6 30 | % metabolite frequency (Hz) freq bandwidth (Hz) flip angle (deg) allowed ripple 31 | mets(1).name = 'passband'; mets(1).f = 0; mets(1).df = 1*c13ppm; mets(1).ang = 90; mets(1).d = .01; 32 | mets(2).name = 'stopband1'; mets(2).f = 9*c13ppm; mets(2).df = 5*c13ppm; mets(2).ang = 0; mets(2).d = .005; 33 | mets(3).name = 'stopband2'; mets(3).f = -9*c13ppm; mets(3).df = 5*c13ppm; mets(3).ang = 0; mets(3).d = .005; 34 | 35 | % create vectors of angles, ripples, and band edges for input to pulse design 36 | [fspec, a_angs, d] = create_freq_specs(mets, 0); 37 | fctr = 0; % force pulse design to optimize for center of frequency specification 38 | s_ftype = 'min'; % minimimum-phase spectral filter 39 | 40 | % SPATIAL PULSE PARAMETERS 41 | % The SPSP type has the biggest impact on the overall response. 42 | % Echoplanar designs will have a larger Fs and can provide thinner slices, 43 | % but they are more sensitive to RF/gradient timing errors and the opposed 44 | % null at Fs/2 can make it difficult to find a pulse suitable for imaging 45 | % all metabolites 46 | z_thk = 1.25; %slice thickness (cm) 47 | z_tb = 2; %spatial time bandwidth 48 | ss_type = 'Flyback Whole'; %spsp type 49 | ptype = 'ex'; %excitation 50 | z_ftype = 'ls'; %spatial filter 51 | z_d1 = 0.01; %passband ripple 52 | z_d2 = 0.01; %stopband ripple 53 | 54 | % 1) Fs: 868.1 B1: 0.223G Power: 3.051e-05 G^2 ms Dur: 20.6m 55 | [g,rf,fs] = ... 56 | ss_design(z_thk, z_tb, [z_d1 z_d2], fspec, a_angs, d, ptype, ... 57 | z_ftype, [], ss_type, fctr, 0); 58 | %% Simulate SPSP response 59 | % With the SPSP pulse above, confirm frequency selectivity when moving 60 | % between metabolites. Need to ensure that off-resonance stopband 61 | % excitation <1% since the RF pulse is doing the spectral encoding 62 | 63 | close all; 64 | freq_c1 = [395 270 180 0 -322]; %Lac, Pyr-Hydrate, Ala, Pyr, Bic 65 | % Pyr 66 | fplot = freq_c1 - freq_c1(4); 67 | ss_plot(g, rf, SS_TS, ptype, z_thk*3, 2.5*[min(fspec) max(fspec)], SS_GAMMA, fplot); 68 | set(gcf,'Name','C1 Pyruvate Excitation','NumberTitle','Off') 69 | 70 | % Lac 71 | fplot = freq_c1 - freq_c1(1); 72 | ss_plot(g, rf, SS_TS, ptype, z_thk*3, 2.5*[min(fspec) max(fspec)], SS_GAMMA, fplot); 73 | set(gcf,'Name','C1 Lactate Excitation','NumberTitle','Off') 74 | 75 | % Bic 76 | fplot = freq_c1 - freq_c1(5); 77 | ss_plot(g, rf, SS_TS, ptype, z_thk*3, 2.5*[min(fspec) max(fspec)], SS_GAMMA, fplot); 78 | set(gcf,'Name','Bicarbonate Excitation','NumberTitle','Off') 79 | 80 | % Ala 81 | fplot = freq_c1 - freq_c1(3); 82 | ss_plot(g, rf, SS_TS, ptype, z_thk*3, 2.5*[min(fspec) max(fspec)], SS_GAMMA, fplot); 83 | set(gcf,'Name','C1 Alanine Excitation','NumberTitle','Off') 84 | 85 | %% Adapt singleband SPSP for alanine 86 | % As seen above, the passband FWHM was too broad to selectively excite 87 | % alanine. To create one that is sufficiently narrow, we will need to reduce 88 | % the passband FWHM and increase the Max Duration in the SS_OPT structure, 89 | % since the total pulsewidth will ultimately limit the passband response 90 | % (for Gaussiun modulation, minimum passband FWHM = 2/pw). 91 | opt = ss_opt({'Max Duration', 24e-3}); 92 | mets(1).df = 0.5*c13ppm; 93 | % Increase stopband width to help suppress ripples from the passband transition 94 | mets(2).df = 6*c13ppm; 95 | mets(3).df = 6*c13ppm; 96 | % Regenerate passband & stopband specifications 97 | [fspec, a_angs, d] = create_freq_specs(mets, 0); 98 | 99 | close all; 100 | % 1) Fs: 868.1 B1: 0.141G Power: 1.765e-05 G^2 ms Dur: 24.0msms 101 | [g,rf,fs] = ... 102 | ss_design(z_thk, z_tb, [z_d1 z_d2], fspec, a_angs, d, ptype, ... 103 | z_ftype, [], ss_type, fctr, 0); 104 | 105 | %% Simulate SPSP response 106 | % With this updated SPSP pulse, the passband will now be selective enough 107 | % to image alanine as well 108 | close all; 109 | 110 | % Pyr 111 | fplot = freq_c1 - freq_c1(4); 112 | ss_plot(g, rf, SS_TS, ptype, z_thk*3, 2.5*[min(fspec) max(fspec)], SS_GAMMA, fplot); 113 | set(gcf,'Name','C1 Pyruvate Excitation','NumberTitle','Off') 114 | 115 | % Lac 116 | fplot = freq_c1 - freq_c1(1); 117 | ss_plot(g, rf, SS_TS, ptype, z_thk*3, 2.5*[min(fspec) max(fspec)], SS_GAMMA, fplot); 118 | set(gcf,'Name','C1 Lactate Excitation','NumberTitle','Off') 119 | 120 | % Bic 121 | fplot = freq_c1 - freq_c1(5); 122 | ss_plot(g, rf, SS_TS, ptype, z_thk*3, 2.5*[min(fspec) max(fspec)], SS_GAMMA, fplot); 123 | set(gcf,'Name','Bicarbonate Excitation','NumberTitle','Off') 124 | 125 | % Ala 126 | fplot = freq_c1 - freq_c1(3); 127 | ss_plot(g, rf, SS_TS, ptype, z_thk*3, 2.5*[min(fspec) max(fspec)], SS_GAMMA, fplot); 128 | set(gcf,'Name','C1 Alanine Excitation','NumberTitle','Off') -------------------------------------------------------------------------------- /fftf.m: -------------------------------------------------------------------------------- 1 | function xf = fftf(x, N, dim) 2 | % FFTF - Forward Fourier transform, shifted 3 | % 4 | % function xf = fftf(x, N, dim) 5 | % 6 | % x -- time samples 7 | % N -- dimension of Fourier space 8 | % - length of vector if unspecified 9 | % - length of column if unspecified and xt a matrix 10 | % dim -- dimension to operate over, only used if matrix 11 | % - column dimension if unspecified 12 | % 13 | 14 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 15 | % 16 | % Spectral-Spatial RF Pulse Design for MRI and MRSI MATLAB Package 17 | % 18 | % Authors: Adam B. Kerr and Peder E. Z. Larson 19 | % 20 | % (c)2007-2011 Board of Trustees, Leland Stanford Junior University and 21 | % The Regents of the University of California. 22 | % All Rights Reserved. 23 | % 24 | % Please see the Copyright_Information and README files included with this 25 | % package. All works derived from this package must be properly cited. 26 | % 27 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 28 | % 29 | % $Header: /home/adam/cvsroot/src/ss/fftf.m,v 1.3 2012/02/01 00:41:22 peder Exp $ 30 | % 31 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 32 | 33 | xsize = size(x); 34 | if length(xsize) > 2, 35 | error('Only handles 2D matrices'); 36 | end; 37 | 38 | if nargin < 2, 39 | [mnval, mndim] = min(xsize); 40 | if mnval > 1, 41 | dim = 1; 42 | N = xsize(dim); 43 | else 44 | dim = 3-mndim; % Get opposite of mndim 45 | N = xsize(dim); 46 | end; 47 | elseif nargin < 3, 48 | [mnval, mndim] = min(xsize); 49 | if mnval > 1, 50 | dim = 1; 51 | if (N < xsize(dim)) 52 | error('N less than number of rows of x'); 53 | end; 54 | else 55 | dim = 3-mndim; % Get opposite of mndim 56 | if (N < xsize(dim)) 57 | error('N less than number of elements of x'); 58 | end; 59 | end; 60 | end; 61 | 62 | % Create zeropad array 63 | % 64 | fsize = xsize; 65 | fsize(dim) = N; 66 | pad = zeros(fsize); 67 | 68 | % Fill in pad array 69 | % 70 | Nd2 = ceil((N+1)/2); 71 | nxd2 = ceil((xsize(dim)+1)/2); 72 | sidx = Nd2 - nxd2 + 1; 73 | if dim == 1, 74 | pad(sidx:sidx+xsize(dim)-1, :) = x; 75 | else 76 | pad(:, sidx:sidx+xsize(dim)-1) = x; 77 | end; 78 | 79 | xf = fftshift(fft(ifftshift(pad,dim),N,dim), dim); 80 | 81 | return; 82 | -------------------------------------------------------------------------------- /fftr.m: -------------------------------------------------------------------------------- 1 | function x = fftr(xf, N, dim) 2 | % FFTR - Reverse Fourier transform, shifted 3 | % 4 | % function x = fftr(xf, N, dim) 5 | % 6 | % xf -- Fourier samples 7 | % N -- dimension of x space 8 | % - length of vector if unspecified 9 | % - length of column if unspecified and xt a matrix 10 | % dim -- dimension to operate over, only used if matrix 11 | % - column dimension if unspecified 12 | % 13 | 14 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 15 | % 16 | % Spectral-Spatial RF Pulse Design for MRI and MRSI MATLAB Package 17 | % 18 | % Authors: Adam B. Kerr and Peder E. Z. Larson 19 | % 20 | % (c)2007-2011 Board of Trustees, Leland Stanford Junior University and 21 | % The Regents of the University of California. 22 | % All Rights Reserved. 23 | % 24 | % Please see the Copyright_Information and README files included with this 25 | % package. All works derived from this package must be properly cited. 26 | % 27 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 28 | % 29 | % $Header: /home/adam/cvsroot/src/ss/fftr.m,v 1.3 2012/02/01 00:41:22 peder Exp $ 30 | % 31 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 32 | 33 | xfsize = size(xf); 34 | if length(xfsize) > 2, 35 | error('Only handles 2D matrices'); 36 | end; 37 | 38 | if nargin < 2, 39 | [mnval, mndim] = min(xfsize); 40 | if mnval > 1, 41 | dim = 1; 42 | N = xfsize(dim); 43 | else 44 | dim = 3-mndim; % Get opposite of mndim 45 | N = xfsize(dim); 46 | end; 47 | elseif nargin < 3, 48 | [mnval, mndim] = min(xfsize); 49 | if mnval > 1, 50 | dim = 1; 51 | if (N > xfsize(dim)) 52 | error('N greater than number of rows of xf'); 53 | end; 54 | else 55 | dim = 3-mndim; % Get opposite of mndim 56 | if (N > xfsize(dim)) 57 | error('N greater than number of elements of xf'); 58 | end; 59 | end; 60 | end; 61 | 62 | % Get transform along dim 63 | % 64 | xpad = fftshift(ifft(ifftshift(xf,dim),[],dim),dim); 65 | 66 | % Fill in pad array 67 | % 68 | Nd2 = ceil((N+1)/2); 69 | nxfd2 = ceil((xfsize(dim)+1)/2); 70 | sidx = nxfd2 - Nd2 + 1; 71 | if dim == 1, 72 | x = xpad(sidx:sidx+N-1, :); 73 | else 74 | x = xpad(:,sidx:sidx+N-1); 75 | end; 76 | 77 | return; 78 | -------------------------------------------------------------------------------- /fir_min_order.m: -------------------------------------------------------------------------------- 1 | % Determines minimum-order linear-phase filter that meets 2 | % amplitude/ripple specifications. 3 | % 4 | % function [h, status] = fir_min_order(n, f, a, d, odd_or_even, a_min, dbg) 5 | % 6 | % Inputs: --- similar to cfirpm 7 | % n: max number of taps to try 8 | % f: frequency bands 9 | % a: amplitude at band edges 10 | % d: ripple in bands 11 | % even_odd: 1 - odd only 12 | % 2 - even only 13 | % anything else - either odd or even 14 | % a_min: minimum amplitude in transition regions, if [], then 15 | % min(0, min(a-d)) is used 16 | % dbg: flag to turn on debugging statements/plots 17 | % 18 | 19 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 20 | % 21 | % Spectral-Spatial RF Pulse Design for MRI and MRSI MATLAB Package 22 | % 23 | % Authors: Adam B. Kerr and Peder E. Z. Larson 24 | % 25 | % (c)2007-2011 Board of Trustees, Leland Stanford Junior University and 26 | % The Regents of the University of California. 27 | % All Rights Reserved. 28 | % 29 | % Please see the Copyright_Information and README files included with this 30 | % package. All works derived from this package must be properly cited. 31 | % 32 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 33 | % 34 | % $Header: /home/adam/cvsroot/src/ss/fir_min_order.m,v 1.11 2012/02/01 00:41:22 peder Exp $ 35 | % 36 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 37 | % 38 | % 39 | % Bisection search calling fir_pm to determine minimum order 40 | % filter that meets linear phase requirements. By default it will check both 41 | % odd and even length filters. If a non-zero point exists at +/- 1, it will 42 | % only check odd filters. Only odd or even filters can be specified by the 43 | % parameter odd_even. 44 | % 45 | % Inputs: 46 | % n - number of taps 47 | % f - frequency bands 48 | % a - amplitudes at band edges 49 | % even_odd: 1 - odd only 50 | % 2 - even only 51 | % anything else - either odd or even 52 | % dbg - level of debug info to print 53 | % 54 | 55 | function [h, status] = fir_min_order(n, f, a, d, even_odd, a_min, dbg) 56 | if nargin < 4, 57 | error(['Usage: function [h, status] = fir_min_order(n, f, a, d, even_odd,' ... 58 | ' a_min, dbg)']); 59 | end; 60 | 61 | if nargin < 5 || isempty(even_odd), 62 | even_odd = 0; 63 | end; 64 | switch (even_odd) 65 | case {1, 2} 66 | otherwise 67 | even_odd = 0; 68 | end; 69 | 70 | if nargin < 6, 71 | a_min = []; 72 | end; 73 | 74 | if nargin < 7, 75 | dbg = 0; 76 | end; 77 | 78 | % Initialize best odd/even filters 79 | % 80 | hbest_odd = []; 81 | hbest_even = []; 82 | 83 | % Get max odd/even num taps 84 | % 85 | n_odd_max = 2*floor((n-1)/2)+1; 86 | n_even_max = 2*floor(n/2); 87 | 88 | if dbg >= 2, 89 | filt_fig = figure; 90 | end; 91 | % Test odd filters first 92 | % 93 | if even_odd ~= 2, 94 | n_bot = 1; 95 | n_top = (n_odd_max+1)/2; 96 | n_cur = n_top; 97 | if (dbg) 98 | fprintf(1, 'Testing odd length filters...\n'); 99 | end; 100 | while (n_top - n_bot > 1), 101 | n_tap = (n_cur * 2 - 1); 102 | if (dbg) 103 | fprintf(1, '%4d taps: ...', n_tap) 104 | end; 105 | [h, status] = fir_pm(n_tap, f, a, d, a_min, dbg); 106 | if strcmp(status, 'Solved') 107 | % feasible 108 | hbest_odd = h; 109 | if dbg, 110 | fprintf(1,'Feasible\n'); 111 | end; 112 | if (dbg >= 2) 113 | figure(filt_fig); 114 | clf; 115 | hold on; 116 | plot_spec(f,a,d); 117 | m = 512; 118 | H = fftf(h, m); 119 | freq = [-m/2:m/2-1]/m*2; 120 | 121 | % Correct H by half-sample offset if even number of taps 122 | % 123 | if bitget(n_tap,1) == 0, 124 | H = H .* exp(-i*pi*freq(:)*0.5); 125 | end; 126 | 127 | plot(freq,real(H)); 128 | title('Frequency Response'); 129 | xlabel('Normalized Frequency'); 130 | fprintf(1,'Pausing...'); 131 | pause; 132 | fprintf(1,'\r \r'); 133 | end; 134 | n_top = n_cur; 135 | if n_top == n_bot+1, 136 | n_cur = n_bot; 137 | else 138 | n_cur = ceil((n_top + n_bot)/2); 139 | end; 140 | else 141 | if dbg, 142 | fprintf(1,'Infeasible\n'); 143 | end; 144 | n_bot = n_cur; 145 | n_cur = ceil((n_bot+n_top)/2); 146 | end; 147 | end; 148 | end 149 | 150 | % Test even filters now 151 | % 152 | if even_odd ~= 1, 153 | n_bot = 1; 154 | if isempty(hbest_odd), 155 | n_top = n_even_max/2; 156 | n_cur = n_top; 157 | else 158 | n_top = min(n_even_max/2, (length(hbest_odd)+1)/2); 159 | n_cur = n_top; 160 | end; 161 | if (dbg) 162 | fprintf(1, 'Testing even length filters...\n'); 163 | end; 164 | while (n_top - n_bot > 1), 165 | n_tap = n_cur * 2; 166 | if (dbg) 167 | fprintf(1, '%4d taps: ...', n_tap) 168 | end; 169 | [h, status] = fir_pm(n_tap, f, a, d, a_min, dbg); 170 | if strcmp(status, 'Solved') 171 | % feasible 172 | hbest_even = h; 173 | if dbg, 174 | fprintf(1,'Feasible\n'); 175 | end; 176 | if (dbg >= 2) 177 | figure(filt_fig); 178 | clf; 179 | hold on; 180 | plot_spec(f,a,d); 181 | m = 512; 182 | H = fftf(h, m); 183 | freq = [-m/2:m/2-1]/m*2; 184 | 185 | % Correct H by half-sample offset if even number of taps 186 | % 187 | if bitget(n_tap,1) == 0, 188 | H = H .* exp(-i*pi*freq(:)*0.5); 189 | end; 190 | 191 | plot(freq,real(H)); 192 | title('Frequency Response'); 193 | xlabel('Normalized Frequency'); 194 | fprintf(1,'Pausing...'); 195 | pause; 196 | fprintf(1,'\r \r'); 197 | end; 198 | n_top = n_cur; 199 | if n_top == n_bot+1, 200 | n_cur = n_bot; 201 | else 202 | n_cur = ceil((n_top + n_bot)/2); 203 | end; 204 | else 205 | if dbg, 206 | fprintf(1,'Infeasible\n'); 207 | end; 208 | n_bot = n_cur; 209 | n_cur = ceil((n_bot+n_top)/2); 210 | end; 211 | end; 212 | end 213 | 214 | if isempty(hbest_odd) && isempty(hbest_even), 215 | status = 'Failed'; 216 | h = []; 217 | if dbg, 218 | fprintf(1,'\nFailed to achieve specs\n'); 219 | end; 220 | else 221 | status = 'Solved'; 222 | if length(hbest_odd) > length(hbest_even), 223 | h = hbest_odd; 224 | else 225 | h = hbest_even; 226 | end; 227 | if dbg, 228 | fprintf(1,'\nOptimum number of filter taps is: %d.\n',length(h)); 229 | end; 230 | end; 231 | 232 | -------------------------------------------------------------------------------- /fir_min_order_linprog.m: -------------------------------------------------------------------------------- 1 | % Based on 2 | % "FIR Filter Design via Spectral Factorization and Convex Optimization" 3 | % by S.-P. Wu, S. Boyd, and L. Vandenberghe 4 | % 5 | % function [h, status] = fir_min_order_linprog(n, f, a, d, even_odd, dbg) 6 | % 7 | % Bisection search calling fir_linprog to determine minimum order 8 | % filter that meets linear phase requirements. By default it will check both 9 | % odd and even length filters. If a non-zero point exists at +/- 1, it will 10 | % only check odd filters. Only odd or even filters can be specified by the 11 | % parameter odd_even. 12 | % 13 | % Inputs: 14 | % n - number of taps 15 | % f - frequency bands 16 | % a - amplitudes at band edges 17 | % d - ripple in bands 18 | % even_odd: 1 - odd only 19 | % 2 - even only 20 | % x - either odd or even 21 | % dbg - level of debug info to print 22 | % 23 | % function [h, status] = fir_min_order_linprog(n, f, a, d, dbg) 24 | % 25 | 26 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 27 | % 28 | % Spectral-Spatial RF Pulse Design for MRI and MRSI MATLAB Package 29 | % 30 | % Authors: Adam B. Kerr and Peder E. Z. Larson 31 | % 32 | % (c)2007-2011 Board of Trustees, Leland Stanford Junior University and 33 | % The Regents of the University of California. 34 | % All Rights Reserved. 35 | % 36 | % Please see the Copyright_Information and README files included with this 37 | % package. All works derived from this package must be properly cited. 38 | % 39 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 40 | % 41 | % $Header: /home/adam/cvsroot/src/ss/fir_min_order_linprog.m,v 1.5 2013/08/15 15:56:12 adam Exp $ 42 | % 43 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 44 | % 45 | 46 | function [h, status] = fir_min_order_linprog(n, f, a, d, even_odd, dbg) 47 | if nargin < 4, 48 | error(['Usage: function [h, status] = fir_min_order(n, f, a, d, even_odd,' ... 49 | ' dbg)']); 50 | end; 51 | 52 | status = 'Failed'; 53 | h = []; 54 | 55 | if nargin < 5 || isempty(even_odd), 56 | even_odd = 0; 57 | end; 58 | switch (even_odd) 59 | case {1, 2} 60 | otherwise 61 | even_odd = 0; 62 | end; 63 | 64 | if nargin < 6, 65 | dbg = 0; 66 | end; 67 | 68 | % Initialize best odd/even filters 69 | % 70 | hbest_odd = []; 71 | hbest_even = []; 72 | 73 | % Get max odd/even num taps 74 | % 75 | n_odd_max = 2*floor((n-1)/2)+1; 76 | n_even_max = 2*floor(n/2); 77 | 78 | if dbg >= 2, 79 | filt_fig = figure; 80 | end; 81 | % Test odd filters first 82 | % 83 | if even_odd ~= 2, 84 | n_bot = 1; 85 | n_top = (n_odd_max+1)/2; 86 | n_cur = n_top; 87 | if (dbg) 88 | fprintf(1, 'Testing odd length filters...\n'); 89 | end; 90 | while (n_top - n_bot > 1), 91 | n_tap = (n_cur * 2 - 1); 92 | if (dbg) 93 | fprintf(1, '%4d taps: ...', n_tap) 94 | end; 95 | [h, status] = fir_linprog(n_tap, f, a, d, hbest_odd, dbg); 96 | if strcmp(status, 'Solved') 97 | % feasible 98 | hbest_odd = h; 99 | if dbg, 100 | fprintf(1,'Feasible\n'); 101 | end; 102 | if (dbg >= 2) 103 | figure(filt_fig); 104 | clf; 105 | hold on; 106 | plot_spec(f,a,d); 107 | m = 512; 108 | H = fftf(h, m); 109 | freq = [-m/2:m/2-1]/m*2; 110 | 111 | % Correct H by half-sample offset if even number of taps 112 | % 113 | if bitget(n_tap,1) == 0, 114 | H = H .* exp(-i*pi*freq(:)*0.5); 115 | end; 116 | 117 | plot(freq,real(H)); 118 | title('Frequency Response'); 119 | xlabel('Normalized Frequency'); 120 | fprintf(1,'Pausing...'); 121 | pause; 122 | fprintf(1,'\r \r'); 123 | end; 124 | n_top = n_cur; 125 | if n_top == n_bot+1, 126 | n_cur = n_bot; 127 | else 128 | n_cur = ceil((n_top + n_bot)/2); 129 | end; 130 | else 131 | if dbg, 132 | fprintf(1,'Infeasible\n'); 133 | end; 134 | n_bot = n_cur; 135 | n_cur = ceil((n_bot+n_top)/2); 136 | end; 137 | end; 138 | end 139 | 140 | % Test even filters now 141 | % 142 | if even_odd ~= 1, 143 | n_bot = 1; 144 | if isempty(hbest_odd), 145 | n_top = n_even_max/2; 146 | n_cur = n_top; 147 | else 148 | n_top = min(n_even_max/2, (length(hbest_odd)+1)/2); 149 | n_cur = n_top; 150 | end; 151 | if (dbg) 152 | fprintf(1, 'Testing even length filters...\n'); 153 | end; 154 | while (n_top - n_bot > 1), 155 | n_tap = n_cur * 2; 156 | if (dbg) 157 | fprintf(1, '%4d taps: ...', n_tap) 158 | end; 159 | [h, status] = fir_linprog(n_tap, f, a, d, hbest_even, dbg); 160 | if strcmp(status, 'Solved') 161 | % feasible 162 | hbest_even = h; 163 | if dbg, 164 | fprintf(1,'Feasible\n'); 165 | end; 166 | if (dbg >= 2) 167 | figure(filt_fig); 168 | clf; 169 | hold on; 170 | plot_spec(f,a,d); 171 | m = 512; 172 | H = fftf(h, m); 173 | freq = [-m/2:m/2-1]/m*2; 174 | 175 | % Correct H by half-sample offset if even number of taps 176 | % 177 | if bitget(n_tap,1) == 0, 178 | H = H .* exp(-i*pi*freq(:)*0.5); 179 | end; 180 | 181 | plot(freq,real(H)); 182 | title('Frequency Response'); 183 | xlabel('Normalized Frequency'); 184 | fprintf(1,'Pausing...'); 185 | pause; 186 | fprintf(1,'\r \r'); 187 | end; 188 | n_top = n_cur; 189 | if n_top == n_bot+1, 190 | n_cur = n_bot; 191 | else 192 | n_cur = ceil((n_top + n_bot)/2); 193 | end; 194 | else 195 | if dbg, 196 | fprintf(1,'Infeasible\n'); 197 | end; 198 | n_bot = n_cur; 199 | n_cur = ceil((n_bot+n_top)/2); 200 | end; 201 | end; 202 | end 203 | 204 | if isempty(hbest_odd) && isempty(hbest_even), 205 | status = 'Failed'; 206 | h = []; 207 | if dbg, 208 | fprintf(1,'\nFailed to achieve specs\n'); 209 | end; 210 | else 211 | status = 'Solved'; 212 | if (isempty(hbest_odd)) 213 | h = hbest_even; 214 | elseif (isempty(hbest_even)) 215 | h = hbest_odd; 216 | elseif (length(hbest_odd) < length(hbest_even)) 217 | h = hbest_odd; 218 | else 219 | h = hbest_even; 220 | end; 221 | 222 | if dbg, 223 | fprintf(1,'\nOptimum number of filter taps is: %d.\n',length(h)); 224 | end; 225 | end; 226 | 227 | -------------------------------------------------------------------------------- /fir_min_order_qprog.m: -------------------------------------------------------------------------------- 1 | % Based on 2 | % "FIR Filter Design via Spectral Factorization and Convex Optimization" 3 | % by S.-P. Wu, S. Boyd, and L. Vandenberghe 4 | % 5 | % Bisection search calling fir_qprog to determine minimum order 6 | % filter that meets linear phase requirements. By default it will check both 7 | % odd and even length filters. If a non-zero point exists at +/- 1, it will 8 | % only check odd filters. Only odd or even filters can be specified by the 9 | % parameter odd_even. 10 | % 11 | % function [h, status] = fir_min_order_qprog(n, f, a, d, even_odd, dbg) 12 | % 13 | % Inputs: 14 | % n - number of taps 15 | % f - frequency bands 16 | % a - amplitudes at band edges 17 | % d - ripple in bands 18 | % even_odd: 1 - odd only 19 | % 2 - even only 20 | % x - either odd or even 21 | % dbg - level of debug info to print 22 | % 23 | % Also constrains H(f) > max(0, min(a(1:2:end)-d), min(a(2:2:end)-d)) 24 | % Also minimizes sum(|H(f)|^2) in transition bands. 25 | % 26 | 27 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 28 | % 29 | % Spectral-Spatial RF Pulse Design for MRI and MRSI MATLAB Package 30 | % 31 | % Authors: Adam B. Kerr and Peder E. Z. Larson 32 | % 33 | % (c)2007-2011 Board of Trustees, Leland Stanford Junior University and 34 | % The Regents of the University of California. 35 | % All Rights Reserved. 36 | % 37 | % Please see the Copyright_Information and README files included with this 38 | % package. All works derived from this package must be properly cited. 39 | % 40 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 41 | % 42 | % $Header: /home/adam/cvsroot/src/ss/fir_min_order_qprog.m,v 1.1 2013/08/15 15:53:38 adam Exp $ 43 | % 44 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 45 | % 46 | % 47 | 48 | function [h, status] = fir_min_order_qprog(n, f, a, d, even_odd, dbg) 49 | if nargin < 4, 50 | error(['Usage: function [h, status] = fir_min_order(n, f, a, d, even_odd,' ... 51 | ' dbg)']); 52 | end; 53 | 54 | status = 'Failed'; 55 | h = []; 56 | 57 | if nargin < 5 || isempty(even_odd), 58 | even_odd = 0; 59 | end; 60 | switch (even_odd) 61 | case {1, 2} 62 | otherwise 63 | even_odd = 0; 64 | end; 65 | 66 | if nargin < 6, 67 | dbg = 0; 68 | end; 69 | 70 | % Initialize best odd/even filters 71 | % 72 | hbest_odd = []; 73 | hbest_even = []; 74 | 75 | % Get max odd/even num taps 76 | % 77 | n_odd_max = 2*floor((n-1)/2)+1; 78 | n_even_max = 2*floor(n/2); 79 | 80 | if dbg >= 2, 81 | filt_fig = figure; 82 | end; 83 | % Test odd filters first 84 | % 85 | if even_odd ~= 2, 86 | n_bot = 1; 87 | n_top = (n_odd_max+1)/2; 88 | n_cur = n_top; 89 | if (dbg) 90 | fprintf(1, 'Testing odd length filters...\n'); 91 | end; 92 | while (n_top - n_bot > 1), 93 | n_tap = (n_cur * 2 - 1); 94 | if (dbg) 95 | fprintf(1, '%4d taps: ...', n_tap) 96 | end; 97 | [h, status] = fir_qprog(n_tap, f, a, d, dbg); 98 | if strcmp(status, 'Solved') 99 | % feasible 100 | hbest_odd = h; 101 | if dbg, 102 | fprintf(1,'Feasible\n'); 103 | end; 104 | if (dbg >= 2) 105 | figure(filt_fig); 106 | clf; 107 | hold on; 108 | plot_spec(f,a,d); 109 | m = 512; 110 | H = fftf(h, m); 111 | freq = [-m/2:m/2-1]/m*2; 112 | 113 | % Correct H by half-sample offset if even number of taps 114 | % 115 | if bitget(n_tap,1) == 0, 116 | H = H .* exp(-i*pi*freq(:)*0.5); 117 | end; 118 | 119 | plot(freq,real(H)); 120 | title('Frequency Response'); 121 | xlabel('Normalized Frequency'); 122 | fprintf(1,'Pausing...'); 123 | pause; 124 | fprintf(1,'\r \r'); 125 | end; 126 | n_top = n_cur; 127 | if n_top == n_bot+1, 128 | n_cur = n_bot; 129 | else 130 | n_cur = ceil((n_top + n_bot)/2); 131 | end; 132 | else 133 | if dbg, 134 | fprintf(1,'Infeasible\n'); 135 | end; 136 | n_bot = n_cur; 137 | n_cur = ceil((n_bot+n_top)/2); 138 | end; 139 | end; 140 | end 141 | 142 | % Test even filters now 143 | % 144 | if even_odd ~= 1, 145 | n_bot = 1; 146 | if isempty(hbest_odd), 147 | n_top = n_even_max/2; 148 | n_cur = n_top; 149 | else 150 | n_top = min(n_even_max/2, (length(hbest_odd)+1)/2); 151 | n_cur = n_top; 152 | end; 153 | if (dbg) 154 | fprintf(1, 'Testing even length filters...\n'); 155 | end; 156 | while (n_top - n_bot > 1), 157 | n_tap = n_cur * 2; 158 | if (dbg) 159 | fprintf(1, '%4d taps: ...', n_tap) 160 | end; 161 | [h, status] = fir_qprog(n_tap, f, a, d, dbg); 162 | if strcmp(status, 'Solved') 163 | % feasible 164 | hbest_even = h; 165 | if dbg, 166 | fprintf(1,'Feasible\n'); 167 | end; 168 | if (dbg >= 2) 169 | figure(filt_fig); 170 | clf; 171 | hold on; 172 | plot_spec(f,a,d); 173 | m = 512; 174 | H = fftf(h, m); 175 | freq = [-m/2:m/2-1]/m*2; 176 | 177 | % Correct H by half-sample offset if even number of taps 178 | % 179 | if bitget(n_tap,1) == 0, 180 | H = H .* exp(-i*pi*freq(:)*0.5); 181 | end; 182 | 183 | plot(freq,real(H)); 184 | title('Frequency Response'); 185 | xlabel('Normalized Frequency'); 186 | fprintf(1,'Pausing...'); 187 | pause; 188 | fprintf(1,'\r \r'); 189 | end; 190 | n_top = n_cur; 191 | if n_top == n_bot+1, 192 | n_cur = n_bot; 193 | else 194 | n_cur = ceil((n_top + n_bot)/2); 195 | end; 196 | else 197 | if dbg, 198 | fprintf(1,'Infeasible\n'); 199 | end; 200 | n_bot = n_cur; 201 | n_cur = ceil((n_bot+n_top)/2); 202 | end; 203 | end; 204 | end 205 | 206 | if isempty(hbest_odd) && isempty(hbest_even), 207 | status = 'Failed'; 208 | h = []; 209 | if dbg, 210 | fprintf(1,'\nFailed to achieve specs\n'); 211 | end; 212 | else 213 | status = 'Solved'; 214 | if (isempty(hbest_odd)) 215 | h = hbest_even; 216 | elseif (isempty(hbest_even)) 217 | h = hbest_odd; 218 | elseif (length(hbest_odd) < length(hbest_even)) 219 | h = hbest_odd; 220 | else 221 | h = hbest_even; 222 | end; 223 | 224 | if dbg, 225 | fprintf(1,'\nOptimum number of filter taps is: %d.\n',length(h)); 226 | end; 227 | end; 228 | 229 | -------------------------------------------------------------------------------- /fir_min_order_qprog_phs.m: -------------------------------------------------------------------------------- 1 | % Based on 2 | % "FIR Filter Design via Spectral Factorization and Convex Optimization" 3 | % by S.-P. Wu, S. Boyd, and L. Vandenberghe 4 | % 5 | % Determines minimum-order linear-phase filter that meets 6 | % amplitude/ripple specifications. 7 | % 8 | % function [h, status] = fir_min_order(n, f, a, d, dbg) 9 | % 10 | % Inputs: --- similar to cfirpm 11 | % n: max number of taps to try 12 | % f: frequency bands 13 | % a: amplitude at band edges 14 | % d: ripple in bands 15 | % dbg: flag to turn on debugging statements/plots 16 | % 17 | 18 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 19 | % 20 | % Spectral-Spatial RF Pulse Design for MRI and MRSI MATLAB Package 21 | % 22 | % Authors: Adam B. Kerr and Peder E. Z. Larson 23 | % 24 | % (c)2007-2011 Board of Trustees, Leland Stanford Junior University and 25 | % The Regents of the University of California. 26 | % All Rights Reserved. 27 | % 28 | % Please see the Copyright_Information and README files included with this 29 | % package. All works derived from this package must be properly cited. 30 | % 31 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 32 | % 33 | % $Header: /home/adam/cvsroot/src/ss/fir_min_order_qprog_phs.m,v 1.2 2012/06/27 16:49:53 adam Exp $ 34 | % 35 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 36 | % 37 | % 38 | % Bisection search calling fir_qprog to determine minimum order 39 | % filter that meets linear phase requirements. By default it will check both 40 | % odd and even length filters. If a non-zero point exists at +/- 1, it will 41 | % only check odd filters. Only odd or even filters can be specified by the 42 | % parameter odd_even. 43 | % 44 | % Inputs: 45 | % n - number of taps 46 | % f - frequency bands 47 | % a - amplitudes at band edges 48 | % even_odd: 1 - odd only 49 | % 2 - even only 50 | % x - either odd or even 51 | % dbg - level of debug info to print 52 | % 53 | 54 | function [h, status] = fir_min_order_qprog_phs(n, f, a, d, even_odd, dbg) 55 | if nargin < 4, 56 | error(['Usage: function [h, status] = fir_min_order(n, f, a, d, even_odd,' ... 57 | ' dbg)']); 58 | end; 59 | 60 | status = 'Failed'; 61 | h = []; 62 | 63 | if nargin < 5 || isempty(even_odd), 64 | even_odd = 0; 65 | end; 66 | switch (even_odd) 67 | case {1, 2} 68 | otherwise 69 | even_odd = 0; 70 | end; 71 | 72 | if nargin < 6, 73 | dbg = 0; 74 | end; 75 | 76 | % Initialize best odd/even filters 77 | % 78 | hbest_odd = []; 79 | hbest_even = []; 80 | 81 | % Get max odd/even num taps 82 | % 83 | n_odd_max = 2*floor((n-1)/2)+1; 84 | n_even_max = 2*floor(n/2); 85 | 86 | % Test odd filters first 87 | % 88 | if even_odd ~= 2, 89 | n_bot = 1; 90 | n_top = (n_odd_max+1)/2; 91 | n_cur = n_top; 92 | if (dbg) 93 | fprintf(1, 'Testing odd length filters...\n'); 94 | end; 95 | while (n_top - n_bot > 1), 96 | n_tap = (n_cur * 2 - 1); 97 | if (dbg) 98 | fprintf(1, '%4d taps: ...', n_tap) 99 | end; 100 | [h, status] = fir_qprog_phs(n_tap, f, a, d, hbest_odd, dbg); 101 | if strcmp(status, 'Solved') 102 | % feasible 103 | hbest_odd = h; 104 | if dbg, 105 | fprintf(1,'Feasible\n'); 106 | end; 107 | n_top = n_cur; 108 | if n_top == n_bot+1, 109 | n_cur = n_bot; 110 | else 111 | n_cur = ceil((n_top + n_bot)/2); 112 | end; 113 | else 114 | if dbg, 115 | fprintf(1,'Infeasible\n'); 116 | end; 117 | n_bot = n_cur; 118 | n_cur = ceil((n_bot+n_top)/2); 119 | end; 120 | end; 121 | end 122 | 123 | % Test even filters now 124 | % 125 | if even_odd ~= 1, 126 | n_bot = 1; 127 | if isempty(hbest_odd), 128 | n_top = n_even_max/2; 129 | n_cur = n_top; 130 | else 131 | n_top = min(n_even_max/2, (length(hbest_odd)+1)/2); 132 | n_cur = n_top; 133 | end; 134 | if (dbg) 135 | fprintf(1, 'Testing even length filters...\n'); 136 | end; 137 | while (n_top - n_bot > 1), 138 | n_tap = n_cur * 2; 139 | if (dbg) 140 | fprintf(1, '%4d taps: ...', n_tap) 141 | end; 142 | [h, status] = fir_qprog_phs(n_tap, f, a, d, hbest_even, dbg); 143 | if strcmp(status, 'Solved') 144 | % feasible 145 | hbest_even = h; 146 | if dbg, 147 | fprintf(1,'Feasible\n'); 148 | end; 149 | n_top = n_cur; 150 | if n_top == n_bot+1, 151 | n_cur = n_bot; 152 | else 153 | n_cur = ceil((n_top + n_bot)/2); 154 | end; 155 | else 156 | if dbg, 157 | fprintf(1,'Infeasible\n'); 158 | end; 159 | n_bot = n_cur; 160 | n_cur = ceil((n_bot+n_top)/2); 161 | end; 162 | end; 163 | end 164 | 165 | if isempty(hbest_odd) && isempty(hbest_even), 166 | status = 'Failed'; 167 | h = []; 168 | if dbg, 169 | fprintf(1,'\nFailed to achieve specs\n'); 170 | end; 171 | else 172 | status = 'Solved'; 173 | if (isempty(hbest_odd)) 174 | h = hbest_even; 175 | elseif (isempty(hbest_even)) 176 | h = hbest_odd; 177 | elseif (length(hbest_odd) < length(hbest_even)) 178 | h = hbest_odd; 179 | else 180 | h = hbest_even; 181 | end; 182 | 183 | % dbg = 2; 184 | if (dbg >= 2) 185 | filt_fig = figure; 186 | 187 | m = 512; 188 | H = fftf(h, m); 189 | freq = [-m/2:m/2-1]/m*2; 190 | 191 | % Correct H by half-sample offset if even number of taps 192 | % 193 | n_tap = length(h); 194 | if bitget(n_tap,1) == 0, 195 | H = H .* exp(-i*pi*freq(:)*0.5); 196 | end; 197 | 198 | figure(filt_fig); 199 | clf; 200 | hold on; 201 | plot_spec_phs(f,a,d,freq,H); 202 | title('Frequency Response'); 203 | xlabel('Normalized Frequency'); 204 | fprintf(1,'Pausing...'); 205 | pause; 206 | fprintf(1,'\r \r'); 207 | end; 208 | 209 | if dbg, 210 | fprintf(1,'\nOptimum number of filter taps is: %d.\n',length(h)); 211 | end; 212 | end; 213 | 214 | -------------------------------------------------------------------------------- /fir_minphase.m: -------------------------------------------------------------------------------- 1 | % Determines minimum-order minimum-phase filter that has a magnitude 2 | % response that meets the magnitude amplitude/ripple specifications. 3 | % It will only return filters with an odd number of taps. 4 | % 5 | % function [h, status] = fir_minphase(n, f, a, d, use_max, dbg) 6 | % 7 | % Inputs: --- similar to cfirpm 8 | % n: max number of taps to try 9 | % f: frequency bands 10 | % a: amplitude at band edges 11 | % d: ripple in bands 12 | % use_max: don't search for min order, use n taps 13 | % dbg: flag to turn on debugging statements/plots 14 | % 15 | 16 | % NOTE: why must spectral factorization accept odd number taps only? 17 | % No chance to do even filters? 18 | % 19 | 20 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 21 | % 22 | % Spectral-Spatial RF Pulse Design for MRI and MRSI MATLAB Package 23 | % 24 | % Authors: Adam B. Kerr and Peder E. Z. Larson 25 | % 26 | % (c)2007-2011 Board of Trustees, Leland Stanford Junior University and 27 | % The Regents of the University of California. 28 | % All Rights Reserved. 29 | % 30 | % Please see the Copyright_Information and README files included with this 31 | % package. All works derived from this package must be properly cited. 32 | % 33 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 34 | % 35 | % $Header: /home/adam/cvsroot/src/ss/fir_minphase.m,v 1.10 2012/06/08 21:35:02 peder Exp $ 36 | % 37 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 38 | % 39 | % Calls fir_min_order to determine minimum-length odd filter that 40 | % meets magnitude-squared response, then performs spectral 41 | % factorization 42 | % 43 | 44 | function [hn,status,fn,an,dn] = fir_minphase(n, f, a, d, use_max, dbg) 45 | if nargin < 4, 46 | error('Usage: function [h, status] = fir_minphase(n, f, a, d, use_max,dbg)'); 47 | end; 48 | 49 | if nargin < 5, 50 | use_max = 0; 51 | end; 52 | 53 | if nargin < 6, 54 | dbg = 0; 55 | end; 56 | 57 | hn = []; 58 | status = 'Failed'; 59 | 60 | % Get magnitude-squared spec 61 | % 62 | d2 = [d(:).'; d(:).']; 63 | d2 = d2(:).'; 64 | mxspec = (a+d2).^2; 65 | mnspec = max(0,(a-d2)).^2; 66 | 67 | % Get "zero" threshold of 2% of lowest spec 68 | % 69 | ztol_perc = 2; 70 | ztol = min(ztol_perc/100 * (mxspec-mnspec)); 71 | 72 | % Offset magnitude-squared spec by ztol, get n 73 | % 74 | mnspec = max(ztol,mnspec); 75 | a_sqr = (mxspec + mnspec)/2; 76 | d2_sqr = (mxspec-mnspec)/2; 77 | d_sqr = d2_sqr(1:2:end); 78 | n_max = 2*n - 1; 79 | 80 | % Get minimum-order linear-phase filter that meets 81 | % magnitude response 82 | % 83 | odd_only = 1; 84 | if ~use_max, 85 | [r, status] = fir_min_order(n_max, f, a_sqr, d_sqr, odd_only, ztol, dbg); 86 | else 87 | [r, status] = fir_pm(n_max, f, a_sqr, d_sqr, ztol, dbg); 88 | end; 89 | if strcmp(status, 'Failed') 90 | fprintf(1,'Failed to get filter\n'); 91 | return; 92 | end 93 | 94 | Rok = 0; 95 | while ~Rok, 96 | % Expand passband/transition regions to reduce 97 | % transition ripple 98 | % 99 | [rn, fn, an, dn] = fir_expand(length(r), f, a_sqr, d_sqr, ztol, dbg); 100 | 101 | oversamp = 15; 102 | m = 2 * oversamp * length(rn); 103 | m2 = 2^ceil(log2(m)); 104 | R = real(fftf(rn,m2)); 105 | 106 | % Check magnitude response to make sure that it is everywhere 0 107 | % 108 | if dbg, 109 | freq = [-m2/2:m2/2-1]/m2*2; 110 | Ro = real(fftf(r,m2)); % Linear-phase must have real autocorrelation 111 | figure; 112 | plot(freq(:),real(Ro(:))); 113 | hold on; 114 | plot(freq(:),real(R(:)),'r'); 115 | plot_spec(fn,an,dn,'g'); 116 | plot_spec(f,a_sqr,d_sqr,'k'); 117 | title('Squared Frequency Response of Autocorrelation Fcns'); 118 | xlabel('Normalized Frequency'); 119 | end; 120 | 121 | % Now use spectral factorization to get return filter 122 | % --- first offset to make sure it's positive 123 | if min(R) < 0, 124 | min_stop = min(a_sqr + d2_sqr); 125 | Rtol_perc = 0.1; % 10% stopband tolerance 126 | Rtol = Rtol_perc * min_stop; % Rule of thumb 127 | if min(R) < -Rtol, 128 | fprintf(1, 'Autocorrelation has negative value\n'); 129 | fprintf(1, ' Tol (%d%% stopband): %e Actual: %e\n', ... 130 | round(Rtol_perc*100), Rtol, -min(R)); 131 | 132 | % Test spectral factorization 133 | % 134 | rn = rn + Rtol; 135 | hn = spectral_fact(rn); 136 | hn = conj(hn(end:-1:1)); 137 | 138 | % Get squared frequency response and check against specs 139 | % + Rtol 140 | % 141 | H = (fftf(hn, m2)); 142 | freq = [-m2/2:m2/2-1]/m2*2; 143 | H2 = abs(H).^2; % Squared-mag response (H is Min-Phase) 144 | nband = length(f)/2; 145 | atol = 0.05; 146 | Rok = 1; 147 | for band = 1:nband, 148 | idx = find((freq >= f(band*2-1)) & (freq <= f(band*2))); 149 | amax = (1+atol)*(a_sqr(band*2-1) + d_sqr(band) + Rtol); 150 | amin = (1-atol)*(a_sqr(band*2-1) - d_sqr(band)); 151 | fail = find((H2(idx) > amax) | ... 152 | (H2(idx) < amin)); 153 | if fail, 154 | fprintf(1, ' Spectral factorization doesn''t meet specs\n'); 155 | fprintf(1, ' Increase number of taps to: %d\n', ... 156 | length(r)+2); 157 | if (dbg) 158 | fprintf(1,''); 159 | figure; 160 | plot_spec(f,a_sqr,d_sqr,'k'); 161 | hold on; 162 | plot(freq(:),H2(:)); 163 | title('Squared Frequency Response of Factorized Filter'); 164 | xlabel('Normalized Frequency'); 165 | pause; 166 | end; 167 | fprintf(1,'\r \r'); 168 | [r, stat] = fir_pm(length(r)+2, f, a_sqr, d_sqr, ztol, ... 169 | dbg); 170 | Rok = 0; 171 | break; 172 | end; 173 | end; 174 | else 175 | if (dbg) 176 | fprintf(1, 'Autocorrelation has negative value, but within tol\n'); 177 | fprintf(1, ' Tol (%d%% stopband): %e Actual: %e\n', ... 178 | round(Rtol_perc*100), Rtol, -min(R)); 179 | end; 180 | rn = rn - min(R); 181 | Rok = 1; 182 | end; 183 | else 184 | if dbg, 185 | fprintf(1,'Autocorrelation OK\n'); 186 | end; 187 | Rok = 1; 188 | end; 189 | end; 190 | 191 | % Spectral factorize and time reverse 192 | % 193 | h = spectral_fact(r); 194 | h = conj(h(end:-1:1)); 195 | 196 | hn = spectral_fact(rn); 197 | hn = conj(hn(end:-1:1)); 198 | if dbg, 199 | m = 512; 200 | freq = [-m/2:m/2-1]/m*2; 201 | H = abs(fftf(h,512)); 202 | Hn = abs(fftf(hn,512)); 203 | figure; 204 | plot(freq,H); 205 | hold on; 206 | plot(freq,Hn,'r'); 207 | plot_spec(f,a,d,'k'); 208 | title('Frequency Response Factorized Filters'); 209 | end; 210 | 211 | -------------------------------------------------------------------------------- /fir_minphase_power.m: -------------------------------------------------------------------------------- 1 | % Determines minimum-order minimum-phase filter that has a magnitude 2 | % response that meets the magnitude amplitude/ripple specifications. 3 | % It will only return filters with an odd number of taps. 4 | % 5 | % function [h, status] = fir_minphase_power(n, f, a, d, use_max, dbg) 6 | % 7 | % Inputs: --- similar to cfirpm 8 | % n: max number of taps to try 9 | % f: frequency bands 10 | % a: amplitude at band edges 11 | % d: ripple in bands 12 | % use_max: don't search for min order, use n taps 13 | % dbg: flag to turn on debugging statements/plots 14 | % 15 | 16 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 17 | % 18 | % Spectral-Spatial RF Pulse Design for MRI and MRSI MATLAB Package 19 | % 20 | % Authors: Adam B. Kerr and Peder E. Z. Larson 21 | % 22 | % (c)2007-2011 Board of Trustees, Leland Stanford Junior University and 23 | % The Regents of the University of California. 24 | % All Rights Reserved. 25 | % 26 | % Please see the Copyright_Information and README files included with this 27 | % package. All works derived from this package must be properly cited. 28 | % 29 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 30 | % 31 | % $Header: /home/adam/cvsroot/src/ss/fir_minphase_power.m,v 1.7 2012/06/19 16:53:51 adam Exp $ 32 | % 33 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 34 | % 35 | % Calls fir_min_order to determine minimum-length odd filter that 36 | % meets magnitude-squared response, then performs spectral 37 | % factorization 38 | % 39 | 40 | function [hn,status] = fir_minphase_power(n, f, a, d, use_max, dbg) 41 | if nargin < 4, 42 | error('Usage: function [h, status] = fir_minphase_power(n, f, a, d, use_max,dbg)'); 43 | end; 44 | 45 | if nargin < 5, 46 | use_max = 0; 47 | end; 48 | 49 | if nargin < 6, 50 | dbg = 0; 51 | end; 52 | 53 | hn = []; 54 | status = 'Failed'; 55 | 56 | % Get magnitude-squared spec 57 | % 58 | d2 = [d(:).'; d(:).']; 59 | d2 = d2(:).'; 60 | mxspec = (a+d2).^2; 61 | mnspec = max(0,(a-d2)).^2; 62 | 63 | % Get "zero" threshold of 2% of lowest spec 64 | % 65 | ztol_perc = 2; 66 | ztol = min(ztol_perc/100 * (mxspec-mnspec)); 67 | 68 | % Offset magnitude-squared spec by ztol, get n 69 | % 70 | mnspec = max(ztol,mnspec); 71 | a_sqr = (mxspec + mnspec)/2; 72 | d2_sqr = (mxspec-mnspec)/2; 73 | d_sqr = d2_sqr(1:2:end); 74 | n_max = 2*n - 1; 75 | 76 | % Get minimum-order linear-phase filter that meets 77 | % magnitude response 78 | % 79 | odd_only = 1; 80 | if ~use_max, 81 | [r, status] = fir_min_order(n_max, f, a_sqr, d_sqr, odd_only, ztol, dbg); 82 | else 83 | [r, status] = fir_pm(n_max, f, a_sqr, d_sqr, ztol, dbg); 84 | end; 85 | if strcmp(status, 'Failed') 86 | fprintf(1,'Failed to get filter\n'); 87 | return; 88 | end 89 | 90 | Rok = 0; 91 | ncurrent = length(r); 92 | while ~Rok, 93 | % Get min power filter 94 | % 95 | [rn,status] = fir_pm_minpow(ncurrent, f, a_sqr, d_sqr, ztol, dbg); 96 | 97 | oversamp = 15; 98 | m = 2 * oversamp * length(rn); 99 | m2 = 2^ceil(log2(m)); 100 | R = real(fftf(rn,m2)); 101 | 102 | % Check magnitude response to make sure that it is everywhere positive 103 | % 104 | if dbg, 105 | freq = [-m2/2:m2/2-1]/m2*2; 106 | Ro = real(fftf(r,m2)); % Linear-phase must have real autocorrelation 107 | figure; 108 | plot(freq(:),real(Ro(:))); 109 | hold on; 110 | plot(freq(:),real(R(:)),'r'); 111 | plot_spec(f,a_sqr,d_sqr,'k'); 112 | title('Squared Frequency Response of Autocorrelation Fcns'); 113 | xlabel('Normalized Frequency'); 114 | end; 115 | 116 | % Now use spectral factorization to get return filter 117 | % --- first offset to make sure it's positive 118 | if min(R) < 0, 119 | min_stop = min(a_sqr + d2_sqr); 120 | Rtol_perc = 0.1; % 10% stopband tolerance 121 | Rtol = Rtol_perc * min_stop; % Rule of thumb 122 | if min(R) < -Rtol, 123 | fprintf(1, 'Autocorrelation has negative value\n'); 124 | fprintf(1, ' Tol (%d%% stopband): %e Actual: %e\n', ... 125 | round(Rtol_perc*100), Rtol, -min(R)); 126 | 127 | % Test spectral factorization 128 | % 129 | rn = rn + Rtol; 130 | hn = spectral_fact(rn); 131 | hn = conj(hn(end:-1:1)); 132 | 133 | % Get squared frequency response and check against specs 134 | % + Rtol 135 | % 136 | H = (fftf(hn, m2)); 137 | freq = [-m2/2:m2/2-1]/m2*2; 138 | H2 = abs(H).^2; % Squared-mag response (H is Min-Phase) 139 | nband = length(f)/2; 140 | atol = 0.05; 141 | Rok = 1; 142 | for band = 1:nband, 143 | idx = find((freq >= f(band*2-1)) & (freq <= f(band*2))); 144 | amax = (1+atol)*(a_sqr(band*2-1) + d_sqr(band) + Rtol); 145 | amin = (1-atol)*(a_sqr(band*2-1) - d_sqr(band)); 146 | fail = find((H2(idx) > amax) | ... 147 | (H2(idx) < amin)); 148 | if fail, 149 | fprintf(1, ' Spectral factorization doesn''t meet specs\n'); 150 | fprintf(1, ' Increase number of taps to: %d\n', ... 151 | length(r)+2); 152 | if dbg, 153 | fprintf(1,''); 154 | figure; 155 | plot_spec(f,a_sqr,d_sqr,'k'); 156 | hold on; 157 | plot(freq(:),R(:),'r'); 158 | plot(freq(:),H2(:)); 159 | grid; 160 | 161 | title('Squared Frequency Response of Original and Factorized Filter'); 162 | xlabel('Normalized Frequency'); 163 | pause; 164 | end 165 | fprintf(1,'\r \r'); 166 | ncurrent = ncurrent + 2; 167 | Rok = 0; 168 | break; 169 | end; 170 | end; 171 | else 172 | fprintf(1, 'Autocorrelation has negative value, but within tol\n'); 173 | fprintf(1, ' Tol (%d%% stopband): %e Actual: %e\n', ... 174 | round(Rtol_perc*100), Rtol, -min(R)); 175 | rn = rn - min(R); 176 | Rok = 1; 177 | end; 178 | elseif strcmp(status, 'Failed') 179 | fprintf(1,'\r \r'); 180 | ncurrent = ncurrent + 2; 181 | Rok = 0; 182 | 183 | else 184 | if dbg, 185 | fprintf(1,'Autocorrelation OK\n'); 186 | end; 187 | Rok = 1; 188 | end; 189 | end; 190 | 191 | % Spectral factorize and time reverse 192 | % 193 | h = spectral_fact(r); 194 | h = conj(h(end:-1:1)); 195 | 196 | hn = spectral_fact(rn); 197 | hn = conj(hn(end:-1:1)); 198 | if dbg, 199 | m = 512; 200 | freq = [-m/2:m/2-1]/m*2; 201 | H = abs(fftf(h,512)); 202 | Hn = abs(fftf(hn,512)); 203 | figure; 204 | plot(freq,H); 205 | hold on; 206 | plot(freq,Hn,'r'); 207 | plot_spec(f,a,d,'k'); 208 | title('Frequency Response Factorized Filters'); 209 | end; 210 | 211 | -------------------------------------------------------------------------------- /fir_pm.m: -------------------------------------------------------------------------------- 1 | 2 | function [h, status] = fir_pm(n, f, a, d, a_min, dbg) 3 | % FIR_LINPROG - FIR filter design using Parks-McLellan algs 4 | % 5 | % Design n-tap linear-phase filter that meets multiband frequency 6 | % specification. 7 | % 8 | % function [h, status] = fir_pm(n, f, a, d, dbg) 9 | % 10 | % Inputs: 11 | % n: number of taps returned 12 | % f: frequency bands 13 | % a: amplitude at band edges 14 | % d: ripple in bands 15 | % a_min: if not present, chooses min(0, min(a-d)) 16 | % dbg: flag to turn on debugging statements/plots 17 | % 18 | 19 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 20 | % 21 | % Spectral-Spatial RF Pulse Design for MRI and MRSI MATLAB Package 22 | % 23 | % Authors: Adam B. Kerr and Peder E. Z. Larson 24 | % 25 | % (c)2007-2011 Board of Trustees, Leland Stanford Junior University and 26 | % The Regents of the University of California. 27 | % All Rights Reserved. 28 | % 29 | % Please see the Copyright_Information and README files included with this 30 | % package. All works derived from this package must be properly cited. 31 | % 32 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 33 | % 34 | % $Header: /home/adam/cvsroot/src/ss/fir_pm.m,v 1.7 2012/02/01 00:41:22 peder Exp $ 35 | % 36 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 37 | % 38 | % Default value for a_min 39 | % 40 | d2 = [d(:).'; d(:).']; 41 | d2 = d2(:).'; 42 | if (nargin < 5) || isempty(a_min), 43 | a_min = min(0,min(a-d2)); 44 | end; 45 | 46 | % Default value for dbg 47 | % 48 | if nargin < 6, 49 | dbg = 0; 50 | end; 51 | 52 | % Determine if real or complex coefficients 53 | % 54 | f = f * pi; % Scale to +/- pi 55 | if min(f) < 0, 56 | real_filter = 0; 57 | else 58 | real_filter = 1; 59 | end; 60 | 61 | % Determine if filter has odd or even number of 62 | % taps 63 | % 64 | if (bitget(n,1) == 1) 65 | odd_filter = 1; 66 | else 67 | odd_filter = 0; 68 | end; 69 | 70 | % If the frequency specification has a non-zero point 71 | % at +/- 1, then the order must be even. A warning is 72 | % printed and a failure returned if this is the case. 73 | % 74 | if (~odd_filter) 75 | idx = find(abs(f) ~= 0); 76 | if find(a(idx) ~= 0) 77 | warning('n odd and frequency spec non-zero at fs/2'); 78 | 79 | status = 'Failed'; 80 | h = []; 81 | return; 82 | end; 83 | end; 84 | 85 | % Oversampling on frequency to determine transition bands 86 | % 87 | oversamp = 8; 88 | 89 | % Get first pass on w 90 | % 91 | if real_filter, 92 | m = oversamp * n; 93 | w = linspace(0,pi,m); 94 | else 95 | m = 2 * oversamp * n; 96 | w = linspace(-pi,pi,m); 97 | end; 98 | 99 | % Find bounds on transition regions and convert to amp/ripple 100 | % 101 | ub_tran = max(a + d2); 102 | lb_tran = a_min; % Set to min amplitude spec 103 | amp_tran = (ub_tran + lb_tran)/2; 104 | ripple_tran = (ub_tran - lb_tran)/2; 105 | 106 | % Find indices of transition bands, build up new frequency spec 107 | % 108 | nband = length(f)/2; 109 | ntran = nband+1; 110 | fn = []; 111 | an = []; 112 | dn = []; 113 | for tran = 1:ntran, 114 | if tran == 1, 115 | f_l = min(w); % This avoids sample at -pi 116 | rband = tran; 117 | f_r = f(rband*2-1); 118 | elseif tran == ntran, 119 | lband = tran-1; 120 | f_l = f(lband*2); 121 | f_r = pi; % This avoids sample at pi 122 | else 123 | lband = tran-1; 124 | f_l = f(lband*2); 125 | rband = tran; 126 | f_r = f(rband*2-1); 127 | end; 128 | idx_tran = find((w > f_l) & (w < f_r)); 129 | % cfirpm seems to choke sometimes---I hypothesize 130 | % this is because the transition edges are too 131 | % close to the actual passbands, so don't take 132 | % the immediately adjacent points 133 | % 134 | nskip = 1; 135 | if length(idx_tran) <= 1+2*nskip, 136 | f_tran = []; 137 | a_tran = []; 138 | d_tran = []; 139 | else 140 | idx_tran = idx_tran(1+nskip:end-nskip); 141 | f_tran = [min(w(idx_tran)) max(w(idx_tran))]; 142 | a_tran = [amp_tran amp_tran]; 143 | d_tran = [ripple_tran]; 144 | end; 145 | fn = [fn f_tran]; 146 | an = [an a_tran]; 147 | dn = [dn d_tran]; 148 | if tran < ntran, 149 | fn = [fn f(tran*2-1) f(tran*2)]; 150 | an = [an a(tran*2-1) a(tran*2)]; 151 | dn = [dn d(tran)]; 152 | end; 153 | end; 154 | 155 | % Determine error weights, then call firpm 156 | % 157 | w = max(dn) ./ dn; 158 | lgrid = 31; % Oversample, default 25 159 | if 0 160 | % firpm has some instability but cfirpm seems ok... 161 | % 162 | if real_filter, 163 | try 164 | [h,d_opt,opt] = firpm(n-1,fn/pi,an,w,{lgrid}); 165 | catch 166 | h = []; 167 | end; 168 | else 169 | [h,d_opt,opt] = cfirpm(n-1,fn/pi,an,w,{lgrid}); 170 | end; 171 | end; 172 | 173 | % [h,d_opt,opt] = cfirpm(n-1,fn/pi,an,w,{lgrid},'skip_stage2'); 174 | try 175 | [h,d_opt,opt] = cfirpm(n-1,fn/pi,an,w,{lgrid}); 176 | catch 177 | h = []; 178 | lsterr = lasterror; 179 | fprintf(1,'Error caught in cfirpm: \n'); 180 | fprintf(1,'%s\n', lsterr.message); 181 | end; 182 | 183 | % Check frequency response at extremal frequencies 184 | % that are within specified bands 185 | % 186 | resp_ok = 0; 187 | if ~isempty(h) 188 | resp_ok = check_response(f/pi, a, d, opt.fgrid, abs(opt.H)); 189 | end; 190 | 191 | if (~resp_ok) 192 | status = 'Failed'; 193 | if dbg>=2, 194 | plot_response(opt.fgrid, opt.H, fn/pi, an, dn); 195 | title('Filter Response'); 196 | pause(1); 197 | end; 198 | h = []; 199 | else 200 | if dbg>=2, 201 | plot_response(opt.fgrid, opt.H, fn/pi, an, dn); 202 | title('Filter Response'); 203 | pause(1); 204 | end; 205 | 206 | h = h(:); 207 | status = 'Solved'; 208 | end; 209 | return; 210 | 211 | 212 | function status = check_response(f,a,d,ftest,htest) 213 | % CHECK_RESPONSE - Check magnitude response to see if it meets specs 214 | % 215 | nband = length(f)/2; 216 | status = 1; 217 | for band = 1:nband, 218 | idx = find((ftest >= f(band*2-1)) & (ftest <= f(band*2))); 219 | if isempty(idx) 220 | break; 221 | end; 222 | 223 | f_off = ftest(idx) - f(band*2-1); 224 | a_test = a(band*2-1) + ... 225 | (a(band*2)-a(band*2-1)) * f_off/(f(band*2)-f(band*2-1)); 226 | 227 | a_hi = a_test + d(band); 228 | a_lo = a_test - d(band); 229 | 230 | if (find((htest(idx) > a_hi) | (htest(idx) < a_lo))) 231 | status = 0; % Fails in at least one sample 232 | return; 233 | end; 234 | end; 235 | 236 | return; 237 | 238 | function plot_response (freq,h,f,a,d) 239 | % plot_response - Plot frequency specification and actual response 240 | % 241 | figure; 242 | hold on; 243 | nband = length(f)/2; 244 | for band = 1:nband, 245 | idx = [band*2-1:band*2]; 246 | plot(f(idx), a(idx)+d(band), 'k--'); 247 | if max(a(idx)-d(band)) > 0, 248 | plot(f(idx), max(0,a(idx)-d(band)), 'k--'); 249 | end; 250 | end; 251 | plot(freq, real(h)); 252 | plot(freq, imag(h),'b--'); 253 | xlabel('Frequency'); 254 | ylabel('Filter Response'); 255 | 256 | return; 257 | -------------------------------------------------------------------------------- /fir_qprog.m: -------------------------------------------------------------------------------- 1 | 2 | function [h, status] = fir_qprog(n, f, a, d, dbg) 3 | % FIR_QPROG - FIR filter design using quadratic programming 4 | % 5 | % Design n-tap linear-phase filter that meets multiband frequency 6 | % specification. 7 | % 8 | % Also constrain H(f) > max(0, min(a(1:2:end)-d), min(a(2:2:end)-d)) 9 | % Also minimizes sum(|H(f)|^2) in transition bands. 10 | % 11 | % function [h, status] = fir_qprog(n, f, a, d, dbg) 12 | % 13 | % Inputs: --- similar to cfirpm 14 | % n: number of taps returned 15 | % f: frequency bands (-1->1) 16 | % a: amplitude at band edges 17 | % d: ripple in bands 18 | % dbg: flag to turn on debugging statements/plots 19 | % 20 | 21 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 22 | % 23 | % Spectral-Spatial RF Pulse Design for MRI and MRSI MATLAB Package 24 | % 25 | % Authors: Adam B. Kerr and Peder E. Z. Larson 26 | % 27 | % (c)2007-2011 Board of Trustees, Leland Stanford Junior University and 28 | % The Regents of the University of California. 29 | % All Rights Reserved. 30 | % 31 | % Please see the Copyright_Information and README files included with this 32 | % package. All works derived from this package must be properly cited. 33 | % 34 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 35 | % 36 | % $Header: /home/adam/cvsroot/src/ss/fir_qprog.m,v 1.2 2013/08/15 17:10:31 adam Exp $ 37 | % 38 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 39 | % 40 | % Default value for dbg 41 | % 42 | if nargin < 5, 43 | dbg = 0; 44 | end; 45 | 46 | % Determine if real or complex coefficients 47 | % 48 | f = f * pi; % Scale to +/- pi 49 | if min(f) < 0, 50 | real_filter = 0; 51 | else 52 | real_filter = 1; 53 | end; 54 | 55 | % Determine if filter has odd or even number of 56 | % taps 57 | % 58 | if (bitget(n,1) == 1) 59 | odd_filter = 1; 60 | else 61 | odd_filter = 0; 62 | end; 63 | 64 | % If the frequency specification has a non-zero point 65 | % at +/- 1, then the order must be even. A warning is 66 | % printed and a failure returned if this is the case. 67 | % 68 | if (~odd_filter) 69 | idx = find(abs(f) == pi); 70 | if find(a(idx) == 1) 71 | warning('n odd and frequency spec 1 at fs/2'); 72 | 73 | status = 'Failed'; 74 | h = []; 75 | return; 76 | end; 77 | end; 78 | 79 | % Determine number of optimization parameters 80 | % 81 | nhalf = ceil(n/2); % number of taps in half-side of 82 | % filter 83 | nx = nhalf; 84 | if ~real_filter, 85 | if odd_filter, 86 | nx = 2*nhalf-1; 87 | else 88 | nx = 2*nhalf; 89 | end; 90 | end; 91 | 92 | % Create optimization arrays 93 | % 94 | oversamp = 15; 95 | undersamp_tran = 1; % Undersampling factor for transition 96 | % regions 97 | % Get first pass on w 98 | % 99 | if real_filter, 100 | m = oversamp * n; 101 | w = linspace(0,pi,m); 102 | else 103 | m = 2 * oversamp * n; 104 | w = linspace(-pi,pi,m); 105 | end; 106 | 107 | % Add explicit samples to w at the edge of each specified band 108 | % 109 | w = sort([w f]); 110 | 111 | % Find indices to passbands/stopbands, and fill in upper/lower bounds 112 | % 113 | idx_band = []; U_band = []; L_band = []; 114 | nband = length(f)/2; 115 | for band = 1:nband, 116 | idx = find( (w >= f(band*2-1)) & (w <= f(band*2)) ); 117 | % Get amplitude from linear interpolation on band 118 | % 119 | idx_band = [idx_band idx]; 120 | if (f(band*2-1) == f(band*2)) 121 | amp = a(band*2-1); 122 | else 123 | amp = a(band*2-1) + (a(band*2)-a(band*2-1)) * ... 124 | ((w(idx) - f(band*2-1))/(f(band*2)-f(band*2-1))); 125 | end; 126 | U_band = [U_band (amp + d(band))]; 127 | L_band = [L_band (amp - d(band))]; 128 | end; 129 | 130 | % Get transition indices 131 | % 132 | idx_tmp = ones(1,length(w)); 133 | idx_tmp(idx_band) = 0; 134 | idx_tran = find(idx_tmp == 1); 135 | 136 | % Get average representation of response 137 | % 138 | lb_resp = size(w); 139 | lb_resp(idx_band) = (U_band + L_band)/2; 140 | lb_resp(idx_tran) = (max(U_band) + min(L_band))/2; 141 | if real_filter, 142 | lb_resp = [lb_resp(end:-1:1) lb_resp(2:end-1)]; 143 | end; 144 | if dbg >= 3, 145 | if real_filter, 146 | wplot = [-w(end:-1:1) w(2:end-1)]; 147 | else 148 | wplot = w; 149 | end; 150 | figure; 151 | plot(wplot,lb_resp); 152 | end; 153 | 154 | % Decimate w in transition regions 155 | % 156 | idx_tran = idx_tran(1:undersamp_tran:end); 157 | 158 | % Add transition band limits to be between the + max 159 | % specification on each band and min of (0,min(L_band)) 160 | % 161 | if ~isempty(idx_tran) 162 | U_amp_tran = max(U_band); 163 | U_tran = U_amp_tran*ones(1,length(idx_tran)); 164 | L_amp_tran = min(0, min(L_band)); 165 | L_tran = L_amp_tran*ones(1,length(idx_tran)); 166 | else 167 | U_tran = []; 168 | L_tran = []; 169 | end; 170 | 171 | % Update w, idx_band 172 | % 173 | wband = w(idx_band); 174 | idx_band = [1:length(wband)]; 175 | wtran = w(idx_tran); 176 | idx_tran = [1:length(wtran)] + length(wband); 177 | w = [wband(:).' wtran(:).']; 178 | m = size(w,2); 179 | 180 | if dbg >= 3, 181 | figure; 182 | plot(w(idx_band),U_band,'*'); 183 | hold on; 184 | plot(w(idx_band),L_band,'o'); 185 | plot(w(idx_tran),U_tran,'r*'); 186 | plot(w(idx_tran),L_tran,'ro'); 187 | pause; 188 | end; 189 | 190 | if real_filter 191 | % create optimization matrices 192 | % A is the matrix used to compute the power spectrum 193 | % A(w,:) = [1 2*cos(w) 2*cos(2*w) ... 2*cos(n*w)] 194 | if (odd_filter) 195 | Acos = [ones(m,1) 2*cos(kron(w',[1:nhalf-1]))]; 196 | else 197 | Acos = [2*cos(kron(w',[0:nhalf-1]+0.5))]; 198 | end; 199 | Asin = []; 200 | else 201 | if (odd_filter) 202 | Acos = [ones(m,1) 2*cos(kron(w',[1:nhalf-1]))]; 203 | Asin = [2*sin(kron(w',[1:nhalf-1]))]; 204 | else 205 | Acos = [2*cos(kron(w',[0:nhalf-1]+0.5))]; 206 | Asin = [2*sin(kron(w',[0:nhalf-1]+0.5))]; 207 | end; 208 | end; 209 | 210 | % Get subset of A matrix for current order 211 | % 212 | A = [Acos Asin]; 213 | 214 | % Build matrix for upper bound constraints 215 | % 216 | A_U = [A(idx_band,:); A(idx_tran,:)]; 217 | U_b = [U_band U_tran]; 218 | 219 | % Build matrices for lower bound constraints 220 | % 221 | A_L = [A(idx_band, :); A(idx_tran,:)]; 222 | L_b = [L_band L_tran]; 223 | 224 | % Combine matrices 225 | % 226 | A_b = [A_U; -A_L]; 227 | b = [U_b -L_b]; 228 | 229 | % Set H to minimize total energy in filter 230 | % Set fmin to 0 231 | H = eye(nx); 232 | fmin = zeros(1,nx); 233 | 234 | % Call minimization routine 235 | % 236 | x0 = []; 237 | if real_filter, 238 | [x,fval,exitflag,output] = ... 239 | quadprog(H, fmin, A_b, b, [],[],[],[],x0,... 240 | optimset('Algorithm', 'interior-point-convex', ... 241 | 'Display','off')); 242 | else 243 | [x,fval,exitflag,output] = ... 244 | quadprog(H, fmin, A_b, b, [],[],[],[],x0,... 245 | optimset('LargeScale','off', 'Algorithm', 'interior-point-convex', 'Display','off')); 246 | end; 247 | 248 | if dbg >= 2, 249 | fprintf(1,'Exitflag: %d\n', exitflag); 250 | switch(exitflag) 251 | case 1 252 | fprintf(1,'First order optimality conditions satisfied\n'); 253 | case 0 254 | fprintf(1,'Maximum number of iterations exceeded\n'); 255 | case -2 256 | fprintf(1,'No feasible point found\n'); 257 | case -3 258 | fprintf(1,'Problem is unbounded\n'); 259 | case -6 260 | fprintf(1,'Non-convex problem detected\n'); 261 | case 3 262 | fprintf(1,'Change in objective function too small\n'); 263 | case -4 264 | fprintf(1,['Current search direction is not a descent direction; ' ... 265 | 'no further progress can be made.\n']); 266 | case 4 267 | fprintf(1,'Local minimizer found\n'); 268 | case -7 269 | fprintf(1,['Magnitude of search direction became too small; no ' ... 270 | 'further progress can be made. The problem is ill-posed ' ... 271 | 'or badly conditioned.\n']); 272 | otherwise 273 | fprintf(1,'Exitflag not recognized\n'); 274 | end 275 | 276 | H = A * x; 277 | figure; 278 | plot_spec(f,a,d); 279 | [wsort, sidx] = sort(w); 280 | plot(w(sidx), H(sidx)); 281 | hold on; 282 | plot(w(sidx), H(sidx),'rx'); 283 | title('Frequency response calculated with A'); 284 | end; 285 | 286 | if (exitflag == 1) % feasible 287 | h = fill_h(x,nhalf,real_filter, odd_filter,dbg); 288 | status = 'Solved'; 289 | else 290 | h = []; 291 | status = 'Failed'; 292 | end; 293 | return; 294 | 295 | function h = fill_h(x,nhalf,real_filter,odd_filter,dbg) 296 | % Function to fill in filter taps from optimization parameters 297 | % 298 | x = x(:); 299 | if real_filter, 300 | if odd_filter, 301 | h = x(1:end); 302 | h = [x(end:-1:2); h]; 303 | else 304 | h = x(1:end); 305 | h = [x(end:-1:1); h]; 306 | end; 307 | else 308 | if odd_filter, 309 | h = x(1:nhalf) + i * [0; x(nhalf+1:end)]; 310 | h = [conj(h(end:-1:2)); h]; 311 | else 312 | h = x(1:nhalf) + i * x(nhalf+1:end); 313 | h = [conj(h(end:-1:1)); h]; 314 | end; 315 | end; 316 | 317 | return; 318 | 319 | -------------------------------------------------------------------------------- /grad_min_bridge.m: -------------------------------------------------------------------------------- 1 | function [g, g1, g2, g3] = grad_min_bridge(m0, f, mxg, mxs, ts) 2 | % GRAD_MIN_BRIDGE - Determine gradient trapezoid that gives required area from middle "bridge" section 3 | % 4 | % [g, g1, g2, g3] = grad_min_bridge (m0, f, mxg, mxs, ts) 5 | % 6 | % m0 - target zeroth moment (G/cm * s) 7 | % f - fraction of ramp to include in bridge [0..1] 8 | % mxg - maximum amplitude (G/cm) 9 | % mxs - maximum slew rate (G/cm/ms) 10 | % ts - sample time in s 11 | % 12 | % g - Gradient 13 | % 14 | 15 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 16 | % 17 | % Spectral-Spatial RF Pulse Design for MRI and MRSI MATLAB Package 18 | % 19 | % Authors: Adam B. Kerr and Peder E. Z. Larson 20 | % 21 | % (c)2007-2011 Board of Trustees, Leland Stanford Junior University and 22 | % The Regents of the University of California. 23 | % All Rights Reserved. 24 | % 25 | % Please see the Copyright_Information and README files included with this 26 | % package. All works derived from this package must be properly cited. 27 | % 28 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 29 | % 30 | % $Header: /home/adam/cvsroot/src/ss/grad_min_bridge.m,v 1.5 2013/08/15 03:34:50 adam Exp $ 31 | % 32 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 33 | 34 | if (m0 < 0) 35 | s = -1; 36 | m0 = -m0; 37 | else 38 | s = 1; 39 | end; 40 | 41 | % Convert mxs to G/cm/s 42 | % 43 | mxs = mxs * 1e3; 44 | 45 | % Determine trapezoid parameters 46 | % 47 | % na - number of constant samples 48 | % nb - number ramp samples 49 | % nc - number samples of ramp in bridge 50 | % A - trapezoid amplitude 51 | % 52 | 53 | dg = mxs * ts; % Max delta in one sample 54 | 55 | % Assume triangle at first and see if max amp requirements met 56 | % -- quadratic with aq, bq, cq coeffs 57 | % 58 | if (f ~= 0) 59 | aq = (2-f)*f; 60 | bq = f; 61 | cq = -m0/(dg*ts); 62 | nb = (-bq + sqrt(bq^2 - 4*aq*cq))/(2*aq); 63 | 64 | nb = ceil(nb); 65 | nc = max(1,ceil(nb*f)); 66 | else 67 | A = m0 / (2*ts); 68 | nb = ceil(A / dg); 69 | nc = 1; 70 | end; 71 | 72 | % Test result 73 | % 74 | dg_test = m0 / ((2*nb-nc+1)*nc*ts); 75 | A = nb * dg_test; 76 | if (A <= mxg) && (dg_test < dg), % This works! 77 | g1 = s*[1:(nb-nc)] * dg_test; 78 | g2 = s*[[(nb-nc+1):nb] [nb:-1:(nb-nc+1)]] * dg_test; 79 | g3 = s*[(nb-nc):-1:0] * dg_test; 80 | g = [g1 g2 g3]; 81 | if abs((sum(g2)*ts) - s*m0) > 10*eps, 82 | fprintf(1,'Area Spec: %f Actual: %f\n', m0, sum(g)*ts); 83 | error('grad_min_bridge: Area not calculated correctly'); 84 | end; 85 | else %% Must be trapezoid 86 | % Subtract area of ramps 87 | % 88 | nb = ceil(mxg/dg); 89 | nc = max(1,ceil(nb*f)); 90 | dg_test = mxg/nb; 91 | a_ramps = (2*nb-nc+1)*nc * dg_test * ts; 92 | 93 | % get number of const samples 94 | % 95 | a_const = m0 - a_ramps; 96 | na = ceil(a_const/ts/mxg); 97 | 98 | % Get correct amplitude now 99 | % 100 | dg_test = m0 / ( ((2*nb-nc+1)*nc + nb*na) *ts); 101 | A = nb * dg_test; 102 | if ((A > mxg) || (dg_test > dg)), 103 | error('Amp/Slew being exceeded'); 104 | end; 105 | g1 = s* [1:(nb-nc)] * dg_test; 106 | g2 = s* [[(nb-nc+1):nb] nb*ones(1,na) [nb:-1:(nb-nc+1)]] * dg_test; 107 | g3 = s* [(nb-nc):-1:0] * dg_test; 108 | g = [g1 g2 g3]; 109 | if abs((sum(g2)*ts) - s*m0) > 10*eps, 110 | error('grad_min_bridge: Area not calculated correctly'); 111 | end; 112 | 113 | end; 114 | 115 | return; 116 | 117 | 118 | -------------------------------------------------------------------------------- /grad_mintrap.m: -------------------------------------------------------------------------------- 1 | function [g] = grad_mintrap (m0, mxg, mxs, ts) 2 | % 3 | % [g] = grad_mintrap (m0, mxg, mxs, ts) 4 | % 5 | % m0 - target zeroth moment (G/cm * s) 6 | % mxg - maximum amplitude (G/cm) 7 | % mxs - maximum slew rate (G/cm/ms) 8 | % ts - sample time in s 9 | % 10 | % g - Gradient 11 | % 12 | 13 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 14 | % 15 | % Spectral-Spatial RF Pulse Design for MRI and MRSI MATLAB Package 16 | % 17 | % Authors: Adam B. Kerr and Peder E. Z. Larson 18 | % 19 | % (c)2007-2011 Board of Trustees, Leland Stanford Junior University and 20 | % The Regents of the University of California. 21 | % All Rights Reserved. 22 | % 23 | % Please see the Copyright_Information and README files included with this 24 | % package. All works derived from this package must be properly cited. 25 | % 26 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 27 | % 28 | % $Header: /home/adam/cvsroot/src/ss/grad_mintrap.m,v 1.4 2013/08/15 03:34:50 adam Exp $ 29 | % 30 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 31 | 32 | if (m0 < 0) 33 | s = -1; 34 | m0 = -m0; 35 | else 36 | s = 1; 37 | end; 38 | 39 | % Convert mxs to G/cm/s 40 | % 41 | mxs = mxs * 1e3; 42 | 43 | % Determine trapezoid parameters 44 | % 45 | % na - number of constant samples 46 | % nb - number ramp samples 47 | % A - trapezoid amplitude 48 | % 49 | 50 | dg = mxs * ts; % Max delta in one sample 51 | nb = ceil(sqrt (m0 / dg / ts)); 52 | A = m0/(nb*ts); 53 | if (A <= mxg), 54 | na = 0; 55 | dg_act = A/nb; 56 | else 57 | nb = ceil (mxg / dg); 58 | dg_act = mxg / nb; 59 | na = ceil((m0 - (nb^2 * dg_act * ts))/mxg/ts); 60 | dg_act = m0 / (nb^2 + na*nb)/ts; 61 | A = nb * dg_act; 62 | end; 63 | 64 | % Construct discrete trapezoid --- always end with a zero value 65 | % 66 | g = s * [[1:nb]*dg_act ones(1,na)*A [nb-1:-1:0]*dg_act]; 67 | 68 | return; 69 | 70 | 71 | -------------------------------------------------------------------------------- /grad_ss.m: -------------------------------------------------------------------------------- 1 | function [gpos,gneg,g1,g2,g3] = grad_ss(m0, n, f, mxg, mxs, ts, equal) 2 | % GRAD_SS - Calculate spectral-spatial bipolar pulse 3 | % 4 | % [gpos, gneg, g1, g2, g3] = grad_ss (m0, n, f, mxg, mxs, ts, equal) 5 | % 6 | % m0 - target zeroth moment (G/cm * s) of one lobe 7 | % n - total number of samples to use if not [] 8 | % f - fraction of ramp to include in bridge [0..1] 9 | % mxg - maximum amplitude (G/cm) 10 | % mxs - maximum slew rate (G/cm/ms) 11 | % ts - sample time in s 12 | % equal - boolean if pos/neg lobes should be same 13 | % 14 | % gpos - Positive lobe gradient 15 | % gneg - Negative lobe gradient 16 | % g1, g2, g3 - Ramp up, bridge, ramp down gradients 17 | % in positive lobe 18 | 19 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 20 | % 21 | % Spectral-Spatial RF Pulse Design for MRI and MRSI MATLAB Package 22 | % 23 | % Authors: Adam B. Kerr and Peder E. Z. Larson 24 | % 25 | % (c)2007-2011 Board of Trustees, Leland Stanford Junior University and 26 | % The Regents of the University of California. 27 | % All Rights Reserved. 28 | % 29 | % Please see the Copyright_Information and README files included with this 30 | % package. All works derived from this package must be properly cited. 31 | % 32 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 33 | % 34 | % $Header: /home/adam/cvsroot/src/ss/grad_ss.m,v 1.9 2013/08/15 03:34:50 adam Exp $ 35 | % 36 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 37 | 38 | m0 = abs(m0); % Must be positive 39 | 40 | % Check n is even if equal lobes called for 41 | % 42 | if equal, 43 | if bitget(n,1) ~= 0, 44 | error('equal lobes specified, but n not even'); 45 | end; 46 | end; 47 | 48 | % Convert mxs to G/cm/s 49 | % 50 | mxs_s = mxs * 1e3; 51 | dg = mxs_s * ts; % Max delta in one sample 52 | 53 | % Determine trapezoid parameters 54 | % 55 | % na - number of constant samples 56 | % nb - number ramp samples 57 | % nc - number samples of ramp in bridge 58 | % A - trapezoid amplitude 59 | % 60 | 61 | % Do different things if number of samples is specified 62 | % 63 | [gp_tmp, g1_tmp, g2_tmp, g3_tmp] = grad_min_bridge(m0, f, mxg, mxs, ts); 64 | if equal, 65 | gn_tmp = -gp_tmp; 66 | else 67 | m0_pos = sum(gp_tmp) * ts; 68 | gn_tmp = grad_mintrap(-m0_pos, mxg, mxs, ts); 69 | end; 70 | 71 | if isempty(n), 72 | gpos = gp_tmp; 73 | g1 = g1_tmp; 74 | g2 = g2_tmp; 75 | g3 = g3_tmp; 76 | gneg = gn_tmp; 77 | else 78 | if (length([gp_tmp gn_tmp]) > n) 79 | error('grad_ss: Solution not obtained in spec num samples'); 80 | end; 81 | 82 | % Save known solution 83 | % 84 | gp_save = gp_tmp; 85 | g1_save = g1_tmp; 86 | g2_save = g2_tmp; 87 | g3_save = g3_tmp; 88 | gn_save = -gp_save; 89 | 90 | nb_save = find(diff(gp_save) == 0, 1, 'first'); 91 | 92 | % Now keep decreasing number of ramp samples in 93 | % positive lobe until "n" exceeded 94 | % 95 | spec_met = 1; 96 | while (spec_met && (nb_save > 1)) 97 | % Get area in ramps 98 | % 99 | nb = nb_save - 1; 100 | nc = max(1,ceil(nb*f)); 101 | a_ramps = (2*nb-nc+1)*nc * dg * ts; 102 | 103 | % Get number of constant samples 104 | % 105 | a_const = m0 - a_ramps; 106 | na = max(0,ceil(a_const/(nb*dg*ts))); 107 | 108 | % Get correct amplitude, gradients now 109 | % 110 | dg_test = m0 / ( ((2*nb-nc+1)*nc + nb*na) *ts); 111 | A = nb * dg_test; 112 | if ((A > mxg) || (dg_test > dg)), 113 | spec_met = 0; 114 | continue; 115 | end; 116 | g1 = [1:(nb-nc)] * dg_test; 117 | g2 = [[(nb-nc+1):nb] nb*ones(1,na) [nb:-1:(nb-nc+1)]] * dg_test; 118 | g3 = [(nb-nc):-1:0] * dg_test; 119 | gp = [g1 g2 g3]; 120 | if abs((sum(g2)*ts) - m0) > 10*eps, 121 | error('grad_ss: Area not calculated correctly'); 122 | end; 123 | 124 | if (equal) 125 | gn = -gp; 126 | else 127 | gn = grad_mintrap(-sum(gp)*ts, mxg, mxs, ts); 128 | end; 129 | 130 | % See if spec still met 131 | % 132 | if length([gp gn]) < n, 133 | spec_met = 1; 134 | 135 | gp_save = gp; 136 | g1_save = g1; 137 | g2_save = g2; 138 | g3_save = g3; 139 | gn_save = gn; 140 | nb_save = nb; 141 | else 142 | spec_met = 0; 143 | end; 144 | end; 145 | 146 | % Fix up result to have "exactly" n samples in it! 147 | % 148 | if ~equal 149 | na = n - length(gn_save) - (2 * nb_save + 1); 150 | else 151 | na = (n - 2*(2 * nb_save + 1))/2; 152 | end; 153 | nb = nb_save; 154 | nc = max(1,ceil(nb*f)); 155 | 156 | % Get correct amplitude, gradients now 157 | % 158 | dg_test = m0 / ( ((2*nb-nc+1)*nc + nb*na) *ts); 159 | A = nb * dg_test; 160 | if ((A >= 1.001 * mxg) || (dg_test > 1.001 * dg)), 161 | error('Amp/Slew being exceeded'); 162 | end; 163 | g1 = [1:(nb-nc)] * dg_test; 164 | g2 = [[(nb-nc+1):nb] nb*ones(1,na) [nb:-1:(nb-nc+1)]] * dg_test; 165 | g3 = [(nb-nc):-1:0] * dg_test; 166 | gpos = [g1 g2 g3]; 167 | if abs((sum(g2)*ts) - m0) > 10*eps, 168 | error('grad_ss: Area not calculated correctly'); 169 | end; 170 | 171 | if (~equal) 172 | ratio = sum(-gn_save)/sum(gpos); 173 | if (ratio < 1-10*eps) 174 | % warning('grad_ss: Improbable ratio'); % fix problem here 175 | end; 176 | gneg = gn_save / ratio; 177 | else 178 | gneg = -gpos; 179 | end; 180 | end; 181 | 182 | 183 | return; 184 | 185 | 186 | -------------------------------------------------------------------------------- /plot_spec.m: -------------------------------------------------------------------------------- 1 | function plot_spec(f, a, d, type) 2 | % PLOT_SPEC - Utility to plot frequency specifications 3 | % 4 | % plot_spec(f, a, d, type) 5 | % 6 | % f - frequency band edges 7 | % a - band amplitudes 8 | % d - band ripple specs 9 | % type - line/plotting type (see S in 'help plot') 10 | 11 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 12 | % 13 | % Spectral-Spatial RF Pulse Design for MRI and MRSI MATLAB Package 14 | % 15 | % Authors: Adam B. Kerr and Peder E. Z. Larson 16 | % 17 | % (c)2007-2011 Board of Trustees, Leland Stanford Junior University and 18 | % The Regents of the University of California. 19 | % All Rights Reserved. 20 | % 21 | % Please see the Copyright_Information and README files included with this 22 | % package. All works derived from this package must be properly cited. 23 | % 24 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 25 | 26 | if nargin < 4, 27 | type = 'k--'; 28 | end; 29 | nband = length(f)/2; 30 | for band = 1:nband, 31 | idx = [band*2-1:band*2]; 32 | plot(f(idx), a(idx)+d(band)*ones(1,2), sprintf('%s',type)); 33 | hold on; 34 | plot(f(idx), a(idx)-d(band)*ones(1,2), sprintf('%s',type)); 35 | end; 36 | -------------------------------------------------------------------------------- /plot_spec_phs.m: -------------------------------------------------------------------------------- 1 | function plot_spec_phs(f, a, d, w, h, type) 2 | % PLOT_SPEC - Utility to plot frequency specifications 3 | % 4 | % plot_spec(f, a, d, type) 5 | % 6 | % f - frequency band edges 7 | % a - band amplitudes 8 | % d - band ripple specs 9 | % type - line/plotting type (see S in 'help plot') 10 | 11 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 12 | % 13 | % Spectral-Spatial RF Pulse Design for MRI and MRSI MATLAB Package 14 | % 15 | % Authors: Adam B. Kerr and Peder E. Z. Larson 16 | % 17 | % (c)2007-2011 Board of Trustees, Leland Stanford Junior University and 18 | % The Regents of the University of California. 19 | % All Rights Reserved. 20 | % 21 | % Please see the Copyright_Information and README files included with this 22 | % package. All works derived from this package must be properly cited. 23 | % 24 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 25 | 26 | if nargin < 6, 27 | type = 'k--'; 28 | end; 29 | nband = length(f)/2; 30 | subplot(211) 31 | for band = 1:nband, 32 | idx = [band*2-1 band*2]; 33 | plot(f(idx), abs(a(band))+abs(d(band))*ones(1,2), sprintf('%s',type)); 34 | hold on; 35 | plot(f(idx), abs(a(band))-abs(d(band))*ones(1,2), sprintf('%s',type)); 36 | end; 37 | plot(w,abs(h)); 38 | 39 | subplot(212) 40 | for band = 1:nband, 41 | idx = [band*2-1 band*2]; 42 | if abs(a(band)) == 0 43 | continue; 44 | end 45 | plot(f(idx), angle(a(band))+angle(d(band))*ones(1,2), sprintf('%s',type)); 46 | hold on; 47 | plot(f(idx), angle(a(band))-angle(d(band))*ones(1,2), sprintf('%s',type)); 48 | end; 49 | plot(w,angle(h)); 50 | -------------------------------------------------------------------------------- /rf_ripple.m: -------------------------------------------------------------------------------- 1 | function [d, a, ang] = rf_ripple(de, a, ang, type) 2 | % RF_RIPPLE - Compute polynomial ripple required for various pulse types 3 | % 4 | % Input 5 | % - de - vector of ripples required 6 | % - a - vector of amplitudes in bands 7 | % - ang - angle of excitation 8 | % - excitation type: 'ex', 'se', 'sat', 'inv' 9 | % 10 | % Output 11 | % - d: ripples for filter design 12 | % - a: band amplitudes for filter design 13 | % - ang: angle of excitation 14 | 15 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 16 | % 17 | % Spectral-Spatial RF Pulse Design for MRI and MRSI MATLAB Package 18 | % 19 | % Authors: Adam B. Kerr and Peder E. Z. Larson 20 | % 21 | % (c)2007-2011 Board of Trustees, Leland Stanford Junior University and 22 | % The Regents of the University of California. 23 | % All Rights Reserved. 24 | % 25 | % Please see the Copyright_Information and README files included with this 26 | % package. All works derived from this package must be properly cited. 27 | % 28 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 29 | 30 | 31 | nband = length(de); 32 | for band = 1:nband, 33 | db = de(band); 34 | ab = a(band); 35 | switch (type ) 36 | case 'ex' 37 | % Determine B ripple originating from mxy ripple, 38 | % find which is most significant, then use this 39 | % to set the ripple 40 | % 41 | mxy_mid = sin(2*asin(sin(ang/2)*ab)); 42 | mxy_pos = max(-1,min(1,db + mxy_mid)); 43 | mxy_neg = max(-1,min(1,-db + mxy_mid)); 44 | 45 | B_mid = sin(ang/2) * ab; 46 | B_pos = sin(asin(mxy_pos)/2); 47 | B_neg = sin(asin(mxy_neg)/2); 48 | 49 | d(band) = max(abs(B_pos-B_mid), abs(B_neg-B_mid))/sin(ang/2); 50 | case 'se' 51 | mxy_mid = (sin(ang/2)*ab)^2; 52 | mxy_pos = max(0,min(1,db + mxy_mid)); 53 | mxy_neg = max(0,min(1,-db + mxy_mid)); 54 | 55 | % check for ripples that will result in B > 1 and scale back 56 | % a if necessary 57 | if (mxy_pos >= 1) || (mxy_neg >= 1) 58 | ab = sqrt(1 - abs(db)) / sin(ang/2); 59 | mxy_mid = (sin(ang/2)*ab)^2; 60 | mxy_pos = max(0,min(1,db + mxy_mid)); 61 | mxy_neg = max(0,min(1,-db + mxy_mid)); 62 | a(band) = ab; 63 | end 64 | 65 | B_mid = sin(ang/2) * ab; 66 | B_pos = sqrt(mxy_pos); 67 | B_neg = sqrt(mxy_neg); 68 | 69 | d(band) = max(abs(B_pos-B_mid), abs(B_neg-B_mid))/sin(ang/2); 70 | % if a(band) == 1, 71 | % d(band) = de(band)/4; 72 | % else 73 | % d(band) = sqrt(de(band)); 74 | % end; 75 | case {'inv','sat'} 76 | mz_mid = 1 - 2* (sin(ang/2)*ab)^2; 77 | mz_pos = max(-1,min(1,db + mz_mid)); 78 | mz_neg = max(-1,min(1,-db + mz_mid)); 79 | 80 | % check for ripples that will result in B > 1 and scale back 81 | % a if necessary 82 | if (mz_neg <= -1) || (mz_pos <= -1) 83 | ab = sqrt(1 - abs(db)/2) / sin(ang/2); 84 | mz_mid = 1 - 2* (sin(ang/2)*ab)^2; 85 | mz_pos = max(-1,min(1,db + mz_mid)); 86 | mz_neg = max(-1,min(1,-db + mz_mid)); 87 | a(band) = ab; 88 | end 89 | 90 | B_mid = sin(ang/2) * ab; 91 | B_pos = sin(acos(mz_pos)/2); 92 | B_neg = sin(acos(mz_neg)/2); 93 | % B_pos = sqrt((1-mz_pos)/2); % same as above 94 | % B_neg = sqrt((1-mz_neg)/2); 95 | 96 | d(band) = max(abs(B_pos-B_mid), abs(B_neg-B_mid))/sin(ang/2); 97 | % if a(band) == 1, 98 | % d(band) = de(band)/8; 99 | % else 100 | % d(band) = sqrt(de(band)/2); 101 | % end; 102 | % case 'sat' 103 | % if a(band) == 1, 104 | % d(band) = de(band)/2; 105 | % else 106 | % d(band) = sqrt(de(band)); 107 | % end; 108 | 109 | end; 110 | end; 111 | 112 | 113 | % scale a if the peak angle has been reduced 114 | if max(a) ~= 1 115 | max_a = max(a); 116 | a = a / max_a; 117 | ang = 2*asin(sin(ang/2)*max_a); 118 | d = d / max_a; 119 | end -------------------------------------------------------------------------------- /rf_tools/README: -------------------------------------------------------------------------------- 1 | This directory contains all the rf_tools matlab stuff. Put this in 2 | your MATLABPATH environment variable 3 | 4 | setenv MATLABPATH /usr/local/wherever/rf_tools 5 | 6 | if that is where you put it. 7 | 8 | You need to compile the simulator, and various parts of the design 9 | algorithm. These are the the "mexX" subdirectories, where X is 4 or 5 10 | depending on whether you are using matlab 4 or 5. 11 | 12 | If you are using matlab 4, cd to mex4, do a "make all", and copy the 13 | .mexsol files back into this directory. 14 | 15 | If you are using matlab 5, run matlab, and cd to the mex5 directory. 16 | If you are using the default compiler on your platform, then invoke 17 | 18 | mex abrx.c 19 | mex b2rf.c 20 | mex b2a.c 21 | mex cabc2rf.c 22 | 23 | Then quit matlab, and copy the mex5/*.mexsol files into the rf_tools 24 | directory. I use gcc under Solaris, in which case, use 25 | 26 | mex CC=gcc OPTFLAGS=-fPIC abrx.c 27 | 28 | instead. The -fPIC flag is important. Without it, things seem to 29 | work, but fail occasionally for no apparent reason. Also, make sure 30 | /usr/ccs/bin and the matlab5.1/bin directory are in your path, and 31 | that you use the Sun make, as, and ld, and not the gnu version that 32 | may be lurking elsewhere. 33 | 34 | There is documentation in man.ps, as well as online help for most 35 | routines. 36 | 37 | New features for this release are support throughout for 2D RF pulses 38 | including design, simulation and plotting. The routine dz2d designs 39 | spiral 2D pulses. The spectral-spatial pulse design routine is new. 40 | It does a true 2D SLR design, so it works fine for large flip angles. 41 | 42 | Let me know if if find any bugs, would like things to work differently, 43 | or have anything you like to see added. 44 | 45 | -- John (pauly@mrsrl.stanford.edu) 46 | 47 | -------------------------------------------------------------------------------- /rf_tools/ab2ex.m: -------------------------------------------------------------------------------- 1 | function mxy = ab2ex(a,b); 2 | 3 | % mxy = ab2ex(a,b) -- or -- mxy = ab2ex(ab) 4 | % 5 | % Computes the excitation profile 2*conj(a).b 6 | 7 | % written by John Pauly, 1992 8 | % (c) Board of Trustees, Leland Stanford Junior University 9 | 10 | if nargin == 1, 11 | [m n] = size(a); 12 | b = a(:,(n/2+1):n); 13 | a = a(:,1:n/2); 14 | end; 15 | 16 | mxy = 2*conj(a).*b; 17 | -------------------------------------------------------------------------------- /rf_tools/ab2inv.m: -------------------------------------------------------------------------------- 1 | % mz = ab2inv(a,b) -- or -- mz = ab2inv(ab) 2 | % 3 | % Computes the inversion profile 1-2*b.*conj(b) 4 | 5 | % written by John Pauly, 1992 6 | % (c) Board of Trustees, Leland Stanford Junior University 7 | 8 | function mz = ab2inv(a,b); 9 | 10 | if nargin == 1, 11 | [m n] = size(a); 12 | b = a(:,(n/2+1):n); 13 | a = a(:,1:n/2); 14 | end; 15 | 16 | mz = 1-2*conj(b).*b; 17 | -------------------------------------------------------------------------------- /rf_tools/ab2sat.m: -------------------------------------------------------------------------------- 1 | % mz = ab2sat(a,b) -- or -- mz = ab2sat(ab) 2 | % 3 | % Computes the saturation profile 1-2*b.*conj(b) 4 | 5 | % written by John Pauly, 1992 6 | % (c) Board of Trustees, Leland Stanford Junior University 7 | 8 | function mz = ab2sat(a,b); 9 | 10 | if nargin == 1, 11 | [m n] = size(a); 12 | b = a(:,(n/2+1):n); 13 | a = a(:,1:n/2); 14 | end; 15 | 16 | mz = 1-2*conj(b).*b; 17 | 18 | -------------------------------------------------------------------------------- /rf_tools/ab2se.m: -------------------------------------------------------------------------------- 1 | % mxy = ab2se(a,b) -- or -- mxy = ab2se(ab) 2 | % 3 | % Computes the spin-echo profile i*b.*b 4 | 5 | % written by John Pauly, 1992 6 | % (c) Board of Trustees, Leland Stanford Junior University 7 | 8 | function mxy = ab2se(a,b); 9 | 10 | if nargin == 1, 11 | [m n] = size(a); 12 | b = a(:,(n/2+1):n); 13 | a = a(:,1:n/2); 14 | end 15 | 16 | mxy = i*b.*b; -------------------------------------------------------------------------------- /rf_tools/ab2st.m: -------------------------------------------------------------------------------- 1 | % mxy = ab2se(a,b) -- or -- mxy = ab2se(ab) 2 | % 3 | % Computes the "straight through" term a.*a 4 | 5 | % written by John Pauly, 1992 6 | % (c) Board of Trustees, Leland Stanford Junior University 7 | 8 | function mxy = ab2st(a,b); 9 | 10 | if nargin == 2, 11 | mxy = i*a.*a; 12 | elseif nargin == 1, 13 | mxy = i*a(:,1).*a(:,1); 14 | else 15 | ab2xx 16 | end 17 | 18 | -------------------------------------------------------------------------------- /rf_tools/abr.m: -------------------------------------------------------------------------------- 1 | % abr - compute the Cayley-Klein parameters for the rotation produced 2 | % by an RF pulse. Uses Le Roux's convention on beta 3 | % 4 | % [a b] = abr(rf, x); 5 | % a, b = Cayley-Klein paramters 6 | % rf = n point rf waveform 7 | % g = optional gradient waveform 8 | % x = vectors of spatial positions to compute a and b. 9 | % 10 | % useful identities: 11 | % mxy = 2*conj(a).*b); selective excitation 12 | % mz = 1 - 2*b.*conj(b); inversion 13 | % mxy = i(b.*b); spin echo profile 14 | % 15 | 16 | % written by John Pauly, 1992 17 | % (c) Board of Trustees, Leland Stanford Junior University 18 | 19 | function [a, b] = abr(rf, g, x, y) 20 | 21 | l = length(rf); 22 | 23 | if nargin == 2, 24 | x = g; 25 | g = ones(1,l)*2*pi/l; 26 | end; 27 | 28 | if nargin == 4, 29 | [a b] = abrx(rf, g, x, y); 30 | elseif (nargin==2) || (nargin==3), 31 | [a b] = abrx(rf, g, x); 32 | end; 33 | 34 | b = -conj(b); 35 | if nargout == 1, 36 | a = [a b]; 37 | end; 38 | 39 | 40 | -------------------------------------------------------------------------------- /rf_tools/cplot.m: -------------------------------------------------------------------------------- 1 | % 2 | % cplot([x,] y) - plot complex function; optional specification of x-axis 3 | % 4 | 5 | % written by John Pauly, 1989 6 | % modified by Peder Larson, 2006 7 | % (c) Board of Trustees, Leland Stanford Junior University 8 | 9 | function cplot(x,y,s) 10 | 11 | if nargin == 1 12 | y = x; 13 | l = length(x); 14 | x = [1:l]/(l+1); 15 | end 16 | 17 | if nargin < 3 18 | plot(x,real(y),x,imag(y)); 19 | elseif nargin == 3 20 | plot(x,real(y),s,x,imag(y),s); 21 | end 22 | 23 | 24 | -------------------------------------------------------------------------------- /rf_tools/csg.m: -------------------------------------------------------------------------------- 1 | % function [nk] = csg(k,mxg,mxs,[g0, gamma]) 2 | % 3 | % This routine takes a k-space trajectory and time warps it to 4 | % meet gradient amplitude and slew rate constraints. 5 | % 6 | % Inputs: 7 | % k -- k-space trajectory, scaled to cycles/cm 8 | % mxg -- maximum gradient, G/cm 9 | % mxs -- maximum slew rate, (G/cm)/ms 10 | % g0 (optional) -- initial gradient strength, G/cm 11 | % gamma (optional) -- kHz/G 12 | % 13 | % Outputs: 14 | % nk -- new k-space trajectory meeting the constraints 15 | % 16 | % csg also reports the gradient duration required. 17 | % 18 | 19 | % Written by John Pauly, 1993 20 | % 21 | % Oct 4, 2004 modified to use 'spline' in interp1, now that it works in 22 | % matlab 7 23 | 24 | function [nk] = csg(k,mxg,mxs,g0,gamma) 25 | 26 | if nargin < 5 27 | gamma = 4.257; 28 | end 29 | 30 | td = 1; 31 | len = length(k); 32 | 33 | % compute initial gradient, slew rate 34 | g = [diff(k)]/(gamma*(td/len)); 35 | if nargin < 4 || isempty(g0) 36 | g = [0 g]; 37 | else 38 | g = [g0 g]; 39 | end 40 | s = diff(g)/(td/len); 41 | %s = [s s(end)]; 42 | 43 | % Compute slew rate limited trajectory 44 | nk = k; 45 | 46 | for N = 1:5 47 | g = [diff(nk)]/(gamma*(td/len)); 48 | if nargin < 4 || isempty(g0) 49 | g = [0 g]; 50 | else 51 | g = [g0 g]; 52 | end 53 | s = diff(g)/(td/len); 54 | ndts = sqrt(abs(s/mxs)); 55 | nt = [0,cumsum(ndts)*td/len]; 56 | %nk = csplinx(nt,k,[1:len]*nt(len)/len); 57 | nk = interp1(nt,nk,[0:len-1]*nt(len)/(len-1),'spline'); 58 | end 59 | 60 | 61 | % Apply the additional gradient amplitude constraint 62 | g = [0 diff(nk)]/(gamma*(nt(len)/len)); 63 | 64 | %ndtg = max(abs(g/mxg),1); 65 | ndtg = max(abs(g),mxg); 66 | nt = [0, cumsum(ndtg(1:end-1))*nt(len)/(mxg*len)]; 67 | %nk = csplinx(nt,nk,[1:len]*nt(len)/len); 68 | nk = interp1(nt,nk,[0:len-1]*nt(len)/(len-1),'spline'); 69 | 70 | % report the waveform length 71 | disp(sprintf('Gradient duration is %6.3f ms',nt(len))); 72 | 73 | -------------------------------------------------------------------------------- /rf_tools/csg2.m: -------------------------------------------------------------------------------- 1 | % function [nk] = csg(k,mxg,mxs) 2 | % 3 | % This routine takes a k-space trajectory and time warps it to 4 | % meet gradient amplitude and slew rate constraints. 5 | % 6 | % Inputs: 7 | % k -- k-space trajectory, scaled to cycles/cm 8 | % mxg -- maximum gradient, G/cm 9 | % mxs -- maximum slew rate, (G/cm)/ms 10 | % 11 | % Outputs: 12 | % nk -- new k-space trajectory meeting the constraints 13 | % 14 | % csg also reports the gradient duration required. 15 | % 16 | 17 | % Written by John Pauly, 1993 18 | 19 | function [nk] = csg(k,mxg,mxs) 20 | 21 | td = 1; 22 | len = length(k); 23 | 24 | g0 = k(2)-k(1); 25 | g1 = k(3)-k(2); 26 | s0 = g1 - g0; 27 | gm1 = g0-s0; 28 | 29 | % compute initial gradient, slew rate 30 | g = [gm1 diff(k)]/(4.26*(td/len)); 31 | s = diff(g)/(td/len); 32 | s = [s(1) s]; 33 | 34 | % Compute slew rate limited trajectory 35 | ndts = sqrt(abs(s/mxs)); 36 | nt = cumsum(ndts)*td/len; 37 | nk = csplinx(nt,k,[1:len]*nt(len)/len); 38 | 39 | % Apply the additional gradient amplitude constraint 40 | g = [0 diff(nk)]/(4.26*(nt(len)/len)); 41 | ndtg = max(abs(g),mxg); 42 | nt = cumsum(ndtg)*nt(len)/(mxg*len); 43 | nk = csplinx(nt,nk,[1:len]*nt(len)/len); 44 | 45 | % report the waveform length 46 | disp(sprintf('Gradient duration is %6.3f ms',nt(len))); 47 | 48 | -------------------------------------------------------------------------------- /rf_tools/dimp.m: -------------------------------------------------------------------------------- 1 | % dimp -- calculate D infinity for a minimum phase filter 2 | 3 | % written by John Pauly, 1992 4 | % (c) Board of Trustees, Leland Stanford Junior University 5 | 6 | function d=dimp(d1,d2) 7 | 8 | d = 0.5*dinf(2*d1,0.5*d2.*d2); 9 | 10 | 11 | -------------------------------------------------------------------------------- /rf_tools/dinf.m: -------------------------------------------------------------------------------- 1 | % dinf -- calculate D infinity for a linear phase filter 2 | 3 | % written by John Pauly, 1992 4 | % (c) Board of Trustees, Leland Stanford Junior University 5 | 6 | function d = dinf(d1,d2) 7 | 8 | a1 = 5.309e-3; 9 | a2 = 7.114e-2; 10 | a3 = -4.761e-1; 11 | a4 = -2.66e-3; 12 | a5 = -5.941e-1; 13 | a6 = -4.278e-1; 14 | 15 | l10d1 = log10(d1); 16 | l10d2 = log10(d2); 17 | 18 | [m1 n1] = size(l10d1); 19 | if (m1 15 | #include 16 | #include 17 | #include "mex.h" 18 | 19 | #define RF prhs[0] /* complex rf */ 20 | #define GC prhs[1] /* complex gradient */ 21 | #define X prhs[2] /* x samples */ 22 | #define Y prhs[3] /* y samples */ 23 | 24 | #define ALF plhs[0] /* 2D grid of alpha's */ 25 | #define BET plhs[1] /* 2D grid of alpha's */ 26 | 27 | #define max(a,b) ((a)>(b) ? (a) : (b)) 28 | #define min(a,b) ((a)<(b) ? (a) : (b)) 29 | 30 | double *rfi, *rfq, *gx, *gy, x, y, *alpr, *alpi, *btpr, *btpi, *xp, *yp; 31 | int ns; 32 | char s[80]; 33 | void abrot(double a[2], double b[2]); 34 | 35 | void mexFunction(int nlhs,mxArray *plhs[], int nrhs, const mxArray *prhs[]) 36 | { 37 | int nx, ny, ix, iy; 38 | double alf[2], bet[2]; 39 | 40 | if ((nrhs < 3) || (nrhs > 4) || (nlhs != 2)) 41 | mexErrMsgTxt("Usage: [alpha, beta] = abrx(rf, g, x {, y})"); 42 | 43 | ns = max(mxGetN(RF),mxGetM(RF)); 44 | if (ns != max(mxGetN(GC),mxGetM(GC))) 45 | mexErrMsgTxt("rf and gradient vectors are of different lengths"); 46 | 47 | /* mexErrMsgTxt(sprintf(s,"ns=%d",ns)); */ 48 | 49 | gx = mxGetPr(GC); 50 | if (nrhs==4) gy = mxGetPi(GC); 51 | rfi = mxGetPr(RF); rfq = mxGetPi(RF); 52 | 53 | nx = max(mxGetN(X),mxGetM(X)); 54 | if (nrhs==4) 55 | ny = max(mxGetN(Y),mxGetM(Y)); 56 | else 57 | ny = 1; 58 | 59 | ALF = mxCreateDoubleMatrix(nx,ny,mxCOMPLEX); 60 | alpr = mxGetPr(ALF); alpi = mxGetPi(ALF); 61 | BET = mxCreateDoubleMatrix(nx,ny,mxCOMPLEX); 62 | btpr = mxGetPr(BET); btpi = mxGetPi(BET); 63 | 64 | if (nrhs==4) yp = mxGetPr(Y); 65 | xp = mxGetPr(X); 66 | 67 | for (iy=0; iy0.0) { 95 | nx = cpr/phi; ny = cpi/phi; nz = cg/phi; 96 | } else { 97 | nx = 0.0; ny = 0.0; nz = 1.0; /* doesn't matter, phi=0*/ 98 | } 99 | csp = cos(phi/2); snp = sin(phi/2); 100 | al[0] = csp; al[1] = nz*snp; 101 | be[0] = ny*snp; be[1] = nx*snp; 102 | 103 | bp[0] = al[0]*b[0]-al[1]*b[1]+be[0]*a[0]-be[1]*(-a[1]); 104 | bp[1] = al[0]*b[1]+al[1]*b[0]+be[1]*a[0]+be[0]*(-a[1]); 105 | 106 | ap[0] = -( be[0] *b[0]-(-be[1])*b[1]) 107 | + al[0] *a[0]-(-al[1])*(-a[1]); 108 | ap[1] = -(-(-(be[1])*b[0]+ be[0] *b[1]) 109 | + (-al[1])*a[0]+ al[0] *(-a[1])); 110 | 111 | a[0] = ap[0]; a[1] = ap[1]; b[0] = bp[0]; b[1] = bp[1]; 112 | } 113 | 114 | return; 115 | } 116 | -------------------------------------------------------------------------------- /rf_tools/mex_files/abrx.m: -------------------------------------------------------------------------------- 1 | function [varargout] = abrx(varargin) 2 | 3 | % AUTOCOMPILE compile the missing mex file on the fly 4 | 5 | % remember the original working directory 6 | pwdir = pwd; 7 | 8 | % determine the name and full path of this function 9 | funname = mfilename('fullpath'); 10 | mexsrc = [funname '.c']; 11 | [mexdir, mexname] = fileparts(funname); 12 | 13 | try 14 | % try to compile the mex file on the fly 15 | warning('trying to compile MEX file from %s', mexsrc); 16 | cd(mexdir); 17 | mex(mexsrc); 18 | cd(pwdir); 19 | success = true; 20 | 21 | catch 22 | % compilation failed 23 | disp(lasterr); 24 | error('could not locate MEX file for %s', mexname); 25 | cd(pwdir); 26 | success = false; 27 | end 28 | 29 | if success 30 | % execute the mex file that was juist created 31 | funname = mfilename; 32 | funhandle = str2func(funname); 33 | [varargout{1:nargout}] = funhandle(varargin{:}); 34 | end -------------------------------------------------------------------------------- /rf_tools/mex_files/abrx.mexmaci64: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LarsonLab/Spectral-Spatial-RF-Pulse-Design/59f4f3a2f404dad147ef292a16cc00be63a77cd7/rf_tools/mex_files/abrx.mexmaci64 -------------------------------------------------------------------------------- /rf_tools/mex_files/abrx.mexw64: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LarsonLab/Spectral-Spatial-RF-Pulse-Design/59f4f3a2f404dad147ef292a16cc00be63a77cd7/rf_tools/mex_files/abrx.mexw64 -------------------------------------------------------------------------------- /rf_tools/mex_files/b2a.c: -------------------------------------------------------------------------------- 1 | /* 2 | * [rf] = b2rf (b) 3 | * 4 | * C program version of b2rf. 5 | * 6 | * Mex driver for subroutine. 7 | * Gets complex rf from complex polynomial b. 8 | * 9 | * Written by Adam Kerr, October 1992 10 | * Modified from John Pauly's code. 11 | * (c) Board of Trustees, Leland Stanford Junior University 12 | */ 13 | 14 | #include 15 | #include 16 | #include "mex.h" 17 | 18 | #define MAXN (1024) 19 | 20 | #define max(a,b) ((a)>(b) ? (a) : (b)) 21 | 22 | /* driver for matlab call */ 23 | 24 | #define B prhs[0] /* alpha polynomial */ 25 | #define A plhs[0] /* RF pulse */ 26 | void b2a(double *b, int n, double *a); 27 | void cabc2rf(double *a, double *b, int n, double *rf); 28 | double a[MAXN*2], b[MAXN*2]; 29 | double rf[MAXN*2]; 30 | 31 | void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) 32 | { 33 | 34 | double *bpr, *bpi, *ar, *ai; 35 | int n; 36 | int i, j; 37 | 38 | if ((nrhs != 1) || (nlhs != 1)) 39 | mexErrMsgTxt("Usage: [rf] = b2rf (b)"); 40 | 41 | n = max (mxGetN(B), mxGetM(B)); 42 | if (n > MAXN) 43 | mexErrMsgTxt("beta polynomial too long"); 44 | 45 | /* copy b into array */ 46 | bpr = mxGetPr(B); 47 | bpi = mxGetPi(B); 48 | if (bpi != NULL) 49 | for (i=0; i 15 | 16 | #define ZEROPAD (16) 17 | #define MAXN (1024 * ZEROPAD) 18 | #define MP (0.0000001) 19 | 20 | #define max(a,b) ((a)>(b) ? (a) : (b)) 21 | #define min(a,b) ((a)<(b) ? (a) : (b)) 22 | #define magsqr(a,j) (a[2*j]*a[2*j] + a[2*j+1]*a[2*j+1]) 23 | void four1(double bf[], int nnc, int fwd); 24 | double bf[MAXN*2], am[MAXN*2], af[MAXN*2]; 25 | 26 | void b2a(double *b, int n, double *a) 27 | { 28 | double bmx, bm, p; 29 | int i, j, nn, nnc; 30 | char s[80]; 31 | 32 | /* next bigger power of 2 */ 33 | for (nn=1; nn<=n; nn*=2); 34 | nn *=2; 35 | /*nn = (int) (exp(log(2)*ceil(log((double)n)/log(2))))+1;*/ 36 | 37 | /* size of arrays used for computation */ 38 | nnc = nn*ZEROPAD; 39 | 40 | for (i=0; i bmx) 50 | bmx = bm; 51 | } 52 | if (bmx >= 1.0) 53 | for (i=0; i 15 | #include 16 | #include "mex.h" 17 | 18 | #define MAXN (1024) 19 | 20 | #define max(a,b) ((a)>(b) ? (a) : (b)) 21 | 22 | /* driver for matlab call */ 23 | 24 | #define B prhs[0] /* alpha polynomial */ 25 | #define RF plhs[0] /* RF pulse */ 26 | void b2a(double *b, int n, double *a); 27 | void cabc2rf(double *a, double *b, int n, double *rf); 28 | double a[MAXN*2], b[MAXN*2]; 29 | double rf[MAXN*2]; 30 | 31 | void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) 32 | { 33 | 34 | double *bpr, *bpi, *rfr, *rfi; 35 | int n; 36 | int i, j; 37 | 38 | if ((nrhs != 1) || (nlhs != 1)) 39 | mexErrMsgTxt("Usage: [rf] = b2rf (b)"); 40 | 41 | n = max (mxGetN(B), mxGetM(B)); 42 | if (n > MAXN) 43 | mexErrMsgTxt("beta polynomial too long"); 44 | 45 | /* copy b into array */ 46 | bpr = mxGetPr(B); 47 | bpi = mxGetPi(B); 48 | if (bpi != NULL) 49 | for (i=0; i 17 | #include 18 | #include "mex.h" 19 | 20 | #define MAXN (4096) 21 | 22 | #define max(a,b) ((a)>(b) ? (a) : (b)) 23 | 24 | /* driver for matlab call */ 25 | 26 | #define A prhs[0] /* alpha, beta polynomials */ 27 | #define B prhs[1] 28 | 29 | #define RF plhs[0] /* RF out - complex */ 30 | 31 | void cabc2rf( double *a, double *b, int n, double *rf); 32 | 33 | double a[MAXN*2], b[MAXN*2], rf[MAXN*2]; 34 | 35 | 36 | void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) 37 | { 38 | 39 | double *ar, *ai, *br, *bi, *rfr, *rfi; 40 | 41 | int n; 42 | int i, j; 43 | 44 | if ((nrhs != 2) || (nlhs != 1)) 45 | mexErrMsgTxt("Usage: [rf] = cabc2rf (a,b)"); 46 | 47 | n = max (mxGetN(A), mxGetM(A)); 48 | if (n > MAXN) 49 | mexErrMsgTxt ("alpha polynomial too long"); 50 | 51 | if (max (mxGetN(B), mxGetM(B)) != n) 52 | mexErrMsgTxt ("alpha, beta polynomials MUST be same length"); 53 | 54 | /* copy a, b into arrays */ 55 | ar = mxGetPr(A); ai = mxGetPi(A); 56 | br = mxGetPr(B); bi = mxGetPi(B); 57 | for (i=0; i 15 | 16 | #define MAXN (1024) 17 | 18 | #define max(a,b) ((a)>(b) ? (a) : (b)) 19 | #define min(a,b) ((a)<(b) ? (a) : (b)) 20 | #define mag(a,j) (sqrt (a[(j)*2]*a[(j)*2]+a[(j)*2+1]*a[(j)*2+1])) 21 | #define re_div(a,i,b,j) ((a[i*2]*b[j*2] + a[i*2+1]*b[j*2+1])/(b[j*2]*b[j*2] + b[j*2+1]*b[j*2+1])) 22 | #define im_div(a,i,b,j) ((a[i*2+1]*b[j*2] - a[i*2]*b[j*2+1])/(b[j*2]*b[j*2] + b[j*2+1]*b[j*2+1])) 23 | double a2[MAXN*2], b2[MAXN*2]; 24 | 25 | 26 | void cabc2rf(double a[], double b[], int n, double rf[]) 27 | { 28 | int i, j; 29 | double sj[2], cj; 30 | double phi, theta; /* use definitions from SLR paper, somewhat 31 | different from those in John's abc2rf() */ 32 | 33 | for (j=n-1; j>=0; j--) 34 | { 35 | /* get real cj and complex sj now */ 36 | cj = sqrt (1 / (1 + (b[j*2]*b[j*2] + b[j*2+1]*b[j*2+1])/ 37 | (a[j*2]*a[j*2] + a[j*2+1]*a[j*2+1]) )); 38 | sj[0] = re_div (b,j,a,j) * cj; 39 | sj[1] = -im_div (b,j,a,j) * cj; 40 | 41 | /* get phi and theta now */ 42 | phi = 2 * atan2 (mag(sj,0), cj); 43 | theta = atan2 (sj[1],sj[0]); 44 | 45 | /* get rf now from phi and theta */ 46 | rf[j*2] = phi * cos (theta); 47 | rf[j*2+1] = phi * sin (theta); 48 | 49 | /* create new polynomials now */ 50 | for (i=0; i<=j; i++) 51 | { 52 | a2[i*2] = cj * a[i*2] + sj[0] * b[i*2] - sj[1] * b[i*2+1]; 53 | a2[i*2+1] = cj * a[i*2+1] + sj[0] * b[i*2+1] + sj[1] * b[i*2]; 54 | b2[i*2] = -sj[0] * a[i*2] - sj[1] * a[i*2+1] + cj * b[i*2]; 55 | b2[i*2+1] = -sj[0] * a[i*2+1] + sj[1] * a[i*2] + cj * b[i*2+1]; 56 | } 57 | 58 | /* copy back into old polynomials now */ 59 | /* try other way 60 | for (i=0; i<=j-1; i++) 61 | { 62 | a[i*2] = a2[i*2+2]; 63 | a[i*2+1] = a2[i*2+3]; 64 | b[i*2] = b2[i*2]; 65 | b[i*2+1] = b2[i*2+1]; 66 | } */ 67 | for (i=0; i<=2*j-1; i++) 68 | { 69 | a[i] = a2[i+2]; 70 | b[i] = b2[i]; 71 | } 72 | } 73 | } 74 | 75 | -------------------------------------------------------------------------------- /rf_tools/mex_files/four1.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #define SWAP(a,b) tempr=(a);(a)=(b);(b)=tempr 4 | 5 | void four1(double data[], int nn, int isign) 6 | { 7 | int n,mmax,m,j,istep,i; 8 | double wtemp,wr,wpr,wpi,wi,theta; 9 | double tempr,tempi; 10 | 11 | n=nn << 1; 12 | j=1; 13 | for (i=1;i i) { 15 | SWAP(data[j],data[i]); 16 | SWAP(data[j+1],data[i+1]); 17 | } 18 | m=n >> 1; 19 | while (m >= 2 && j > m) { 20 | j -= m; 21 | m >>= 1; 22 | } 23 | j += m; 24 | } 25 | mmax=2; 26 | while (n > mmax) { 27 | istep=2*mmax; 28 | theta=6.28318530717959/(isign*mmax); 29 | wtemp=sin(0.5*theta); 30 | wpr = -2.0*wtemp*wtemp; 31 | wpi=sin(theta); 32 | wr=1.0; 33 | wi=0.0; 34 | for (m=1;m= f(band*2-1)) & (w <= f(band*2)) ); 34 | idx_band = [idx_band idx]; 35 | end; 36 | wt_band = 10; 37 | wt = ones(length(w),1); 38 | wt(idx_band) = wt_band; 39 | 40 | % Get reference transform 41 | % 42 | t_ref = [0:N-1]; 43 | Wref = exp(-i*kron(w', t_ref)); 44 | Fref = Wref * h(:); 45 | Fref_wt = wt .* Fref; 46 | 47 | if (dbg >= 2), 48 | filt_fig = figure; 49 | end; 50 | 51 | 52 | ni_2 = floor(ni/2); 53 | for idx = 1:ni 54 | % Get actual sampling positions 55 | % 56 | t_act = t_ref + off + (idx-1)/ni; 57 | 58 | if (dbg >= 2) 59 | figure(filt_fig); 60 | subplot(411); 61 | stem([t_ref.' t_act.'], ones(length(t_ref),2)); 62 | legend('Reference', 'Actual'); 63 | title('Sampling Locations'); 64 | end; 65 | 66 | % Get actual, add weights 67 | % 68 | Wact = exp(-i*kron(w', t_act)); 69 | Wact_wt = repmat(wt,1,length(t_act)) .* Wact; 70 | 71 | % Get new filter 72 | % 73 | hi(idx,:) = pinv(Wact_wt)*Fref_wt; 74 | 75 | if ( (dbg >= 2) && (rem(idx,4)==0) ), 76 | plot_db = 0; 77 | figure(filt_fig); 78 | 79 | Fact = Wact * h(:); 80 | Fact_fix = Wact * hi(idx,:).'; 81 | 82 | subplot(4,1,2); 83 | hold off; 84 | plot(abs(h)); 85 | hold on; 86 | plot(abs(hi(idx)), 'r--'); 87 | title('Beta Polynomials'); 88 | 89 | subplot(4,1,3); 90 | if plot_db, 91 | hold off; 92 | plot(w/pi,20*log10(abs(Fref)),'b-'); 93 | hold on; 94 | plot(w/pi, 20*log10(abs(Fact)), 'g--'); 95 | plot(w/pi, 20*log10(abs(Fact_fix)), 'r--'); 96 | ylabel('DB Scale'); 97 | else 98 | hold off; 99 | plot(w/pi,abs(Fref),'b-'); 100 | hold on; 101 | plot(w/pi, abs(Fact), 'g--'); 102 | plot(w/pi, abs(Fact_fix), 'r--'); 103 | ylabel('Linear Scale'); 104 | end; 105 | 106 | title('Magnitude Response'); 107 | 108 | subplot(4,1,4); 109 | hold off; 110 | plot(w/pi,angle(Fref),'b-'); 111 | hold on; 112 | plot(w/pi,angle(Fact),'g--'); 113 | plot(w/pi,angle(Fact_fix),'r--'); 114 | title('Phase Response'); 115 | 116 | fprintf(1,'Offset: %f -- Hit any key to continue\n', off + (idx-1)/ni); 117 | pause; 118 | end; 119 | 120 | 121 | end; 122 | 123 | hi = hi(:); 124 | 125 | return; 126 | 127 | 128 | 129 | 130 | 131 | -------------------------------------------------------------------------------- /spec_interp_nonuniform.m: -------------------------------------------------------------------------------- 1 | function [hi, t_all] = spec_interp_nonuniform(h, ni, off, f, dbg) 2 | % SPEC_INTERP_NONUNIFORM - Interpolate filter to nonuniform taps while 3 | % keeping spectral response consistent 4 | % 5 | 6 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 7 | % 8 | % Spectral-Spatial RF Pulse Design for MRI and MRSI MATLAB Package 9 | % 10 | % Authors: Adam B. Kerr and Peder E. Z. Larson 11 | % 12 | % (c)2007-2011 Board of Trustees, Leland Stanford Junior University and 13 | % The Regents of the University of California. 14 | % All Rights Reserved. 15 | % 16 | % Please see the Copyright_Information and README files included with this 17 | % package. All works derived from this package must be properly cited. 18 | % 19 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 20 | 21 | 22 | if isempty(off) 23 | off = 0.5; 24 | end 25 | 26 | N = length(h); 27 | mult_factor = 15; 28 | w = linspace(-pi, pi, 2*mult_factor*N); 29 | 30 | % Try weighting samples that are in frequency band 31 | % more heavily 32 | % 33 | f = f * pi; 34 | idx_band = []; 35 | nband = length(f)/2; 36 | for band = 1:nband, 37 | idx = find( (w >= f(band*2-1)) & (w <= f(band*2)) ); 38 | idx_band = [idx_band idx]; 39 | end; 40 | wt_band = 10; 41 | wt = ones(length(w),1); 42 | wt(idx_band) = wt_band; 43 | 44 | % Get reference transform 45 | % 46 | t_ref = [0:N-1]; 47 | Wref = exp(-i*kron(w', t_ref)); 48 | Fref = Wref * h(:); 49 | Fref_wt = wt .* Fref; 50 | 51 | if (dbg >= 2), 52 | filt_fig = figure; 53 | end; 54 | 55 | t_off = ([1:ni]-(ni+1)/2)/(ni-1)*2*off; 56 | t_all = zeros(ni, N); 57 | 58 | for idx = [1:ni] 59 | % Get actual sampling positions 60 | % 61 | t_all(idx,:) = t_ref + t_off(idx)* (-1).^[0:N-1]; 62 | t_act = t_all(idx,:); 63 | 64 | if (dbg >= 2) 65 | figure(filt_fig); 66 | subplot(411); 67 | stem([t_ref.' t_act.'], ones(length(t_ref),2)); 68 | legend('Reference', 'Actual'); 69 | title('Sampling Locations'); 70 | end; 71 | 72 | % Get actual, add weights 73 | % 74 | Wact = exp(-i*kron(w', t_act)); 75 | Wact_wt = repmat(wt,1,length(t_act)) .* Wact; 76 | 77 | % Get new filter 78 | % 79 | hi(idx,:) = pinv(Wact_wt)*Fref_wt; 80 | 81 | if ( (dbg >= 2) && (rem(idx,5)==0) ), 82 | plot_db = 0; 83 | figure(filt_fig); 84 | 85 | Fact = Wact * h(:); 86 | Fact_fix = Wact * hi(idx,:).'; 87 | 88 | subplot(4,1,2); 89 | hold off; 90 | plot(abs(h)); 91 | hold on; 92 | plot(abs(hi(idx)), 'r--'); 93 | title('Beta Polynomials'); 94 | 95 | subplot(4,1,3); 96 | if plot_db, 97 | hold off; 98 | plot(w/pi,20*log10(abs(Fref)),'b-'); 99 | hold on; 100 | plot(w/pi, 20*log10(abs(Fact)), 'g--'); 101 | plot(w/pi, 20*log10(abs(Fact_fix)), 'r--'); 102 | ylabel('DB Scale'); 103 | else 104 | hold off; 105 | plot(w/pi,abs(Fref),'b-'); 106 | hold on; 107 | plot(w/pi, abs(Fact), 'g--'); 108 | plot(w/pi, abs(Fact_fix), 'r--'); 109 | ylabel('Linear Scale'); 110 | end; 111 | 112 | title('Magnitude Response'); 113 | 114 | subplot(4,1,4); 115 | hold off; 116 | plot(w/pi,angle(Fref),'b-'); 117 | hold on; 118 | plot(w/pi,angle(Fact),'g--'); 119 | plot(w/pi,angle(Fact_fix),'r--'); 120 | title('Phase Response'); 121 | 122 | fprintf(1,'Offset: %f -- Hit any key to continue\n', t_off(idx)); 123 | pause; 124 | end; 125 | 126 | 127 | end; 128 | 129 | hi = hi(:); 130 | t_all = t_all(:); 131 | 132 | return; 133 | 134 | 135 | 136 | 137 | 138 | -------------------------------------------------------------------------------- /spectral_fact.m: -------------------------------------------------------------------------------- 1 | % h = spectral_fact(r) 2 | % 3 | % Spectral factorization using Kolmogorov 1939 approach 4 | % (code follows pp. 232-233, Signal Analysis, by A. Papoulis) 5 | % 6 | % Computes the minimum-phase impulse response which satisfies 7 | % given auto-correlation. 8 | % 9 | % Input: 10 | % r: auto-correlation coefficients should be passed in 11 | % as a column vector, odd length 12 | % Output 13 | % h: impulse response that gives the desired auto-correlation 14 | 15 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 16 | % 17 | % Spectral-Spatial RF Pulse Design for MRI and MRSI MATLAB Package 18 | % 19 | % Authors: Adam B. Kerr and Peder E. Z. Larson 20 | % 21 | % (c)2007-2011 Board of Trustees, Leland Stanford Junior University and 22 | % The Regents of the University of California. 23 | % All Rights Reserved. 24 | % 25 | % Please see the Copyright_Information and README files included with this 26 | % package. All works derived from this package must be properly cited. 27 | % 28 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 29 | 30 | 31 | function h = spectral_fact(r) 32 | 33 | % length of the impulse response sequence 34 | nr = length(r); 35 | n = (nr+1)/2; 36 | 37 | % over-sampling factor 38 | mult_factor = 30; % should have mult_factor*(n) >> n 39 | m = mult_factor*n; 40 | 41 | % computation method: 42 | % H(exp(jTw)) = alpha(w) + j*phi(w) 43 | % where alpha(w) = 1/2*ln(R(w)) and phi(w) = Hilbert_trans(alpha(w)) 44 | 45 | % compute 1/2*ln(R(w)) 46 | w = 2*pi*[0:m-1]/m; 47 | R = exp( -j*kron(w',[-(n-1):n-1]) )*r; 48 | R = abs(real(R)); % remove numerical noise from the imaginary part 49 | alpha = 1/2*log(R); 50 | 51 | % find the Hilbert transform 52 | alphatmp = fft(alpha); 53 | alphatmp(floor(m/2)+1:m) = -alphatmp(floor(m/2)+1:m); 54 | alphatmp(1) = 0; 55 | alphatmp(floor(m/2)+1) = 0; 56 | phi = real(ifft(j*alphatmp)); 57 | 58 | % now retrieve the original sampling 59 | index = find(rem([0:m-1],mult_factor)==0); 60 | alpha1 = alpha(index); 61 | phi1 = phi(index); 62 | 63 | % compute the impulse response (inverse Fourier transform) 64 | h = ifft(exp(alpha1+j*phi1),n); 65 | -------------------------------------------------------------------------------- /ss_b1verse.m: -------------------------------------------------------------------------------- 1 | function [rfv, gv] = ss_b1verse(g, rf, b1max, gmax, smax, ts, gamma, slew_penalty, dbg); 2 | % [rfv, gv] = ss_b1verse(g, rf, b1max, gmax, smax, ts, gamma, slew_penalty, dbg) 3 | % 4 | % Use VERSE to reduce maximum RF amplitude while retaining an 5 | % identical pulse duration. 6 | % Input RF and gradient must correspond (ie RF should be once VERSE'd if 7 | % gradient is varying). 8 | % A slew rate penalty that reduces the allowable slew rate for larger RF 9 | % amplitudes is also implemented with the slew_penalty variable that 10 | % determines the degree of this penalty. 11 | % 12 | % INPUTS: 13 | % g - gradient in G/cm 14 | % rf - scaled as per rftools (sum(rf) = flip) 15 | % b1max - max RF 16 | % gmax - max gradient, in G/cm 17 | % smax - max slew rate, in G/cm/ms 18 | % ts - sampling interval, in sec 19 | % gamma - gyromagnetic ratio, in Hz/G 20 | % slew_penalty (optional) - degree of slew rate reduction 21 | % default is 0 (no penalty) 22 | % dbg (optional) - indicates debug level 23 | % 24 | % OUTPUTS: 25 | % rfv - versed RF 26 | % gv - new gradient 27 | 28 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 29 | % 30 | % Spectral-Spatial RF Pulse Design for MRI and MRSI MATLAB Package 31 | % 32 | % Authors: Adam B. Kerr and Peder E. Z. Larson 33 | % 34 | % (c)2007-2011 Board of Trustees, Leland Stanford Junior University and 35 | % The Regents of the University of California. 36 | % All Rights Reserved. 37 | % 38 | % Please see the Copyright_Information and README files included with this 39 | % package. All works derived from this package must be properly cited. 40 | % 41 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 42 | 43 | % TODO: 44 | % - allow negative gradients 45 | 46 | if nargin < 8 47 | slew_penalty = 0; 48 | end 49 | 50 | if nargin < 9 51 | dbg = 0; 52 | end 53 | 54 | if length(g) == 1 55 | g = g*ones(size(rf)); 56 | end 57 | 58 | if length(b1max) == 1 59 | b1max = b1max*ones(size(rf)); 60 | end 61 | 62 | dt = ts*ones(size(rf)); 63 | t_unif = ([0:length(rf)-1] + 0.5) * ts; 64 | dt_unif = dt; 65 | 66 | S_rfg = 1 / (2*pi*gamma*ts); 67 | 68 | % convert to Gauss 69 | if dbg >= 2 70 | rfg = rf *S_rfg; 71 | fprintf('Initial Max RF fraction: %f\n', max(abs(rfg) ./ b1max)); 72 | end 73 | 74 | T = sum(dt); 75 | 76 | notdone = 1; 77 | 78 | % maxiter may not be necessary because 79 | % the routine should either fail or succeed at some point 80 | niter = 0; 81 | maxiter = 100; 82 | 83 | %area = sum(g); 84 | 85 | while (niter < maxiter) 86 | niter = niter + 1; 87 | 88 | Imaxed = zeros(size(rf)); 89 | Islewed = zeros(size(rf)); 90 | % fix points with zero gradient 91 | Islewed(find(g==0)) = 1; 92 | % fix endpoints 93 | Islewed([1,end]) = 1; 94 | 95 | if dbg >= 2 96 | figure(99) 97 | subplot(311) 98 | plot(cumsum(dt),g) 99 | subplot(312) 100 | plot(cumsum(dt),abs(rf) / (2*pi*gamma*ts)) 101 | subplot(313) 102 | plot(cumsum(dt),diff(g) ./ ((dt(1:end-1) + dt(2:end))/2)) 103 | pause 104 | end 105 | 106 | b = ones(1,5)/5; 107 | b =firls(8, [0 .03 .06 1], [1 1 0 0]); 108 | rffilt = filtfilt(b, 1, abs(rf)) / (sum(b)^2); 109 | rffilt = max(abs(rf), rffilt); 110 | % rffilt = abs(rf); 111 | filtmax = max( max(abs(rffilt)), max(b1max)/S_rfg); 112 | s_mod = (1 - 0.999*rffilt/filtmax) .^slew_penalty; 113 | % s_mod = (1 - 0.999*abs(rf)/max(abs(rf))) .^slew_penalty; 114 | 115 | % s_mod = (tanh((slew_penalty(1) - rffilt/filtmax) * slew_penalty(2)) + 1 ) / 2; 116 | smax_mod = smax*1e3 * s_mod; 117 | 118 | % Go through gradient forward looking for slew-rate violations 119 | for k = 2:length(rf) 120 | slew = (g(k) - g(k-1)) / (dt(k) + dt(k-1)) / 0.5; 121 | 122 | if 1 123 | smaxk = smax*1e3 * ... 124 | (1 - (abs(rffilt(k)) + abs(rffilt(k-1)))*S_rfg / (b1max(k)+b1max(k-1)) )^slew_penalty; 125 | else 126 | smaxk = smax_mod(k); 127 | end 128 | if (slew > smaxk ) 129 | gh = g(k); gl = g(k-1); 130 | dth = dt(k); dtl = dt(k-1); 131 | 132 | a = smax_mod(k) * dth; 133 | b = smax_mod(k) * dtl + 2*gl; 134 | c = -2*gh; 135 | 136 | scale = (-b + sqrt(b^2-4*a*c))/(2*a); 137 | 138 | rf(k) = rf(k) / scale; 139 | g(k) = g(k) / scale; 140 | dt(k) = dt(k) * scale; 141 | Islewed(k) = 1; 142 | end 143 | 144 | end 145 | 146 | % Go through gradient in reverse looking for slew-rate violations 147 | for k = length(rf)-1:-1:1 148 | slew = (g(k) - g(k+1)) / (dt(k) + dt(k+1)) / 0.5; 149 | 150 | if 1 151 | smaxk = smax*1e3 * ... 152 | (1 - (abs(rffilt(k)) + abs(rffilt(k+1)))*S_rfg / (b1max(k) + b1max(k+1)) )^slew_penalty; 153 | else 154 | smaxk = smax_mod(k); 155 | end 156 | if (slew > smaxk) 157 | gh = g(k); gl = g(k+1); 158 | dth = dt(k); dtl = dt(k+1); 159 | 160 | a = smax_mod(k) * dth; 161 | b = smax_mod(k) * dtl + 2*gl; 162 | c = -2*gh; 163 | 164 | scale = (-b + sqrt(b^2-4*a*c))/(2*a); 165 | 166 | rf(k) = rf(k) / scale; 167 | g(k) = g(k) / scale; 168 | dt(k) = dt(k) * scale; 169 | Islewed(k) = 1; 170 | end 171 | 172 | end 173 | 174 | 175 | % Check for violoations of max RF 176 | for k = 1:length(rf) 177 | rfscale = abs(rf(k)) * S_rfg / (b1max(k)*.999); 178 | 179 | if (rfscale > 1) 180 | rf(k) = rf(k) / rfscale; 181 | g(k) = g(k) / rfscale; 182 | dt(k) = dt(k) * rfscale; 183 | 184 | Imaxed(k) = 1; 185 | end 186 | end 187 | 188 | 189 | % compensate for time expansion in removing max RF and slew 190 | % rate violations by shrinking other time samples and increasing 191 | % RF and gradient correspondingly 192 | Ifixed = find(Imaxed | Islewed); 193 | Ishrink = find(~(Imaxed | Islewed)); 194 | shrink_scale = (T - sum(dt(Ifixed))) / sum(dt(Ishrink)); 195 | 196 | % Check if pulse exceeds length requirement 197 | if shrink_scale < 0 198 | % error('ss_verse: RF pulse unrealizeable'); 199 | rfv = []; 200 | gv = []; 201 | return; 202 | elseif (abs(shrink_scale-1) < 1e-14) || (isempty(Imaxed) && isempty(Islewed)) 203 | % DONE! 204 | notdone = 0; 205 | else 206 | rf(Ishrink) = rf(Ishrink) / shrink_scale; 207 | g(Ishrink) = g(Ishrink) / shrink_scale ; 208 | dt(Ishrink) = dt(Ishrink) * shrink_scale; 209 | 210 | % timepoints at center of little hard pulses 211 | t = cumsum([0, dt(1:end-1)]) + dt/2; 212 | 213 | rf = interp1(t, rf, t_unif, 'spline', 'extrap'); 214 | g = interp1(t, g, t_unif, 'spline', 'extrap'); 215 | % g = g/sum(g) * area; 216 | dt = dt_unif; 217 | end 218 | 219 | niter = niter + 1; 220 | 221 | end 222 | 223 | % check if gradient limits exceeded 224 | gscale = abs(g) / gmax; 225 | if max(gscale) > 1 226 | %error('ss_verse: RF pulse unrealizeable'); 227 | rfv = []; 228 | gv = []; 229 | return; 230 | end 231 | 232 | rfv = rf; 233 | gv = g; 234 | 235 | if dbg >= 2 236 | rfvg = rfv*S_rfg; 237 | fprintf('Final Max RF fraction: %f\n', max(abs(rfvg) ./ b1max)); 238 | end 239 | -------------------------------------------------------------------------------- /ss_band_plot.m: -------------------------------------------------------------------------------- 1 | function ss_band_plot(f, a, d, fo, fs, f1, f2, sym) 2 | % SS_BAND_PLOT - Plot filter specification 3 | % 4 | % Inputs: 5 | % f - frequency band edges in normalized frequency [-1..1] 6 | % a - amplitude of bands 7 | % d - ripple weighting 8 | % fo - offset absolute frequency 9 | % fs - sampling frequency 10 | % f1, f2 - upper lower absolute frequencies to plot 11 | % sym - flag whether response is symmetric 12 | % 13 | 14 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 15 | % 16 | % Spectral-Spatial RF Pulse Design for MRI and MRSI MATLAB Package 17 | % 18 | % Authors: Adam B. Kerr and Peder E. Z. Larson 19 | % 20 | % (c)2007-2011 Board of Trustees, Leland Stanford Junior University and 21 | % The Regents of the University of California. 22 | % All Rights Reserved. 23 | % 24 | % Please see the Copyright_Information and README files included with this 25 | % package. All works derived from this package must be properly cited. 26 | % 27 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 28 | % 29 | % $Header: /home/adam/cvsroot/src/ss/ss_band_plot.m,v 1.6 2013/08/15 03:34:50 adam Exp $ 30 | % 31 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 32 | 33 | % Exit if frequency vector is empty 34 | % 35 | if isempty(f), 36 | fprintf(1,'No frequency bands to plot\n'); 37 | return; 38 | end; 39 | 40 | if sym, 41 | fmod = f; 42 | f = [-fmod(end:-1:1) fmod]; 43 | a = [a(end:-1:1) a]; 44 | d = [d(end:-1:1) d]; 45 | end; 46 | 47 | % Plot unaliased frequency bands 48 | % 49 | nf = length(f)/2; 50 | figure; 51 | subplot(211); 52 | hold on; 53 | for idx = 1:nf, 54 | plot(f((idx*2-1):(idx*2))*fs/2+fo, [a(idx) a(idx)]); 55 | plot(f((idx*2-1):(idx*2))*fs/2+fo, [a(idx) a(idx)] + d(idx), '--'); 56 | plot(f((idx*2-1):(idx*2))*fs/2+fo, [a(idx) a(idx)] - d(idx), ... 57 | '--'); 58 | end; 59 | grid; 60 | title('Unaliased Filter Specification'); 61 | xlabel('Frequency'); 62 | ylabel('Amplitude Response'); 63 | 64 | % Plot absolute frequency response 65 | % 66 | subplot(212); 67 | hold on; 68 | 69 | % Find what repeating responses need to be considered 70 | % 71 | low_mult = ceil(((fo-f1)-fs/2) / fs); 72 | hi_mult = ceil(((f2-fo)-fs/2) / fs); 73 | for mult = -low_mult:hi_mult, 74 | foff = fo + fs*mult; 75 | for idx = 1:nf, 76 | % Get repeating band edge 77 | % 78 | f_l = (f(idx*2-1) * fs/2) + foff; 79 | f_u = (f(idx*2) * fs/2) + foff; 80 | 81 | % Plot if in bounds 82 | % 83 | f_l = min(max(f_l, f1), f2); 84 | f_u = min(max(f_u, f1), f2); 85 | if (f_l ~= f_u), 86 | plot([f_l f_u], [a(idx) a(idx)]); 87 | plot([f_l f_u], [a(idx) a(idx)] + d(idx), '--'); 88 | plot([f_l f_u], [a(idx) a(idx)] - d(idx), '--'); 89 | end; 90 | end; 91 | end; 92 | grid; 93 | title(sprintf('Filter Specification - Fs: %6.1f Hz', fs)); 94 | xlabel('Frequency'); 95 | ylabel('Amplitude Response'); 96 | 97 | 98 | 99 | 100 | -------------------------------------------------------------------------------- /ss_band_plot_phs.m: -------------------------------------------------------------------------------- 1 | function ss_band_plot_phs(f, a, d, fo, fs, f1, f2, sym) 2 | % SS_BAND_PLOT - Plot filter specification 3 | % 4 | % Inputs: 5 | % f - frequency band edges in normalized frequency [-1..1] 6 | % a - amplitude of bands 7 | % d - ripple weighting 8 | % fo - offset absolute frequency 9 | % fs - sampling frequency 10 | % f1, f2 - upper lower absolute frequencies to plot 11 | % sym - flag whether response is symmetric 12 | % 13 | 14 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 15 | % 16 | % Spectral-Spatial RF Pulse Design for MRI and MRSI MATLAB Package 17 | % 18 | % Authors: Adam B. Kerr and Peder E. Z. Larson 19 | % 20 | % (c)2007-2011 Board of Trustees, Leland Stanford Junior University and 21 | % The Regents of the University of California. 22 | % All Rights Reserved. 23 | % 24 | % Please see the Copyright_Information and README files included with this 25 | % package. All works derived from this package must be properly cited. 26 | % 27 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 28 | % 29 | % $Header: /home/adam/cvsroot/src/ss/ss_band_plot_phs.m,v 1.2 2013/08/15 03:34:50 adam Exp $ 30 | % 31 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 32 | 33 | % Exit if frequency vector is empty 34 | % 35 | if isempty(f), 36 | fprintf(1,'No frequency bands to plot\n'); 37 | return; 38 | end; 39 | 40 | if sym, 41 | fmod = f; 42 | f = [-fmod(end:-1:1) fmod]; 43 | a = [a(end:-1:1) a]; 44 | d = [d(end:-1:1) d]; 45 | end; 46 | 47 | % Plot unaliased frequency bands 48 | % 49 | nf = length(f)/2; 50 | figure; 51 | subplot(211); 52 | hold on; 53 | for idx = 1:nf, 54 | plot(f((idx*2-1):(idx*2))*fs/2+fo, abs([a(idx) a(idx)])); 55 | plot(f((idx*2-1):(idx*2))*fs/2+fo, abs([a(idx) a(idx)]) + abs(d(idx)), '--'); 56 | plot(f((idx*2-1):(idx*2))*fs/2+fo, abs([a(idx) a(idx)]) - abs(d(idx)), ... 57 | '--'); 58 | end; 59 | grid; 60 | title('Unaliased Filter Specification'); 61 | xlabel('Frequency'); 62 | ylabel('Amplitude Response'); 63 | 64 | % Plot absolute frequency response 65 | % 66 | subplot(212); 67 | hold on; 68 | 69 | % Find what repeating responses need to be considered 70 | % 71 | low_mult = ceil(((fo-f1)-fs/2) / fs); 72 | hi_mult = ceil(((f2-fo)-fs/2) / fs); 73 | for mult = -low_mult:hi_mult, 74 | foff = fo + fs*mult; 75 | for idx = 1:nf, 76 | % Get repeating band edge 77 | % 78 | f_l = (f(idx*2-1) * fs/2) + foff; 79 | f_u = (f(idx*2) * fs/2) + foff; 80 | 81 | % Plot if in bounds 82 | % 83 | f_l = min(max(f_l, f1), f2); 84 | f_u = min(max(f_u, f1), f2); 85 | if (f_l ~= f_u), 86 | plot([f_l f_u], abs([a(idx) a(idx)])); 87 | plot([f_l f_u], abs([a(idx) a(idx)]) + abs(d(idx)), '--'); 88 | plot([f_l f_u], abs([a(idx) a(idx)]) - abs(d(idx)), '--'); 89 | end; 90 | end; 91 | end; 92 | grid; 93 | title(sprintf('Filter Specification - Fs: %6.1f Hz', fs)); 94 | xlabel('Frequency'); 95 | ylabel('Amplitude Response'); 96 | 97 | 98 | 99 | 100 | -------------------------------------------------------------------------------- /ss_globals.m: -------------------------------------------------------------------------------- 1 | % 2 | % 3 | % SS_GLOBALS 4 | % 5 | % Common global definitions for SS package 6 | % 7 | % Parameters 8 | % NAME DESCRIPTION DEFAULT 9 | % ---------- ------------------------------------------------------- --------- 10 | % 'Nucleus' Nuclei of interest, possible values include 'Hydrogen' 11 | % 'Hydrogen', 'Lithium', 'Carbon', 'Sodium', 'Phosphorous' 12 | % 'Max Grad' Maximum Gradient Strength (G/cm) 5 13 | % 'Max Slew' Maximum Slew Rate (G/cm/ms) 20 14 | % 'Sample Time' Sampling Time (s) 4e-6 15 | % 'Max B1' Maximum B1 amplitude (G) 0.2 16 | % 'Max Duration' Maximum Pulse Duration (s) 20e-3 17 | % 'Num Lobe Iters' Spectral Sampling Frequency Iterations 10 18 | % 'Equal Lobes' Force Equal positive and negative gradient lobes 0 19 | % 'Verse Fraction' Fraction of ramps to use with VERSE 0.8 20 | % 'Num Fs Test' Spectral aliasing frequencys to test 100 21 | % 'Spect Correct' Spectral Correction with actual sampling 0 22 | % 'Spect Correct Reg' Regularization factor for spectral correction inversion 0 23 | % 'SLR' Shinnar-Le Roux Correction for large tip pulses 0 24 | % 'Min Order' Find minimum order FIR filter 1 25 | % 'B1 Verse' Apply B1-VERSE'ing for reduced peak power 0 26 | % 'Slew Penalty' In B1-VERSE, use slew-rate penalty to reduce delay sensitivity 0 27 | % 28 | 29 | 30 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 31 | % 32 | % Spectral-Spatial RF Pulse Design for MRI and MRSI MATLAB Package 33 | % 34 | % Authors: Adam B. Kerr and Peder E. Z. Larson 35 | % 36 | % (c)2007-2011 Board of Trustees, Leland Stanford Junior University and 37 | % The Regents of the University of California. 38 | % All Rights Reserved. 39 | % 40 | % Please see the Copyright_Information and README files included with this 41 | % package. All works derived from this package must be properly cited. 42 | % 43 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 44 | % 45 | % $Header: /home/adam/cvsroot/src/ss/ss_globals.m,v 1.14 2014/05/22 20:43:59 peder Exp $ 46 | % 47 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 48 | 49 | 50 | % Gamma definitions -- all in Hz/G 51 | % 52 | SS_GAMMA_HYDROGEN = 4257.6; 53 | SS_GAMMA_LITHIUM = 1654.6; 54 | SS_GAMMA_CARBON = 1070.5; 55 | SS_GAMMA_SODIUM = 1126.2; 56 | SS_GAMMA_PHOSPHOROUS = 1723.5; 57 | 58 | % Declare globals 59 | % 60 | global SS_INIT_DONE; 61 | 62 | % Nucleus info 63 | % 64 | global SS_NUCLEUS SS_GAMMA; 65 | 66 | % Gradient/timing parameters 67 | % 68 | global SS_MXG SS_MXS SS_TS; 69 | 70 | % RF parameters 71 | % 72 | global SS_MAX_B1 SS_MAX_DURATION; 73 | 74 | % Design tolerances, parameters 75 | % 76 | global SS_NUM_LOBE_ITERS SS_EQUAL_LOBES SS_VERSE_FRAC SS_NUM_FS_TEST; 77 | global SS_SPECT_CORRECT_FLAG SS_SPECT_CORRECT_REGULARIZATION SS_SLR_FLAG; 78 | global SS_VERSE_B1 SS_SLEW_PENALTY SS_MIN_ORDER; 79 | 80 | % Define globals if not already defined 81 | % 82 | if isempty(SS_INIT_DONE), 83 | % Indicate init has been done 84 | % 85 | SS_INIT_DONE = 1; 86 | 87 | % Nucleus info 88 | % 89 | SS_NUCLEUS = 'Hydrogen'; 90 | SS_GAMMA = SS_GAMMA_HYDROGEN; 91 | 92 | % Gradient/timing parameters 93 | % 94 | SS_MXG = 5.0; % G/cm 95 | SS_MXS = 20; % G/cm/ms 96 | SS_TS = 4e-6; % Sampling time (s) 97 | 98 | % RF parameters 99 | % 100 | SS_MAX_B1 = 0.2; % Gauss 101 | SS_MAX_DURATION = 20e-3; % Max allowed duration 102 | 103 | 104 | % Design tolerances, parameters 105 | % 106 | SS_NUM_LOBE_ITERS = 10; 107 | SS_EQUAL_LOBES = 0; 108 | SS_VERSE_FRAC = 0.8; 109 | SS_NUM_FS_TEST = 100; 110 | SS_SPECT_CORRECT_FLAG = 0; 111 | SS_SPECT_CORRECT_REGULARIZATION = 0; 112 | SS_SLR_FLAG = 0; 113 | SS_MIN_ORDER = 1; 114 | SS_VERSE_B1 = 0; 115 | SS_SLEW_PENALTY = 0; 116 | end; 117 | -------------------------------------------------------------------------------- /ss_opt.m: -------------------------------------------------------------------------------- 1 | function options = ss_opt(new_options) 2 | % SS_OPT - Set options for spectral-spatial design 3 | % 4 | % function options = ss_opt(new_options) 5 | % 6 | % options - cell array of current options 7 | % new_options - cell array of new options to set 8 | % 9 | % Pass new_options as [] to reset options 10 | % 11 | % See help ss_globals for options 12 | 13 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 14 | % 15 | % Spectral-Spatial RF Pulse Design for MRI and MRSI MATLAB Package 16 | % 17 | % Authors: Adam B. Kerr and Peder E. Z. Larson 18 | % 19 | % (c)2007-2011 Board of Trustees, Leland Stanford Junior University and 20 | % The Regents of the University of California. 21 | % All Rights Reserved. 22 | % 23 | % Please see the Copyright_Information and README files included with this 24 | % package. All works derived from this package must be properly cited. 25 | % 26 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 27 | % 28 | % $Header: /home/adam/cvsroot/src/ss/ss_opt.m,v 1.14 2013/08/15 14:41:09 adam Exp $ 29 | % 30 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 31 | 32 | ss_globals; 33 | 34 | if (nargin < 1) 35 | error('Usage: options = ss_opt(new_options)'); 36 | end; 37 | 38 | if isempty(new_options) 39 | SS_INIT_DONE = []; 40 | ss_globals; 41 | end; 42 | 43 | new_options = new_options.'; 44 | new_options = new_options(:); 45 | if (mod(length(new_options), 2) == 1), 46 | error('Input parameter (new_options) must be of even length'); 47 | end; 48 | 49 | nopt = length(new_options)/2; 50 | for idx = 1:nopt, 51 | opt = new_options{idx*2-1}; 52 | optparm = new_options{idx*2}; 53 | switch opt, 54 | 55 | case 'Nucleus' % Set nucleus and gamma 56 | switch optparm, 57 | case 'Hydrogen' 58 | SS_NUCLEUS = optparm; 59 | SS_GAMMA = SS_GAMMA_HYDROGEN; 60 | case 'Lithium' 61 | SS_NUCLEUS = optparm;; 62 | SS_GAMMA = SS_GAMMA_LITHIUM; 63 | case 'Carbon' 64 | SS_NUCLEUS = optparm; 65 | SS_GAMMA = SS_GAMMA_CARBON; 66 | case 'Sodium' 67 | SS_NUCLEUS = optparm; 68 | SS_GAMMA = SS_GAMMA_SODIUM; 69 | case 'Phosphorous' 70 | SS_NUCLEUS = optparm; 71 | SS_GAMMA = SS_GAMMA_PHOSPHOROUS; 72 | otherwise 73 | error(sprintf('Nucleus: %s not recognized', optparm)); 74 | end; 75 | 76 | case 'Max Grad', 77 | SS_MXG = optparm; 78 | 79 | case 'Max Slew', 80 | SS_MXS = optparm; 81 | 82 | case 'Max B1', 83 | SS_MAX_B1 = optparm; 84 | 85 | case 'Max Duration', 86 | SS_MAX_DURATION = optparm; 87 | 88 | case 'Num Lobe Iters', 89 | SS_NUM_LOBE_ITERS = optparm; 90 | 91 | case 'Equal Lobes', 92 | SS_EQUAL_LOBES = optparm; 93 | 94 | case 'Verse Fraction', 95 | SS_VERSE_FRAC = optparm; 96 | 97 | case 'Num Fs Test', 98 | SS_NUM_FS_TEST = optparm; 99 | 100 | case 'Spect Correct', 101 | SS_SPECT_CORRECT_FLAG = optparm; 102 | 103 | case 'Spect Correct Reg', 104 | SS_SPECT_CORRECT_REGULARIZATION = optparm; 105 | 106 | case 'SLR', 107 | SS_SLR_FLAG = optparm; 108 | 109 | case 'Min Order', 110 | SS_MIN_ORDER = optparm; 111 | 112 | case 'B1 Verse', 113 | SS_VERSE_B1 = optparm; 114 | 115 | case 'Slew Penalty', 116 | SS_SLEW_PENALTY = optparm; 117 | 118 | otherwise 119 | SS_INIT_DONE = []; 120 | ss_globals; 121 | error(sprintf('Option: %s not recognized',opt)); 122 | end; 123 | end; 124 | 125 | % Fill in options to pass back 126 | % 127 | idx = 1; 128 | 129 | options{idx, 1} = 'Nucleus'; 130 | options{idx, 2} = SS_NUCLEUS; 131 | idx = idx+1; 132 | 133 | options{idx, 1} = 'Max Grad'; 134 | options{idx, 2} = SS_MXG; 135 | idx = idx+1; 136 | 137 | options{idx, 1} = 'Max Slew'; 138 | options{idx, 2} = SS_MXS; 139 | idx = idx+1; 140 | 141 | options{idx, 1} = 'Sample Time'; 142 | options{idx, 2} = SS_TS; 143 | idx = idx+1; 144 | 145 | options{idx, 1} = 'Max B1'; 146 | options{idx, 2} = SS_MAX_B1; 147 | idx = idx+1; 148 | 149 | options{idx, 1} = 'Max Duration'; 150 | options{idx, 2} = SS_MAX_DURATION; 151 | idx = idx+1; 152 | 153 | options{idx, 1} = 'Num Lobe Iters'; 154 | options{idx, 2} = SS_NUM_LOBE_ITERS; 155 | idx = idx+1; 156 | 157 | options{idx, 1} = 'Equal Lobes'; 158 | options{idx, 2} = SS_EQUAL_LOBES; 159 | idx = idx+1; 160 | 161 | options{idx, 1} = 'Verse Fraction'; 162 | options{idx, 2} = SS_VERSE_FRAC; 163 | idx = idx+1; 164 | 165 | options{idx, 1} = 'Num Fs Test'; 166 | options{idx, 2} = SS_NUM_FS_TEST; 167 | idx = idx+1; 168 | 169 | options{idx, 1} = 'Spect Correct'; 170 | options{idx, 2} = SS_SPECT_CORRECT_FLAG; 171 | idx = idx+1; 172 | 173 | options{idx, 1} = 'Spect Correct Reg'; 174 | options{idx, 2} = SS_SPECT_CORRECT_REGULARIZATION; 175 | idx = idx+1; 176 | 177 | options{idx, 1} = 'SLR'; 178 | options{idx, 2} = SS_SLR_FLAG; 179 | idx = idx+1; 180 | 181 | options{idx, 1} = 'Min Order'; 182 | options{idx, 2} = SS_MIN_ORDER; 183 | idx = idx+1; 184 | 185 | options{idx, 1} = 'B1 Verse'; 186 | options{idx, 2} = SS_VERSE_B1; 187 | idx = idx+1; 188 | 189 | options{idx, 1} = 'Slew Penalty'; 190 | options{idx, 2} = SS_SLEW_PENALTY; 191 | idx = idx+1; 192 | 193 | 194 | -------------------------------------------------------------------------------- /ss_plot.m: -------------------------------------------------------------------------------- 1 | function [f,z,m] = ss_plot(g, rf, samp, ptype, fov, bw, gamma, fplot, isodelay) 2 | % SS_PLOT - Plot performance of spectral-spatial 3 | % 4 | % function [f,z,m] = ss_plot(g, rf, samp, [ptype], [fov], [bw], [gamma], 5 | % [fplot], [isodelay]) 6 | % 7 | % INPUTS 8 | % g - gradient in G/cm 9 | % rf - RF in G 10 | % samp - sample period in s 11 | % [ptype] - pulse type: 'ex', 'se', 'sat', 'inv' 12 | % [fov] - Spatial fov in cm to plot 13 | % [bw] - Spectral bw in Hz to plot 14 | % [gamma] - Gamma to be used, Default:4257 15 | % [fplot] - frequencies to plot spatial profiles 16 | % [isodelay] - Unwind spectral phase shift for given isodelay - default: 0 17 | % 18 | % OUTPUTS 19 | % f - frequency points plotted (Hz) 20 | % z - spatial points plotted (cm) 21 | % m - simulated magnetization 22 | % 23 | % If optional parameters [] are set to empty matrices, 24 | % it attempts to estimate reasonable values 25 | % 26 | 27 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 28 | % 29 | % Spectral-Spatial RF Pulse Design for MRI and MRSI MATLAB Package 30 | % 31 | % Authors: Adam B. Kerr and Peder E. Z. Larson 32 | % 33 | % (c)2007-2011 Board of Trustees, Leland Stanford Junior University and 34 | % The Regents of the University of California. 35 | % All Rights Reserved. 36 | % 37 | % Please see the Copyright_Information and README files included with this 38 | % package. All works derived from this package must be properly cited. 39 | % 40 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 41 | 42 | 43 | % Some defines 44 | % 45 | NPLOT = 100; % Number plot points 46 | 47 | if nargin < 3, 48 | error(['Usage: ss_plot(g, rf, samp, [ptype], [fov], [bw], [gamma],' ... 49 | ' [fp], [fs])']); 50 | end; 51 | 52 | if ~isreal(g), 53 | error('Only works with 1-D gradient'); 54 | end; 55 | 56 | if length(g) ~= length(rf), 57 | error('RF and gradient must be same length'); 58 | end; 59 | g = g(:); 60 | rf = rf(:); 61 | 62 | % Check ptype 63 | if ( (nargin < 4) || isempty(ptype) ), 64 | ptype = 'ex'; 65 | else 66 | switch ptype, 67 | case {'ex', 'se', 'sat', 'inv'} 68 | otherwise 69 | error(sprintf(['SS_PLOT: pulse type (ptype) of: %s not' ... 70 | ' recognized'], ptype)); 71 | end; 72 | end 73 | 74 | if ( (nargin < 5) || isempty(fov)) 75 | fov = 4.0; 76 | end 77 | 78 | if ( (nargin < 6) || isempty(bw) ), 79 | % Take Fourier transform of gradient and find peak 80 | % component 81 | g_fft = fft(g); 82 | res = length(g); 83 | res_d2 = fix(res/2); 84 | [mxval, idx] = max(abs(g_fft(1:res_d2))); 85 | freq_peak = 1/samp * idx/length(g); 86 | 87 | % Plot will be from -bw/2:bw/2 88 | % 89 | bwidth = round(4 * freq_peak / 100) * 100; 90 | bw = [-bwidth/2 bwidth/2]; 91 | elseif length(bw) == 1 92 | bw = [-bw/2 bw/2]; 93 | end 94 | 95 | if ( (nargin < 5) || isempty(fov) ), 96 | % Estimate FOV to plot from k-space swing 97 | % 98 | kz = cumsum(gamma * g * samp); 99 | kz_peak = (max(kz) - min(kz))/2; 100 | fov = ceil(16 / kz_peak); 101 | end; 102 | 103 | if ( (nargin < 7) || isempty(gamma) ), 104 | gamma = 4257; 105 | end; 106 | 107 | if ( (nargin < 9) || isempty(isodelay) ), 108 | isodelay = 0; 109 | end; 110 | 111 | 112 | 113 | 114 | % Get x and y vectors for abr 115 | % 116 | dbw = diff(bw) / (NPLOT-1); 117 | f = [bw(1):dbw:bw(2)]; 118 | 119 | dfov = fov / (NPLOT-1); 120 | z = [-fov/2:dfov:fov/2]; 121 | 122 | % Convert RF to a rotation in radians 123 | % 124 | rf_rot = 2 * pi * gamma * rf(:) * samp; 125 | 126 | % Build gradient that gives rotation 127 | % in radians when scaled by "f" (off-resonance in Hz) 128 | % 129 | gf_rot = 2 * pi * samp * ones(size(g(:))); 130 | 131 | % Convert gradient to a rotation in radians 132 | % when scaled by "z" 133 | % 134 | gz_rot = 2 * pi * gamma * g(:) * samp; 135 | 136 | % Add single samples at end of all waveforms to account for isodelay correction 137 | % 138 | rf_rot = [rf_rot;0]; 139 | gf_rot = [gf_rot;(-2 * pi * isodelay)]; 140 | gz_rot = [gz_rot;0]; 141 | 142 | % Get Mxy now 143 | % 144 | m = calc_mag(rf_rot, gz_rot+i*gf_rot, z, f, ptype); 145 | 146 | % Make plots now 147 | % 148 | figure; 149 | 150 | % RF 151 | % 152 | t = [0:length(g)-1] * samp * 1e3; 153 | subplot(411); 154 | plot(t,abs(rf), 'r-'); 155 | hold on; 156 | plot(t,real(rf),'b--'); 157 | hold on; 158 | plot(t,imag(rf), 'g--'); 159 | title('RF Envelope - I/Q'); 160 | ylabel('(Gauss)'); 161 | xlabel('Time [ms]'); 162 | 163 | % Gradient 164 | % 165 | subplot(412); 166 | plot(t,g,'b-'); 167 | title('Excitation Gradient'); 168 | ylabel('[G/cm]'); 169 | xlabel('Time [ms]'); 170 | 171 | switch ptype 172 | case {'ex', 'se'} 173 | % abs(Mxy) 174 | % 175 | subplot(4,3,7); 176 | imagesc(f,z,abs(m)); 177 | colormap(gray) 178 | xlabel('Frequency [Hz]'); 179 | ylabel('Position [cm]'); 180 | title('Magnitude M_{xy}') 181 | 182 | % Angle(Mxy) 183 | % 184 | subplot(4,3,8); 185 | imagesc(f,z,angle(m)); 186 | xlabel('Frequency [Hz]'); 187 | ylabel('Position [cm]'); 188 | title('Phase M_{xy}'); 189 | case {'inv', 'sat'} 190 | % Mz 191 | subplot(4,3,[7,8]); 192 | imagesc(f,z,m); 193 | colormap(gray) 194 | xlabel('Frequency [Hz]'); 195 | ylabel('Position [cm]'); 196 | title('M_{z}') 197 | end 198 | 199 | % Spectral plot at z = 0 200 | % 201 | subplot(4,3,9); 202 | dbw_fine = diff(bw)/499; 203 | f_fine = [bw(1):dbw_fine:bw(2)]; 204 | m_center = calc_mag(rf_rot,gz_rot+i*gf_rot, 0, f_fine,ptype); 205 | plot_mag(f_fine, m_center, ptype); 206 | xlabel('Frequency [Hz]'); 207 | title(sprintf('Spectral Profile - Z = 0')); 208 | 209 | 210 | % Passband/Stopband plots 211 | % 212 | if (nargin < 8) || isempty(f), 213 | % Sort frequency from 0 out 214 | [tmp idx] = sort(abs(f)); 215 | fsort = f(idx); 216 | 217 | % Get spectral profile 218 | m_z0 = calc_mag(rf_rot,gz_rot+i*gf_rot, 0, fsort,ptype); 219 | 220 | % Find maximum closest to 0 frequency 221 | [mval idx] = max(abs(m_z0)); 222 | fplot = fsort(idx); 223 | end; 224 | 225 | nplot = length(fplot); 226 | for idx = 1:nplot, 227 | subplot(4,nplot,3*nplot + idx); 228 | m_f = calc_mag(rf_rot,gz_rot+i*gf_rot, z, fplot(idx),ptype); 229 | plot_mag(z,m_f,ptype); 230 | title(sprintf('%5.1f', fplot(idx))); 231 | xlabel('Position [cm]'); 232 | end; 233 | 234 | end 235 | 236 | % Helper functions to avoid lots of "switch" statements 237 | 238 | function M = calc_mag(rfs, gs, z, f, ptype) 239 | 240 | [a,b] = abr(rfs,gs, z, f); 241 | switch ptype 242 | case 'ex' 243 | M = ab2ex(a,b); 244 | case 'inv' 245 | M = ab2inv(a,b); 246 | case 'sat' 247 | M = ab2sat(a,b); 248 | case 'se' 249 | M = ab2se(a, b); 250 | end 251 | 252 | end 253 | 254 | function plot_mag(x, M, ptype) 255 | 256 | switch ptype 257 | case {'ex', 'se'} 258 | plot(x,abs(M), 'r-',x,real(M),'b--', x,imag(M), 'g--'); 259 | grid; 260 | ylabel('M_{xy}'); 261 | case {'inv', 'sat'} 262 | plot(x,M, 'b-'); 263 | grid; 264 | ylabel('M_{z}'); 265 | end 266 | 267 | end -------------------------------------------------------------------------------- /ss_response_mxy.m: -------------------------------------------------------------------------------- 1 | function [mxy mz] = ss_response_mxy(g,rf,z,f,ts,gamma, isodelay) 2 | % SS_RESPONSE_MXY - Get Mxy response at given z, f position/frequency 3 | % 4 | % [mxy mz] = ss_response_mxy(g,rf,z,f,ts,gamma) 5 | % 6 | % Input 7 | % g - gradient, G/cm 8 | % rf - rf G 9 | % z - cm 10 | % f - frequency 11 | % ts - sampling time (s) 12 | % gamma - gyromagnetic ratio 13 | % [isodelay] - Unwind spectral phase shift for given isodelay - default: 0 14 | 15 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 16 | % 17 | % Spectral-Spatial RF Pulse Design for MRI and MRSI MATLAB Package 18 | % 19 | % Authors: Adam B. Kerr and Peder E. Z. Larson 20 | % 21 | % (c)2007-2011 Board of Trustees, Leland Stanford Junior University and 22 | % The Regents of the University of California. 23 | % All Rights Reserved. 24 | % 25 | % Please see the Copyright_Information and README files included with this 26 | % package. All works derived from this package must be properly cited. 27 | % 28 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 29 | % 30 | % $Header: /home/adam/cvsroot/src/ss/ss_response_mxy.m,v 1.6 2013/08/15 03:34:50 adam Exp $ 31 | % 32 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 33 | 34 | if ( (nargin < 7) || isempty(isodelay) ), 35 | isodelay = 0; 36 | end; 37 | 38 | % Convert RF to a rotation in radians 39 | % 40 | rf_rot = 2 * pi * gamma * rf(:) * ts; 41 | 42 | % Build gradient that gives rotation 43 | % in radians when scaled by "f" (off-resonance in Hz) 44 | % 45 | gf_rot = 2 * pi * ts * ones(size(g(:))); 46 | 47 | % Convert gradient to a rotation in radians 48 | % when scaled by "z" 49 | % 50 | gz_rot = 2 * pi * gamma * g(:) * ts; 51 | 52 | % Add single samples at end of all waveforms to account for isodelay correction 53 | % 54 | rf_rot = [rf_rot;0]; 55 | gf_rot = [gf_rot;(-2 * pi * isodelay)]; 56 | gz_rot = [gz_rot;0]; 57 | 58 | 59 | % Get Mxy now 60 | % 61 | [a b] = abr(rf_rot, gz_rot+i*gf_rot, z, f); 62 | mxy = ab2ex(a, b); 63 | mz = ab2sat(a, b); 64 | 65 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /ss_shift.m: -------------------------------------------------------------------------------- 1 | function rf_shift = ... 2 | ss_shift(g, rf, z_shift, f_shift); 3 | 4 | 5 | % SS_SHIFT - frequency and/or spatial shift of spectral-spatial pulse 6 | % 7 | % [g, rf, fs_best, z_plot, f_plot, m_plot] = ... 8 | % ss_design(z_thk, z_tb, z_de, f, a_angs, de,... 9 | % ptype, z_ftype, s_ftype, ss_type, ... 10 | % f_off, dbg, no_plot) 11 | % 12 | % INPUTS 13 | % g - gradient (G/cm) 14 | % rf - RF (G) 15 | % z_shift - slice shift [cm] 16 | % f_shift - frequency shift [Hz] 17 | % 18 | % OUTPUTS 19 | % rf_shift - updated RF (G) 20 | 21 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 22 | % 23 | % Spectral-Spatial RF Pulse Design for MRI and MRSI MATLAB Package 24 | % 25 | % Authors: Peder E. Z. Larson 26 | % 27 | % (c)2024 The Regents of the University of California. 28 | % All Rights Reserved. 29 | % 30 | % Please see the Copyright_Information and README files included with this 31 | % package. All works derived from this package must be properly cited. 32 | % 33 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 34 | 35 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 36 | % Check all inputs 37 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 38 | 39 | 40 | if (nargin < 3) 41 | error(['Usage: ss_shift(z_thk, z_tb, z_d, f, a_angs, d,' ... 42 | 'ptype, z_fttype, s_ftype, ss_type, foff)']); 43 | end 44 | 45 | 46 | 47 | % Check x_shift 48 | % 49 | if isempty(z_shift) 50 | z_shift = 0; 51 | end 52 | 53 | % Check f_shift 54 | % 55 | if (nargin < 4) || isempty(f_shift) 56 | f_shift = 0; 57 | end 58 | 59 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 60 | % Initialize globals 61 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 62 | 63 | ss_globals; 64 | 65 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 66 | % frequency shift 67 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 68 | 69 | rf_shift = rf .* exp(-1i*2*pi*[0:length(rf)-1]*SS_TS*f_shift); 70 | 71 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 72 | % spatial shift 73 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 74 | 75 | t = SS_TS * [1:length(rf)]; 76 | f_g = SS_GAMMA* g *z_shift; 77 | %rf_shift = rf_shift .* exp(-1i*2*pi* [f_g(2:end),0].*t); 78 | 79 | k_z = SS_GAMMA* (cumsum(g) - sum(g))*SS_TS; 80 | rf_shift = rf_shift .* exp(-1i*2*pi* k_z*z_shift); 81 | 82 | 83 | -------------------------------------------------------------------------------- /ss_spect_correct.m: -------------------------------------------------------------------------------- 1 | function rfm = ss_spect_correct(b, bsf, Nper, Noff, f, ptype, ss_type, slr, ... 2 | reg_factor, dbg) 3 | % SS_SPECT_CORRECT - Correct spectral filter for irregular sampling 4 | % 5 | % function rf = ss_spect_correct(b, bsf, Nper, Noff, f, ptype, ss_type, slr, reg_factor, dbg) 6 | % 7 | % Inputs: 8 | % b - spectral filter design, normalized so that passband has value of 1 9 | % bsf - beta polynomial scale factors (normally sin(ang/2)) 10 | % Nper - period in samples between taps of b 11 | % Noff - vector of sample offsets from reference point (may be fractional) 12 | % f - Normalized frequency bands to try to correct - may be outside Nyquist (-1..1) 13 | % ptype - type of pulse, 'ex', 'se', 'sat, 'inv' 14 | % ss_type - 'Flyback' or 'EP' 15 | % slr - SLR flag 16 | % reg_factor - regularization factor to reduce peak amplitude increases 17 | % from matrix inversion in EP designs 18 | % 19 | % Outputs: 20 | % rf - rf taps to give desired tip 21 | % 22 | % It is assumed that the first gradient lobe is a positive one, 23 | % 24 | % If a "Flyback" ss_type is specified, then filter taps will all 25 | % be moving forward together by "n" units. With minimum-phase filters 26 | % it is best to use the spectral filter reference for the first tap 27 | % then move forward. 28 | % 29 | % If a "EP" ss_type is specified, then filter taps will be moving 30 | % alternately forward/backward by "n" units. The reference should 31 | % be the midpoint of the spectral lobes. 32 | % 33 | 34 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 35 | % 36 | % Spectral-Spatial RF Pulse Design for MRI and MRSI MATLAB Package 37 | % 38 | % Authors: Adam B. Kerr and Peder E. Z. Larson 39 | % 40 | % (c)2007-2011 Board of Trustees, Leland Stanford Junior University and 41 | % The Regents of the University of California. 42 | % All Rights Reserved. 43 | % 44 | % Please see the Copyright_Information and README files included with this 45 | % package. All works derived from this package must be properly cited. 46 | % 47 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 48 | % 49 | % This method is also described in: 50 | % (1) C.H. Cunningham, D.B. Vigneron, A.P. Chen, D. Xu, M. Lustig, D.A. Kelley, 51 | % J.M. Pauly, Spectral?spatial excitation and refocusing for reduced volume 52 | % mis- registration at 7 Tesla, in: Proceedings of the 14th Annual Meeting 53 | % of ISMRM, Seattle, 2006, p. 72. 54 | % (2) C.H. Cunningham, A.P. Chen, M. Lustig, J. Lupo, D. Xu, J. Kurhanewicz, 55 | % R.E. Hurd, J.M. Pauly, S.J. Nelson, D.B. Vigneron, Pulse sequence for 56 | % dynamic volumetric imaging of hyperpolarized metabolic products, J. Magn. 57 | % Reson. 193 (1) (2008) 139?146. 58 | 59 | 60 | % Error checking on inputs 61 | % 62 | if nargin < 7, 63 | error('Usage: ss_spect_correct(b, bsf, N, n, f, ptype, ss_type, reg_factor, dbg)'); 64 | end; 65 | 66 | if nargin < 8, 67 | reg_factor = 0; 68 | end; 69 | 70 | if nargin < 9, 71 | dbg = 0; 72 | end; 73 | 74 | switch ss_type, 75 | case {'Flyback', 'EP'} 76 | otherwise 77 | error('ss_type must be ''Flyback'' or ''EP'''); 78 | end; 79 | 80 | if length(bsf) ~= length(Noff), 81 | error('bsf / offset vectors not same length'); 82 | end; 83 | nfilt = length(bsf); 84 | 85 | % Calculate sampling positions of ref filter taps 86 | % 87 | N = length(b); 88 | t_ref = [0:N-1]; 89 | 90 | % Get sampling in pass/stopbands 91 | % ensuring that mult_factor*N samples exist 92 | % 93 | mult_factor = 15; 94 | fdiff = diff(f); 95 | fsum = sum(fdiff(1:2:end)); % frequency range that is sampled 96 | df = fsum/(mult_factor*N); 97 | 98 | nband = length(f)/2; 99 | 100 | % Use this definition of frequency to correct entire base bandwidth but 101 | % not aliased bands: 102 | % w = linspace(-pi, pi, 2*mult_factor*N); 103 | 104 | w = []; 105 | for band = 1:nband, 106 | nf = ceil((f(band*2)-f(band*2-1))/df) + 1; 107 | df_act = (f(band*2)-f(band*2-1))/(nf-1); 108 | wband = f(band*2-1) + [0:nf-1]*df_act; 109 | w = [w pi*wband]; 110 | end; 111 | 112 | % Plot w sampling 113 | % 114 | if (dbg >= 2), % Verbose 115 | figure; 116 | subplot(2,1,1); 117 | plot(w/pi,ones(length(w)),'bx'); 118 | hold on; 119 | for band = 1:nband, 120 | plot(f(band*2-1)*ones(1,2), [0 1], 'r'); 121 | plot(f(band*2)*ones(1,2), [0 1], 'r'); 122 | end; 123 | title('Band Sampling'); 124 | xlabel('Normalized Frequency'); 125 | axis([min(w/pi) max(w/pi) -0.2 1.2]); 126 | 127 | [h_freq,freq] = freqz(b,1,[min(w/pi):0.005:max(w/pi)],2); 128 | 129 | subplot(2,1,2); 130 | plot(freq,abs(h_freq)); 131 | title('Frequency Response'); 132 | xlabel('Normalized Frequency'); 133 | axis([min(w/pi) max(w/pi) -0.2 1.2]); 134 | end; 135 | 136 | 137 | % Calculate spectral filter corrections 138 | % 139 | if (dbg >= 2), 140 | filt_fig = figure; 141 | end; 142 | 143 | % Get reference transform 144 | % 145 | Wref = exp(-i*kron(w', t_ref)); 146 | Fref = Wref * b(:); 147 | 148 | for idx = 1:nfilt, 149 | % Get actual sampling positions 150 | % 151 | switch (ss_type), 152 | case 'Flyback', 153 | t_act = t_ref + Noff(idx)/Nper; 154 | case 'EP', 155 | t_act = t_ref + (Noff(idx)/Nper * (-1).^[0:N-1]); 156 | end; 157 | 158 | if (dbg >= 2) 159 | figure(filt_fig); 160 | subplot(411); 161 | stem([t_ref.' t_act.'], ones(length(t_ref),2)); 162 | legend('Reference', 'Actual'); 163 | title('Sampling Locations'); 164 | end; 165 | 166 | % Get actual desired frequency response 167 | % 168 | Wact = exp(-i*kron(w', t_act)); 169 | 170 | switch (ss_type) 171 | case 'Flyback' 172 | 173 | % Get least-squares fit to filter 174 | % 175 | type = 0; 176 | switch(type) 177 | case 0 178 | % Least-squares solution with pseudo-inversion 179 | % no regularization in this problem, typically not needed for Flyback designs 180 | % 181 | Wact_pinv = pinv(Wact); 182 | bm(:,idx) = Wact_pinv * Fref; 183 | %bm(:,idx) = inv(Wact'*Wact + 1e-4*eye(size(Wact,2)))*Wact'*Fref; 184 | case 1 185 | % Do constrained least-squares - best option, but slow 186 | % Could be sped up using pdco 187 | % 188 | bm(:,idx) = lscon(Wact, Fref(:), 0, 1.2*max(abs(b)), b(:), 0); 189 | case 2 190 | % pdco option 191 | % 192 | c = 1; 193 | pdco_options = pdcoSet; 194 | d1 = 1e-6; 195 | d2 = 1; 196 | x0 = b(:); 197 | y0 = zeros(size(Wact,1),1); 198 | z0 = ones(size(Wact,2),1); 199 | xsize = mean(abs(b)); 200 | zsize = 1; 201 | bm(:,idx) = pdco(c, Wact, Fref(:), -1.2*max(abs(b)), 1.2*max(abs(b)), ... 202 | d1, d2, pdco_options, x0, y0, z0, xsize,zsize,1); 203 | end; 204 | 205 | if 0 206 | figure; 207 | Fref_fix = Wact * bm(:,idx); 208 | plot(w/pi,abs(Fref_fix)); 209 | pause; 210 | end; 211 | 212 | % Since samples are still uniform, we can 213 | % just use SLR to get rf 214 | % 215 | if slr, 216 | rfm(:,idx) = b2rf(bsf(idx) * bm(:,idx)); 217 | else 218 | rfm(:,idx) = 2*asin(abs(bsf(idx))) * conj(bm(:,idx)); 219 | end; 220 | case 'EP' 221 | 222 | % Get least-squares fit to filter 223 | % 224 | % Wact_pinv = pinv(Wact); 225 | % bm(:,idx) = Wact_pinv * Fref; 226 | 227 | % Changed to regularized least-squares, works pretty well to keep peak B1 from 228 | % getting too large 229 | bm(:,idx) = inv(Wact'*Wact + reg_factor*eye(size(Wact,2)))*Wact'*Fref; 230 | 231 | % not very successful with constrained least-squares for keeping peak from getting large 232 | % bm(:,idx) = lsqlin(Wact, Fref(:), [], [], [], [], zeros(size(b)), 2*max(abs(b))*ones(size(b)), b(:)); 233 | 234 | % Can't do SLR yet 235 | % 236 | if slr, 237 | rfm(:,idx) = 2*asin(abs(bsf(idx)))*conj(bm(:,idx)); 238 | else 239 | rfm(:,idx) = 2*asin(abs(bsf(idx))) * bm(:,idx); 240 | end; 241 | end; 242 | 243 | if ( (dbg >= 2) && (rem(idx,10)==0) ), 244 | figure(filt_fig); 245 | show_M = 0; 246 | plot_db = 0; 247 | if show_M, 248 | rf_ref = b2rf(bsf(round(end/2)) * b(:)); 249 | subplot(412); 250 | hold off; 251 | plot(abs(rf_ref),'b-'); 252 | hold on; 253 | plot(abs(rfm(:,idx)),'r--'); 254 | 255 | % Fill in large time indices with RF 256 | % 257 | t_ref_x = Nper + t_ref*Nper; 258 | t_act_x = Nper + round(t_act*Nper); 259 | 260 | % Fill in large (mostly zero) arrays with RF 261 | % 262 | rf_ref_x = zeros(1,(N+2)*Nper); 263 | rf_ref_x(t_ref_x) = rf_ref; 264 | 265 | rf_act_x = zeros(1,(N+2)*Nper); 266 | rf_act_x(t_act_x) = rf_ref; 267 | 268 | rf_actfix_x = zeros(1,(N+2)*Nper); 269 | rf_actfix_x(t_act_x) = rfm(:,idx); 270 | 271 | % Now determine M 272 | % 273 | g = pi * ones(length(rf_ref_x)); 274 | Mref = ab2ex(abr(rf_ref_x, g, w/pi)); 275 | Mact = ab2ex(abr(rf_act_x, g, w/pi)); 276 | Mactfix = ab2ex(abr(rf_actfix_x, g, w/pi)); 277 | subplot(413); 278 | if plot_db, 279 | hold off; 280 | plot(w/pi,20*log10(abs(Mref)),'b'); 281 | hold on; 282 | plot(w/pi,20*log10(abs(Mactfix)),'r--'); 283 | plot(w/pi,20*log10(abs(Mact)),'g--'); 284 | ylabel('DB Scale'); 285 | else 286 | hold off; 287 | plot(w/pi,abs(Mref),'b'); 288 | hold on; 289 | plot(w/pi,abs(Mactfix),'r--'); 290 | plot(w/pi,abs(Mact),'g--'); 291 | ylabel('Linear Scale'); 292 | end; 293 | title('Magnitude Magnetization'); 294 | 295 | subplot(414); 296 | hold off; 297 | plot(w/pi,angle(Mref),'b'); 298 | hold on; 299 | plot(w/pi,angle(Mactfix),'r--'); 300 | plot(w/pi,angle(Mact),'g--'); 301 | title('Phase Magnetization'); 302 | else, 303 | Fact = Wact * b(:); 304 | Fact_fix = Wact * bm(:,idx); 305 | 306 | subplot(4,1,2); 307 | hold off; 308 | plot(abs(b)); 309 | hold on; 310 | plot(abs(bm(:,idx)), 'r--'); 311 | title('Beta Polynomials'); 312 | 313 | subplot(4,1,3); 314 | if plot_db, 315 | hold off; 316 | plot(w/pi,20*log10(abs(Fref)),'b-'); 317 | hold on; 318 | plot(w/pi, 20*log10(abs(Fact)), 'g--'); 319 | plot(w/pi, 20*log10(abs(Fact_fix)), 'r--'); 320 | ylabel('DB Scale'); 321 | else 322 | hold off; 323 | plot(w/pi,abs(Fref),'b-'); 324 | hold on; 325 | plot(w/pi, abs(Fact), 'g--'); 326 | plot(w/pi, abs(Fact_fix), 'r--'); 327 | ylabel('Linear Scale'); 328 | end; 329 | 330 | title('Magnitude Response'); 331 | 332 | subplot(4,1,4); 333 | hold off; 334 | plot(w/pi,angle(Fref),'b-'); 335 | hold on; 336 | plot(w/pi,angle(Fact),'g--'); 337 | plot(w/pi,angle(Fact_fix),'r--'); 338 | title('Phase Response'); 339 | end; 340 | 341 | fprintf(1,'Offset: %f -- Hit any key to continue\n', Noff(idx)); 342 | pause; 343 | end; 344 | end; 345 | 346 | -------------------------------------------------------------------------------- /ss_verse.m: -------------------------------------------------------------------------------- 1 | % rfv = ss_verse(gv,rf) 2 | % 3 | % Computes the versed version of rf for a given time-vayring gradient gv 4 | 5 | % written by John Pauly, 1992 6 | % Bug fixes by Peder Larson, 2007 7 | % 8 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 9 | % 10 | % Spectral-Spatial RF Pulse Design for MRI and MRSI MATLAB Package 11 | % 12 | % Authors: Adam B. Kerr and Peder E. Z. Larson 13 | % 14 | % (c)1992-2011 Board of Trustees, Leland Stanford Junior University and 15 | % The Regents of the University of California. 16 | % All Rights Reserved. 17 | % 18 | % Please see the Copyright_Information and README files included with this 19 | % package. All works derived from this package must be properly cited. 20 | % 21 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 22 | 23 | function rfv = ss_verse(g,rf) 24 | 25 | [m n] = size(g); 26 | if m