├── LICENSE ├── README.md ├── attic ├── @SingleGIRFdata │ ├── ComputeGirf.m │ ├── ComputeInputs.m │ ├── FilterGIRF.m │ ├── SingleGIRFdata.m │ ├── load_input_grads.m │ └── vis.m └── @SystemGIRFdata │ ├── ComputePE.m │ ├── PredictGrad.m │ ├── SystemGIRFdata.m │ └── vis.m ├── classes ├── @GirfApplier │ ├── GirfApplier.m │ ├── PredictGrad.m │ └── PredictGradZF.m ├── @GirfEssential │ ├── ConvertDomain.m │ ├── GirfEssential.m │ ├── Load.m │ └── Save.m └── @GirfProvider │ ├── ComputeGirf.m │ ├── GirfProvider.m │ ├── PeakElimination.m │ ├── ResampleInOut.m │ ├── VarSmoothFreq.m │ ├── Vis.m │ ├── WindowFreq.m │ └── WindowTime.m ├── test └── DemoGirf.m └── utils ├── BW_filter.m ├── BW_window.m ├── CenteredPhase.m ├── CenteredTime.m ├── ComputeInputs.m ├── VariableSmoothing.m ├── raised_cosine.m ├── sweeps.m ├── time2freq.m └── trapezoid.m /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2020 4 | Authors: Johanna Vannesjo, Jennifer Nussbaum, Lars Kasper, Nadine Graedel 5 | University of Zurich and ETH Zurich, University of Oxford 6 | All rights reserved. 7 | 8 | Redistribution and use in source and binary forms, with or without 9 | modification, are permitted provided that the following conditions are met: 10 | 11 | 1. Redistributions of source code must retain the above copyright notice, this 12 | list of conditions and the following disclaimer. 13 | 14 | 2. Redistributions in binary form must reproduce the above copyright notice, 15 | this list of conditions and the following disclaimer in the documentation 16 | and/or other materials provided with the distribution. 17 | 18 | 3. Neither the name of the copyright holder nor the names of its 19 | contributors may be used to endorse or promote products derived from 20 | this software without specific prior written permission. 21 | 22 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 23 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 25 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 26 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 27 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 28 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 29 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 30 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 31 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 32 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # GIRF 2 | Code package to calculate and use the gradient impulse response function (GIRF) of an MRI gradient system. 3 | 4 | The basic functionality for GIRF calculation and trajectory prediction is demonstrated in 'DemoGirf.m' in the test-folder. Make sure that the folders 'classes' and 'utils' are on the Matlab path. 5 | 6 | *If you use this code package for your research, please cite at least one of the following papers (depending on the use case):* 7 | **Gradient system characterization** 8 | Vannesjo, S.J., Haeberlin, M., Kasper, L., Pavan, M., Wilm, B.J., Barmet, C., Pruessmann, K.P., 2013. Gradient system characterization by impulse response measurements with a dynamic field camera. Magn Reson Med 69, 583–593. https://doi.org/10.1002/mrm.24263 9 | **Image reconstruction based on the GIRF characterization** 10 | Vannesjo, S.J., Graedel, N.N., Kasper, L., Gross, S., Busch, J., Haeberlin, M., Barmet, C., Pruessmann, K.P., 2016. Image reconstruction using a gradient impulse response model for trajectory prediction. Magn Reson Med 76, 45–58. https://doi.org/10.1002/mrm.25841 11 | **GIRF-based spiral fMRI** 12 | Graedel, N.N., Kasper, L., Engel, M., Nussbaum, J., Wilm, B.J., Pruessmann, K.P., Vannesjo, S.J., 2019. Feasibility of spiral fMRI based on an LTI gradient model. bioRxiv 805580. https://doi.org/10.1101/805580 13 | **GIRF-based pre-emphasis of gradient or shim channels** 14 | Vannesjo, S.J., Duerst, Y., Vionnet, L., Dietrich, B.E., Pavan, M., Gross, S., Barmet, C., Pruessmann, K.P., 2017. Gradient and shim pre-emphasis by inversion of a linear time-invariant system model. Magn Reson Med 78, 1607–1622. https://doi.org/10.1002/mrm.26531 15 | 16 | 17 | -------------------------------------------------------------------------------- /attic/@SingleGIRFdata/ComputeGirf.m: -------------------------------------------------------------------------------- 1 | function GIRF = ComputeGirf(this) 2 | % Actual GIRF computation 3 | % 4 | % 5 | % Author: Johanna Vannesjo (johanna.vannesjo@gmail.com) 6 | % Copyright (C) 2014 IBT, University of Zurich and ETH Zurich, 7 | % 2016 FMRIB centre, University of Oxford 8 | % 9 | % This file is part of a code package for GIRF computation and application. 10 | % The package is available under a BSD 3-clause license. Further info see: 11 | % https://github.com/MRI-gradient/girf 12 | % 13 | 14 | 15 | fprintf('Starting GIRF calculation...') 16 | ticGC = tic; 17 | 18 | % Get data size 19 | ns = length(this.tOut); 20 | if isempty(this.nOut) 21 | this.nOut = size(this.outputs,3); 22 | end 23 | if isempty(this.nIn) 24 | this.nIn = size(this.inputs,2); 25 | end 26 | 27 | % Call utils resample function to get input and output onto same raster! 28 | % Need flag for using input or output time raster as base for calculations? 29 | % 30 | 31 | % Zero-fill input and output for increased GIRF frequency resolution 32 | if ~isempty(this.doZerofill) 33 | dt = this.tOut(2)-this.tOut(1); 34 | T = ns*dt; 35 | ns_fill = ceil((this.doZerofill - T)/dt); 36 | inputs = [this.inputs; zeros(ns_fill, this.nIn)]; 37 | IN = fftshift(fft(inputs),1); 38 | outputs = zeros(ns+ns_fill, this.nIn, this.nOut); 39 | outputs(1:ns,:,:) = this.outputs; 40 | OUT = fftshift(fft(outputs),1); 41 | tOut = [0:dt:(ns+ns_fill-1)*dt]' + this.tOut(1); 42 | ns = ns+ns_fill; 43 | else 44 | IN = this.IN; 45 | OUT = this.OUT; 46 | tOut = this.tOut; 47 | end 48 | 49 | % Perform least-squares estimation from inputs 50 | f = time2freq(tOut); 51 | IN_inv = 1./sum(abs(IN).^2,2); 52 | GIRF = zeros(ns,this.nOut); 53 | for iK = 1:this.nOut 54 | GIRF(:,iK) = IN_inv.*sum(conj(IN).*OUT(:,:,iK),2); 55 | end 56 | GIRF(isnan(GIRF)) = 0; 57 | 58 | timeGC = toc(ticGC); 59 | fprintf(' done after %f seconds! \n', timeGC) 60 | 61 | % if this.doVarSmoothing 62 | % [GIRF, f] = VariableSmoothing(GIRF, f, this.BW); 63 | % else 64 | % GIRF = BWwindow(GIRF, f, this.BW, this.windowType, this.BW_rc); 65 | % end 66 | 67 | this.GIRF = GIRF; 68 | this.f = f; 69 | end 70 | 71 | -------------------------------------------------------------------------------- /attic/@SingleGIRFdata/ComputeInputs.m: -------------------------------------------------------------------------------- 1 | function inputs = ComputeInputs(this) 2 | % Function to compute set of blip inputs 3 | % 4 | % 5 | % Author: Johanna Vannesjo (johanna.vannesjo@gmail.com) 6 | % Copyright (C) 2014 IBT, University of Zurich and ETH Zurich, 7 | % 2016 FMRIB centre, University of Oxford 8 | % 9 | % This file is part of a code package for GIRF computation and application. 10 | % The package is available under a BSD 3-clause license. Further info see: 11 | % https://github.com/MRI-gradient/girf 12 | % 13 | 14 | 15 | % Check type of input pulse 16 | if isempty(this.pulseType) || ~iscell(this.pulseType) 17 | ex1 = ' girfo.pulseType = {''blips''}'; 18 | ex2 = ' girfo.pulseType = {''sweeps''}'; 19 | ex3 = ' girfo.pulseType = {''blips'' ''sweeps''}'; 20 | error('Please specify input pulse type as cell object, examples: \n %s \n %s \n %s',ex1,ex2,ex3) 21 | end 22 | 23 | inputsAll = []; 24 | for iP = 1:length(this.pulseType) 25 | switch this.pulseType{iP}; 26 | case {'blips' 'blip' 'Blips' 'Blip'} 27 | isBlips = 1; isSweeps = 0; 28 | case {'sweeps' 'sweep' 'Sweeps' 'Sweep'} 29 | isSweeps = 1; isBlips = 0; 30 | end 31 | 32 | % Set blip parameters 33 | if isBlips 34 | nPulses = length(this.dur); 35 | if this.fixedSlope 36 | this.amp = this.fixedSlope*this.dur; 37 | end 38 | if length(this.t0)= t0 & this.tOut <= t0+pulseDur); 101 | indsIn = ceil((this.tOut(indsOut)-t0)/this.dtIn)+1; 102 | inputs(indsOut,iIn) = pulseIn(indsIn); 103 | elseif strcmp(this.ipType,'interpolated') % calculate on input raster & do linear interpolation onto output raster 104 | indsOut = find(this.tOut >= tIn(1)+ t0 & this.tOut <= tIn(end)+ t0); 105 | inputs(indsOut,iIn) = interp1(tIn+t0,pulseIn,this.tOut(indsOut)); 106 | end 107 | end 108 | end 109 | % Concatenate all input pulses 110 | inputsAll = [inputsAll inputs]; 111 | end 112 | 113 | this.inputs = inputsAll; 114 | this.nIn = size(inputsAll,2); 115 | 116 | end 117 | 118 | 119 | -------------------------------------------------------------------------------- /attic/@SingleGIRFdata/FilterGIRF.m: -------------------------------------------------------------------------------- 1 | function [ GIRF, f ] = FilterGIRF( this ) 2 | %Window & smooth GIRf 3 | % 4 | % 5 | % Author: Johanna Vannesjo (johanna.vannesjo@gmail.com) 6 | % Copyright (C) 2014 IBT, University of Zurich and ETH Zurich, 7 | % 2016 FMRIB centre, University of Oxford 8 | % 9 | % This file is part of a code package for GIRF computation and application. 10 | % The package is available under a BSD 3-clause license. Further info see: 11 | % https://github.com/MRI-gradient/girf 12 | % 13 | 14 | 15 | if this.doVarSmoothing 16 | if ~this.isSmoothed 17 | [GIRF, f] = VariableSmoothing(this.GIRF, this.f, this.BW/2); 18 | this.isSmoothed = 1; 19 | this.f = f; 20 | end 21 | else 22 | if ~this.isWindowed 23 | GIRF = BWwindow(this.GIRF, this.f, this.BW, this.windowType, this.BW_rc); 24 | this.isWindowed = 1; 25 | end 26 | end 27 | this.GIRF = GIRF; 28 | 29 | end 30 | 31 | -------------------------------------------------------------------------------- /attic/@SingleGIRFdata/SingleGIRFdata.m: -------------------------------------------------------------------------------- 1 | classdef SingleGIRFdata < matlab.mixin.Copyable 2 | % Class for handling GIRFs and trajectory predictions 3 | % 4 | % girfX = SingleGIRFdata(); 5 | % 6 | % This class handles all parameteres related to the calculation of a 7 | % measured Gradient or Shim Impulse Response Function. 8 | % 9 | % Author: Johanna Vannesjo (johanna.vannesjo@gmail.com) 10 | % Copyright (C) 2014 IBT, University of Zurich and ETH Zurich, 11 | % 2016 FMRIB centre, University of Oxford 12 | % 13 | % This file is part of a code package for GIRF computation and application. 14 | % The package is available under a BSD 3-clause license. Further info see: 15 | % https://github.com/MRI-gradient/girf 16 | % 17 | 18 | 19 | properties 20 | 21 | %%%% GIRF - essential variables %%%%%%%%%%%%%%%%%%%% 22 | GIRF; % Calculated GIRF (self- and crossterms) 23 | f; % frequency vector for GIRF 24 | df; % nominal frequency resolution of GIRF 25 | 26 | inputs; % [nSamples x nIn] System inputs in the time domain 27 | outputs; % [nSamples x nIn x nOut] measured gradient/shim fields in the time domain 28 | nIn; % Number of input pulses 29 | nOut; % Number of output field basis functions 30 | 31 | tIn; % time vector for input gradients 32 | dtIn; % gradient input sampling interval 33 | tOut; % time vector for measured fields 34 | dtOut; % sampling interval of measured output 35 | 36 | %%%% Switches and filter parameters %%%%%%%%%%%%%%%% 37 | doZerofill = [];% [s] Fill up in/out to 1 Hz frequency resolution 38 | doVarSmoothing = 0; % Perform variable smoothing on calculated GIRF 39 | isSmoothed = 0; % Has variable smoothing been performed already? 40 | 41 | windowType = 'rc';% Type of BW window {'ga' 'rc' 'bl' 'bh'} 42 | BW = []; % BW of GIRF window, FWHM 43 | BW_rc = 1/3; % Transition parameter of BW window (only for raised cosine window) 44 | isWindowed = 0; % Has BW windowing been performed already? 45 | 46 | self; % Gradient or shim channel monitored field number 47 | channel; % Gradient or shim channel name 48 | 49 | %%%% Input properties (all) %%%%%%%%%%%%%%%%%%%%% 50 | pulseType % input pulse type {'blips' 'sweeps' 'arb'} 51 | ipType = 'ideal'; %{'ideal' 'sampleAndHold' 'interpolated'} 52 | 53 | %%%% Blip input related properties %%%%%%%%%%%%%% 54 | dur; % input blip slope durations 55 | dur2; % input blip slope durations 56 | plateau; % input trapezoid plateau lengths 57 | fixedSlope = 0; % pulse slope if same for all blips 58 | amp; % input pulse amplitudes 59 | t0; % input pulse starting time (relative to tOut) 60 | 61 | %%%% Sweep input related properties %%%%%%%%%%%%%% 62 | sweepType; % sweep type {'linear' 'quadratic' 'cubic' int} 63 | Tsweep; % sweep pulse length 64 | ampSweep; % input pulse amplitudes 65 | t0Sweep; % input pulse starting time (relative to tOut) 66 | f1; % sweep start frequency 67 | f2; % sweep end frequency 68 | phi0; % sweep pulse starting phase 69 | AM; % structure determining sweep amplitude modulation 70 | smoothing; % structure deteriming sweep end smoothing 71 | slew = 200; % slew rate limit of gradient system 72 | ilPhaseShift = 0;% shift of sweep pulse between interleaves 73 | ilTshift = 0; % shift factor of Tsweep between interleaves 74 | 75 | %%%% Measured output data handling - obsolete?? %%%%%%%%%%%%%%% 76 | % gamma = 2.675222099e8; % 1H, as in ReconstructionData 77 | 78 | dataPath; % path to reconstructed traj-/or NI-data 79 | origDataPath = 'data'; % path to original dataset (for finding params folder) 80 | dataID; % [n_concats x nIn] data numbers to be loaded 81 | kBasis; % selected output field basis functions TODO - is this used at all??? 82 | dyns; % {nIn} dynamics to be averaged 83 | il = 1; % interleaves to use for GIRF calculation 84 | ilTot = 1; % total number of interleaves acquired (for correct shifting) 85 | tUse; % time interval of measured data to use for GIRF calculation 86 | 87 | doOffsetCorr; % correct for baseline offset {0 or 1} 88 | doSlopeCorr; % correct for baseline slope {0 or 1} 89 | tCorr % time interval for calculation of correction parameters 90 | 91 | %%%% Obsolete or useless...? %%%%%%%%%%%%%%%%%%%%%%%% 92 | isReadinInput = 0; % If input acquired with Philips readin patch 93 | nr_averages; 94 | average_k; 95 | TR; 96 | shifts; 97 | Grads; 98 | 99 | end 100 | properties (Dependent = true, SetAccess = private) 101 | IN; % system inputs in the frequency domain 102 | OUT; % measured gradient/shim fields in the frequency domain 103 | t_k; % time vector shifted by dt/2 (for phase data) 104 | girft; % GIRF in the time domain 105 | end 106 | methods 107 | 108 | % Get methods for dependent properties 109 | function IN = get.IN(this) 110 | IN = fftshift(fft(this.inputs),1); 111 | end 112 | function OUT = get.OUT(this) 113 | OUT = fftshift(fft(this.outputs),1); 114 | end 115 | function t_k = get.t_k(this) 116 | t_k = this.tOut+this.dt/2; 117 | end 118 | function girft = get.girft(this) 119 | girft = fftshift(ifft(ifftshift(this.GIRF,1)),1); 120 | end 121 | 122 | end 123 | end -------------------------------------------------------------------------------- /attic/@SingleGIRFdata/load_input_grads.m: -------------------------------------------------------------------------------- 1 | function inputs = load_input_grads(this) 2 | % Function to load input gradients from files in parameter 3 | % directory 4 | % 5 | % 6 | % Author: Johanna Vannesjo (johanna.vannesjo@gmail.com) 7 | % Copyright (C) 2014 IBT, University of Zurich and ETH Zurich, 8 | % 2016 FMRIB centre, University of Oxford 9 | % 10 | % This file is part of a code package for GIRF computation and application. 11 | % The package is available under a BSD 3-clause license. Further info see: 12 | % https://github.com/MRI-gradient/girf 13 | % 14 | 15 | 16 | this.nIn = size(this.dataID,2); 17 | 18 | % Load from file 19 | for iN = 1:this.nIn 20 | [G, t_in] = load_grads_from_file(1, [], 1, this.dataID(1,iN)); 21 | % in_temp(:,iN) = G(:,this.self-1); 22 | in_temp(:,iN) = G(:,2); 23 | end 24 | 25 | % Interpolate onto output time grid 26 | ns_in = length(t_in); 27 | ns_out = length(this.tOut); 28 | dt_in = t_in(2) - t_in(1); 29 | dtOut = this.tOut(2) - this.tOut(1); 30 | if ns_in ~= ns_out || dt_in ~= dtOut 31 | for iN = 1:this.nIn 32 | inputs(:,iN) = interp1(t_in, in_temp(:,iN), this.tOut); 33 | end 34 | end 35 | inputs(isnan(inputs)) = 0; 36 | 37 | % this.t_in = t_in; 38 | % inputs = in_temp; 39 | this.inputs = inputs; 40 | end 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /attic/@SingleGIRFdata/vis.m: -------------------------------------------------------------------------------- 1 | function vis(this, varargin) 2 | % Visualization function 3 | % 4 | % 5 | % Author: Johanna Vannesjo (johanna.vannesjo@gmail.com) 6 | % Copyright (C) 2014 IBT, University of Zurich and ETH Zurich, 7 | % 2016 FMRIB centre, University of Oxford 8 | % 9 | % This file is part of a code package for GIRF computation and application. 10 | % The package is available under a BSD 3-clause license. Further info see: 11 | % https://github.com/MRI-gradient/girf 12 | % 13 | 14 | 15 | % Assign input variables and defaults 16 | argin_names = {'plottype' 'domain' 'k_plot' 'BW' 'BW_rc' 'pulses' 'integration'}; 17 | defaults = {'''GIRF''' '''f''' '''self''' '0' '0' '''all''' '0'}; 18 | for iN = 1:nargin-1 19 | eval([argin_names{iN} ' = varargin{' num2str(iN) '};']); 20 | end 21 | for iN = nargin:length(argin_names) 22 | eval([argin_names{iN} ' = ' defaults{iN} ';']); 23 | end 24 | if strcmp(k_plot,'self') 25 | k_plot = this.self; 26 | end 27 | if strcmp(pulses, 'all') 28 | pulses = 1:this.nIn; 29 | end 30 | 31 | % Make vector for x-axis in plot 32 | switch domain 33 | case 'f' 34 | xdata = this.f; 35 | if isempty(xdata) 36 | xdata = time2freq(this.tOut); 37 | this.f = xdata; 38 | end 39 | if BW 40 | inds = find(abs(xdata) 2 142 | saveType = varargin{1}; 143 | else 144 | saveType = 'variables'; 145 | end 146 | switch saveType 147 | case {0 'obj' 'object'} 148 | girfo = this; 149 | save(saveName, 'girfo') 150 | case {1 'var' 'variables'} 151 | GIRF = this.GIRF; 152 | f = this.f; 153 | channels = this.channels; 154 | save(saveName, 'GIRF', 'f', 'channels') 155 | case {2 'dir' 'directories'} 156 | if ~exist(saveName,'dir') 157 | mkdir(saveName) 158 | end 159 | channels = this.channels; 160 | for iCh = 1:length(channels) 161 | if ~isempty(this.GIRF{iCh}) 162 | fileName = [saveName filesep channels{iCh}]; 163 | GIRF = this.GIRF{iCh}; 164 | f = this.f{iCh}; 165 | channel = channels{iCh}; 166 | save(fileName, 'GIRF', 'f', 'channel') 167 | end 168 | end 169 | end 170 | end 171 | 172 | % Load GIRF data saved as variables 173 | function Load(this,loadName,loadType,varargin) 174 | if nargin < 3 175 | if exist(loadName,'var') 176 | loadType = 'var'; 177 | elseif exist(loadName,'dir') 178 | loadType = 'dir'; 179 | else 180 | error('Could not recognize %s as neither file nor directory',loadName) 181 | end 182 | end 183 | 184 | switch loadType 185 | case {'var','variables'} 186 | try 187 | GIRFdata = load(loadName); 188 | catch 189 | error('Could not load: %s', loadName) 190 | end 191 | 192 | if isfield(GIRFdata,'channels') 193 | channels = GIRFdata.channels; 194 | else 195 | channels = this.channels; 196 | end 197 | for iCh = 1:length(channels) 198 | this.(channels{iCh}).GIRF = GIRFdata.GIRF(:,:,iCh); 199 | this.(channels{iCh}).f = GIRFdata.f; 200 | end 201 | case {'dir','directories'} 202 | for iCh = 1:length(this.allChannels) 203 | ch = this.allChannels{iCh}; 204 | fileName = [loadName filesep ch '.mat']; 205 | if exist(fileName,'file') 206 | try 207 | GIRFdata = load(fileName); 208 | if isfield(GIRFdata,'SIRF') 209 | this.(ch).GIRF = GIRFdata.SIRF; 210 | else 211 | this.(ch).GIRF = GIRFdata.GIRF; 212 | end 213 | this.(ch).f = GIRFdata.f; 214 | catch 215 | fprintf('Could not find SIRF/GIRF variable in: %s \n',fileName) 216 | end 217 | else 218 | fprintf('Could not find file: %s \n',fileName) 219 | end 220 | end 221 | end 222 | end 223 | 224 | % Remove inputs & outputs from single GIRF data to save disk space 225 | function ClearInOut(this) 226 | for iG = 1:17 227 | this.(this.channels{iG}).inputs = []; 228 | this.(this.channels{iG}).outputs = []; 229 | end 230 | end 231 | end 232 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 233 | end 234 | -------------------------------------------------------------------------------- /attic/@SystemGIRFdata/vis.m: -------------------------------------------------------------------------------- 1 | function vis(this, varargin) 2 | % Visualization function 3 | % 4 | % 5 | % Author: Johanna Vannesjo (johanna.vannesjo@gmail.com) 6 | % Copyright (C) 2014 IBT, University of Zurich and ETH Zurich, 7 | % 2016 FMRIB centre, University of Oxford 8 | % 9 | % This file is part of a code package for GIRF computation and application. 10 | % The package is available under a BSD 3-clause license. Further info see: 11 | % https://github.com/MRI-gradient/girf 12 | % 13 | 14 | 15 | % Assign input variables and defaults 16 | argin_names = {'GIRFs' 'domain' 'k_plot' 'BW' 'BW_rc' 'integration'}; 17 | defaults = {'{''X'' ''Y'' ''Z''}' 'f' 'self' '0' '0' '0'}; 18 | for iN = 1:nargin 19 | eval([argin_names{iN} ' = varargin{' num2str(iN) '};']); 20 | end 21 | for iN = nargin+1:length(argin_names) 22 | eval([argin_names{iN} ' = ' defaults{iN} ';']); 23 | end 24 | 25 | switch domain % TODO: f and t in SystemGIRFdata 26 | case 'f' 27 | xdata = this.f; 28 | if BW 29 | inds = find(abs(xdata) 0 && isa(girfEssential,'GirfEssential') 32 | this.isFreqDomainGirf = girfEssential.isFreqDomainGirf; 33 | this.inChannels = girfEssential.inChannels; 34 | this.outBasis = girfEssential.outBasis; 35 | 36 | this.girf = girfEssential.girf; 37 | this.freq = girfEssential.freq; 38 | 39 | this.girfTime = girfEssential.girfTime; 40 | this.time = girfEssential.time; 41 | end 42 | end 43 | 44 | % NOTE: Most of the methods are saved in separate function.m-files in this folder; 45 | % except: constructor, delete, set/get methods for properties. 46 | 47 | end % methods 48 | 49 | end 50 | -------------------------------------------------------------------------------- /classes/@GirfApplier/PredictGrad.m: -------------------------------------------------------------------------------- 1 | function [gOut, kOut, tK] = PredictGrad(this, tIn, gIn, tOut, predChannels, convType) 2 | %Calculate pre-emphasis prediction 3 | % 4 | % IN 5 | % tIn Input time vector 6 | % gIn Input gradients 7 | % tOut Optional: Input time vector (may be same as input time vector) 8 | % channels Optional: Channels to predict 9 | % convType Optional: Type of convolution for prediction 10 | % 11 | % OUT 12 | % gOut Predicted gradient output (on time vector of tOut) 13 | % kOut Predicted k-output (on time vector of tOut + dt/2) 14 | % tK Time vector for predicted k-output 15 | % 16 | % EXAMPLE 17 | % [gOut, kOut, tK] = girfApplier.PredictGrad(this, tIn, gIn, tOut, channels, convType); 18 | % 19 | % See also GirfApplier, GirfEssential 20 | % 21 | % Author: Johanna Vannesjo (johanna.vannesjo@gmail.com) 22 | % Copyright (C) 2014 IBT, University of Zurich and ETH Zurich, 23 | % 2016 FMRIB centre, University of Oxford 24 | % 25 | % This file is part of a code package for GIRF computation and application. 26 | % The package is available under a BSD 3-clause license. Further info see: 27 | % https://github.com/MRI-gradient/girf 28 | % 29 | 30 | 31 | % Assign default variables 32 | if ~exist('predChannels','var') 33 | predChannels = {'X' 'Y' 'Z'}; 34 | end 35 | if ~exist('convType','var') 36 | convType = 'conv'; 37 | end 38 | if (nargin < 4 ) || isempty(tOut) 39 | tOut = tIn; 40 | end 41 | 42 | % Check dimensions 43 | nCh = length(predChannels); 44 | nIn = size(gIn,2); 45 | if nIn ~= nCh 46 | error('Number of inputs (%d) must match number of assigned channels (%s)',nIn,[predChannels{:}]) 47 | end 48 | nOut = size(this.girf, 2); 49 | nsH = length(this.freq); 50 | nIl = size(gIn,3); 51 | 52 | % Assemble H & select required channels 53 | H = zeros(nsH,nIn,nOut); 54 | for iCh = 1:nCh 55 | [chExists, chInd] = ismember(predChannels{iCh},this.inChannels); 56 | if chExists 57 | H(:,iCh,:) = this.girf(:,:,chInd); 58 | else 59 | error('Input channel %s cannot be found',predChannels{iCh}) 60 | end 61 | end 62 | fH = this.freq; 63 | dfH = fH(2)-fH(1); 64 | 65 | % Set length of prediction 66 | TH = 1/dfH; 67 | TO = max(tIn(end),tOut(end)) - min(tIn(1),tOut(1)); 68 | TZF = min(TH,TO); 69 | 70 | % Prepare input 71 | dtIn = tIn(2) - tIn(1); 72 | nZF = ceil(TZF/dtIn); 73 | gIn = [zeros(nZF,nIn,nIl); gIn; zeros(nZF,nIn,nIl)]; 74 | tIn = [tIn(1)+dtIn*(-nZF:-1)'; tIn; tIn(end)+dtIn*(1:nZF)']; 75 | fIn = time2freq(tIn); 76 | IN = fftshift(fft(gIn),1)*dtIn; 77 | nsIn = length(tIn); 78 | T_In = tIn(end) - tIn(1); 79 | 80 | % Interpolate GIRF onto input grid 81 | HIp = interp1(fH,H,fIn); 82 | HIp(isnan(HIp)) = 0; 83 | 84 | if strcmp(convType,'conv') 85 | hTime = real(ifft(ifftshift(HIp,1))); 86 | tStart = 1e-3; % impulse response startup time to use 87 | tSettle = TZF; % impulse response settling time to use 88 | tEndInd = ceil(tSettle/dtIn); 89 | tStartInd = ceil(tStart/dtIn); 90 | hTime([tEndInd+1:nsIn-tStartInd-1],:,:) = 0; 91 | HNew = fftshift(fft(hTime),1); 92 | HNew(isnan(HNew)) = 0; 93 | HIp = HNew; 94 | end 95 | 96 | OUT = zeros(nsIn, nOut, nIl); 97 | for iIl = 1:nIl 98 | OUT(:,:,iIl) = sum(repmat(IN(:,:,iIl),[1 1 nOut]).*HIp,2); 99 | end 100 | gOut = ifft(ifftshift(OUT,1))/dtIn; 101 | gOut = real(gOut); 102 | 103 | % Integrate gradients to k-coefficients 104 | gamma = this.gamma1H; 105 | kOut = cumsum(gOut)*dtIn*gamma; 106 | 107 | % Optionally interpolate output onto new grid 108 | dtOut = tOut(2) - tOut(1); 109 | gOut = interp1(tIn, gOut, tOut); 110 | kOut = interp1(tIn + dtIn/2, kOut, tOut + dtOut/2); 111 | gOut(isnan(gOut)) = 0; 112 | kOut(isnan(kOut)) = 0; 113 | 114 | tK = tOut + dtOut/2; 115 | 116 | 117 | 118 | 119 | 120 | 121 | -------------------------------------------------------------------------------- /classes/@GirfApplier/PredictGradZF.m: -------------------------------------------------------------------------------- 1 | function [gOut, kOut, tK] = PredictGradZF(this, tIn, gIn, tOut, predChannels, convType) 2 | %Calculate pre-emphasis prediction 3 | % 4 | % IN 5 | % tIn Input time vector 6 | % gIn Input gradients 7 | % tOut Optional: Input time vector (may be same as input time vector) 8 | % channels Optional: Channels to predict 9 | % convType Optional: Type of convolution for prediction 10 | % 11 | % OUT 12 | % gOut Predicted gradient output (on time vector of tOut) 13 | % kOut Predicted k-output (on time vector of tOut + dt/2) 14 | % tK Time vector for predicted k-output 15 | % 16 | % EXAMPLE 17 | % [gOut, kOut, tK] = girfApplier.PredictGrad(this, tIn, gIn, tOut, channels, convType); 18 | % 19 | % See also GirfApplier, GirfEssential 20 | % 21 | % Author: Johanna Vannesjo (johanna.vannesjo@gmail.com) 22 | % Copyright (C) 2014 IBT, University of Zurich and ETH Zurich, 23 | % 2016 FMRIB centre, University of Oxford 24 | % 25 | % This file is part of a code package for GIRF computation and application. 26 | % The package is available under a BSD 3-clause license. Further info see: 27 | % https://github.com/MRI-gradient/girf 28 | % 29 | 30 | 31 | % Assign default variables 32 | if ~exist('predChannels','var') 33 | predChannels = {'X' 'Y' 'Z'}; 34 | end 35 | if ~exist('convType','var') 36 | convType = 'conv'; 37 | end 38 | if (nargin < 4 ) || isempty(tOut) 39 | tOut = tIn; 40 | end 41 | 42 | % Check dimensions 43 | nCh = length(predChannels); 44 | nIn = size(gIn,2); 45 | if nIn ~= nCh 46 | error('Number of inputs (%d) must match number of assigned channels (%s)',nIn,[predChannels{:}]) 47 | end 48 | nOut = size(this.girf, 2); 49 | nsH = length(this.freq); 50 | nIl = size(gIn,3); 51 | 52 | % Assemble H & select required channels 53 | H = zeros(nsH,nIn,nOut); 54 | for iCh = 1:nCh 55 | [chExists, chInd] = ismember(predChannels{iCh},this.inChannels); 56 | if chExists 57 | H(:,iCh,:) = this.girf(:,:,chInd); 58 | else 59 | error('Input channel %s cannot be found',predChannels{iCh}) 60 | end 61 | end 62 | dfH = this.df; 63 | dtH = this.dt; 64 | 65 | % Set length of prediction 66 | TH = 1/dfH; 67 | TO = max(tIn(end),tOut(end)) - min(tIn(1),tOut(1)); 68 | % TZF = min(TH,TO); 69 | 70 | % Zero-fill input at start and end to avoid aliasing 71 | nsIn = size(gIn,1); 72 | dtIn = tIn(2) - tIn(1); 73 | nZFMin = ceil(TH/dtIn); 74 | [dtR1,dtR2] = rat(dtIn/dtH); 75 | dtN = dtR1*dtR2; 76 | nZFTot = ceil((nZFMin+nsIn)/dtN)*dtN-nsIn; 77 | nZFneg = floor(nZFTot/2); 78 | nZFpos = ceil(nZFTot/2); 79 | gIn = [zeros(nZFneg,nIn,nIl); gIn; zeros(nZFpos,nIn,nIl)]; 80 | tIn = [tIn(1)+dtIn*(-nZFneg:-1)'; tIn; tIn(end)+dtIn*(1:nZFpos)']; 81 | fIn = time2freq(tIn); 82 | dfIn = fIn(2)-fIn(1); 83 | IN = fftshift(fft(gIn),1)*dtIn; 84 | nsIn = length(tIn); 85 | T_In = nsIn*dtIn; 86 | 87 | % Zero-fill H in time domain to matching T_in 88 | hTime = real(ifft(ifftshift(H,1))); 89 | tStart = 1e-3; % impulse response startup time to use 90 | tStartInd = ceil(tStart/dtIn); 91 | nZFHT = round(T_In/dtH) - size(hTime,1); 92 | hTime = [hTime(1:tStartInd,:,:); zeros(nZFHT,nIn,nOut); hTime(tStartInd+1:end,:,:)]; 93 | H = fftshift(fft(hTime),1); 94 | nsH = size(hTime,1); 95 | dfH = 1/(nsH*dtH); 96 | fH = dfH*((1:nsH)' - (floor(nsH/2)+1)); 97 | 98 | % Zero-fill H or IN to matching bandwidth 99 | if max(fIn) - max(fH) > dfH 100 | nZFHneg = ceil((fH(1) - fIn(1)))/dfH; 101 | nZFHpos = ceil((fIn(end)-fH(end)))/dfH; 102 | H = [zeros(nZFHneg,nIn,nOut); H; zeros(nZFHpos,nIn,nOut)]; 103 | nsH = size(H,1); 104 | fH = dfH*((1:nsH)' - floor(nsH/2)+1); 105 | dtH = 1/(nsH*dfH); 106 | elseif max(fH) - max(fIn) > dfH 107 | dfIn = fIn(2)-fIn(1); 108 | nZFHneg = ceil((fIn(1) - fH(1)))/dfIn; 109 | nZFHpos = ceil((fH(end)-fIn(end)))/dfIn; 110 | IN = [zeros(nZFHneg,nIn,nOut); IN; zeros(nZFHpos,nIn,nOut)]; 111 | nsIn = size(IN,1); 112 | fIn = dfIn*((1:nsIn)' - floor(nsIn/2)+1); 113 | dtIn = 1/(nsIn*dfIn); 114 | end 115 | 116 | % Interpolate H if needed 117 | if abs((dfH-dfIn)/dfH)>1e-6 || abs((dtH-dtIn)/dtH)>1e-6 118 | H = interp1(fH,H,fIn); 119 | H(isnan(H)) = 0; 120 | end 121 | 122 | OUT = zeros(nsIn, nOut, nIl); 123 | for iIl = 1:nIl 124 | OUT(:,:,iIl) = sum(repmat(IN(:,:,iIl),[1 1 nOut]).*H,2); 125 | end 126 | gOut = ifft(ifftshift(OUT,1))/dtIn; 127 | gOut = real(gOut); 128 | 129 | % Integrate gradients to k-coefficients 130 | gamma = this.gamma1H; 131 | kOut = cumsum(gOut)*dtIn*gamma; 132 | 133 | % Optionally interpolate output onto new grid 134 | dtOut = tOut(2) - tOut(1); 135 | gOut = interp1(tIn, gOut, tOut); 136 | kOut = interp1(tIn + dtIn/2, kOut, tOut + dtOut/2); 137 | gOut(isnan(gOut)) = 0; 138 | kOut(isnan(kOut)) = 0; 139 | 140 | tK = tOut + dtOut/2; 141 | 142 | 143 | 144 | 145 | 146 | 147 | -------------------------------------------------------------------------------- /classes/@GirfEssential/ConvertDomain.m: -------------------------------------------------------------------------------- 1 | function ConvertDomain(this,domain) 2 | %Convert frequency-domain GIRF to time domain and vice versa 3 | % 4 | % IN 5 | % domain Specify conversion domains {'freq2time' 'time2freq'} 6 | % Default is determined by this.isFreqDomainGirf 7 | % 8 | % OUT 9 | % 10 | % EXAMPLE 11 | % girf.ConvertDomain(); 12 | % girf.ConvertDomain(freq2time); 13 | % 14 | % See also GirfEssential 15 | % 16 | % Author: Johanna Vannesjo (johanna.vannesjo@gmail.com) 17 | % Copyright (C) 2014 IBT, University of Zurich and ETH Zurich, 18 | % 2016 FMRIB centre, University of Oxford 19 | % 20 | % This file is part of a code package for GIRF computation and application. 21 | % The package is available under a BSD 3-clause license. Further info see: 22 | % https://github.com/MRI-gradient/girf 23 | % 24 | 25 | 26 | if nargin < 2 27 | if this.isFreqDomainGirf 28 | domain = 'freq2time'; 29 | else 30 | domain = 'time2freq'; 31 | end 32 | end 33 | switch domain 34 | case {'freq2time' 'f2t'} 35 | % Convert from frequency to time domain 36 | this.time = time2freq(this.freq); 37 | this.girfTime = real(fftshift(ifft(ifftshift(this.girf,1)),1)); 38 | case {'time2freq' 't2f'} 39 | % Convert from time to frequency domain 40 | this.freq = time2freq(this.time); 41 | this.girf = fftshift(fft(ifftshift(this.girfTime,1)),1); 42 | end 43 | end 44 | -------------------------------------------------------------------------------- /classes/@GirfEssential/GirfEssential.m: -------------------------------------------------------------------------------- 1 | classdef GirfEssential < matlab.mixin.Copyable 2 | %Holds GIRF data in frequency/and or time domain 3 | % 4 | % 5 | % EXAMPLE 6 | % girf = GirfEssential(); 7 | % girf = GirfEssential(girf,freq,1,inChannels,outBasis); 8 | % girf = GirfEssential(girfTime,time,0,inChannels,outBasis); 9 | % 10 | % See also GirfProvider, GirfApplier 11 | % 12 | % Authors: Johanna Vannesjo (johanna.vannesjo@gmail.com), 13 | % Jennifer Nussbaum, Lars Kasper 14 | % Copyright (C) 2014 IBT, University of Zurich and ETH Zurich, 15 | % 2016 FMRIB centre, University of Oxford 16 | % 17 | % This file is part of a code package for GIRF computation and application. 18 | % The package is available under a BSD 3-clause license. Further info see: 19 | % https://github.com/MRI-gradient/girf 20 | % 21 | 22 | 23 | properties 24 | % is GIRF calculated in freq or time domain? 25 | isFreqDomainGirf; 26 | % Input gradient and shim channel names 27 | inChannels; 28 | % Output field basis 29 | outBasis; 30 | 31 | % [nSamples x nOutBasis x nInChannels] Calculated GIRF (self- and crossterms) 32 | girf; 33 | % [nSamples] frequency vector for GIRF 34 | freq; 35 | 36 | % [nSamples x nOutBasis x nInChannels] GIRF in the time domain 37 | girfTime; 38 | % [nSamples] time vector 39 | time; 40 | 41 | end % properties 42 | 43 | properties (Dependent = true, SetAccess = private) 44 | % [nInChannels] Self-term basis function for each input gradient/shim channel 45 | selfBasis; 46 | 47 | % nominal frequency resolution of GIRF 48 | df; 49 | % time resolution of GIRF 50 | dt; 51 | end % dependent properties 52 | 53 | methods 54 | 55 | % Constructor of class 56 | function this = GirfEssential(data,vect,isFreq,inChannels,outBasis) 57 | if nargin > 0 58 | this.isFreqDomainGirf = isFreq; 59 | if isFreq 60 | this.girf = data; 61 | this.freq = vect; 62 | else 63 | this.girfTime = data; 64 | this.time = vect; 65 | end 66 | this.ConvertDomain(); 67 | if nargin > 3 68 | this.inChannels = inChannels; 69 | end 70 | if nargin > 4 71 | this.outBasis = outBasis; 72 | end 73 | end 74 | end 75 | 76 | % Get methods for dependent properties 77 | function selfBasis = get.selfBasis(this) 78 | % TODO - use defined enumeration instead!!! 79 | selfBasis = zeros(1,length(this.inChannels)); 80 | for iIn = 1:length(this.inChannels) 81 | switch this.inChannels{iIn} 82 | case {'Z0' 'B0'} 83 | selfBasis(iIn) = 1; 84 | case {'X'} 85 | selfBasis(iIn) = 2; 86 | case {'Y'} 87 | selfBasis(iIn) = 3; 88 | case {'Z'} 89 | selfBasis(iIn) = 4; 90 | end 91 | end 92 | end 93 | function df = get.df(this) 94 | if isempty(this.freq) 95 | this.freq = time2freq(this.time); 96 | end 97 | df = this.freq(2)-this.freq(1); 98 | end 99 | function dt = get.dt(this) 100 | if isempty(this.time) 101 | this.time = time2freq(this.freq); 102 | end 103 | dt = this.time(2)-this.time(1); 104 | end 105 | 106 | end % methods 107 | 108 | end 109 | -------------------------------------------------------------------------------- /classes/@GirfEssential/Load.m: -------------------------------------------------------------------------------- 1 | function Load(this, filename) 2 | %Function to load girf data from file into GirfEssential object 3 | % 4 | % IN 5 | % filename Name of file to load data from 6 | % 7 | % OUT 8 | % 9 | % EXAMPLE 10 | % girfE.Load(mySavedGirfFilename); 11 | % 12 | % See also GirfEssential 13 | % 14 | % Author: Johanna Vannesjo (johanna.vannesjo@gmail.com) 15 | % Copyright (C) 2014 IBT, University of Zurich and ETH Zurich, 16 | % 2016 FMRIB centre, University of Oxford 17 | % 18 | % This file is part of a code package for GIRF computation and application. 19 | % The package is available under a BSD 3-clause license. Further info see: 20 | % https://github.com/MRI-gradient/girf 21 | % 22 | 23 | 24 | if ~exist(filename,'file') 25 | error('Could not find file: %s',filename) 26 | else 27 | in = load(filename); 28 | loadedVars = fieldnames(in); 29 | classProps = properties(this); 30 | for iV = 1:length(classProps) 31 | if ismember(classProps{iV},loadedVars) 32 | mp = findprop(this,classProps{iV}); 33 | if ~mp.Dependent 34 | this.(classProps{iV}) = in.(classProps{iV}); 35 | end 36 | end 37 | end 38 | end 39 | -------------------------------------------------------------------------------- /classes/@GirfEssential/Save.m: -------------------------------------------------------------------------------- 1 | function Save(this, filename, overwrite) 2 | %Function to save data in GirfEssential object to file 3 | % 4 | % IN 5 | % filename Name of file to save data into 6 | % overwrite Overwrite existing file? 7 | % 8 | % OUT 9 | % 10 | % EXAMPLE 11 | % girfE.Save(mySaveGirfFilename); 12 | % 13 | % See also GirfEssential 14 | % 15 | % Author: Johanna Vannesjo (johanna.vannesjo@gmail.com) 16 | % Copyright (C) 2014 IBT, University of Zurich and ETH Zurich, 17 | % 2016 FMRIB centre, University of Oxford 18 | % 19 | % This file is part of a code package for GIRF computation and application. 20 | % The package is available under a BSD 3-clause license. Further info see: 21 | % https://github.com/MRI-gradient/girf 22 | % 23 | 24 | 25 | if nargin < 3 26 | overwrite = 0; 27 | end 28 | 29 | if exist(filename,'file') 30 | if ~overwrite 31 | warning('Already existing file: %s, not saving girf!!',filename) 32 | return 33 | end 34 | end 35 | 36 | classProps = properties(this); 37 | firstSave = 1; 38 | for iV = 1:length(classProps) 39 | mp = findprop(this,classProps{iV}); 40 | if ~mp.Dependent 41 | eval([classProps{iV} ' = this.' classProps{iV} ';']); 42 | if firstSave == 1 43 | save(filename,classProps{iV}); 44 | firstSave = 0; 45 | else 46 | save(filename,classProps{iV},'-append'); 47 | end 48 | end 49 | end 50 | end 51 | -------------------------------------------------------------------------------- /classes/@GirfProvider/ComputeGirf.m: -------------------------------------------------------------------------------- 1 | function [girf, freq] = ComputeGirf(this) 2 | % Performs the GIRF calculation in the frequency domain 3 | % 4 | % [girf,freq] = girf.ComputeGirf(); 5 | % 6 | % IN 7 | % timeIn [nSamples x 1] input time vector 8 | % in [nSamples x nInChannels x nWaveforms] input gradient/shim 9 | % waveforms 10 | % timeOut [nSamples x 1] output time vector (must be same as input) 11 | % out [nSamples x nOutBasis x nWaveforms] measured output 12 | % gradient/shim waveforms 13 | % 14 | % OUT 15 | % freq [nSamples x 1] girf frequency vector 16 | % girf [nSamples x nOutBasis x nInChannels] calculated girf 17 | % 18 | % Author: Johanna Vannesjo (johanna.vannesjo@gmail.com) 19 | % Copyright (C) 2014 IBT, University of Zurich and ETH Zurich, 20 | % 2016 FMRIB centre, University of Oxford 21 | % 22 | % This file is part of a code package for GIRF computation and application. 23 | % The package is available under a BSD 3-clause license. Further info see: 24 | % https://github.com/MRI-gradient/girf 25 | % 26 | 27 | 28 | fprintf('Starting GIRF calculation...') 29 | ticGC = tic; 30 | 31 | % Check data consistency 32 | if size(this.timeIn,1) ~= size(this.timeOut,1) 33 | error('Resample input and output waveforms onto same time grid before girf calculation!!') 34 | end 35 | 36 | % Get data size 37 | nS = length(this.timeOut); 38 | nIn = size(this.in,2); 39 | nOut = size(this.out,2); 40 | nWaveforms = size(this.in,3); 41 | 42 | % Get frequency domain input and output waveforms 43 | freq = time2freq(this.timeOut(:,1)); 44 | inFreq = this.inFreq; 45 | outFreq = this.outFreq; 46 | 47 | % Find out whether each waveform uses only one inputchannel 48 | if nIn >1 49 | inWaveforms = zeros(nIn, nWaveforms); 50 | for inCh = 1: nIn 51 | for w = 1: nWaveforms 52 | if numel(find(inFreq(:,inCh,w))) > 4 % non-zero input 53 | inWaveforms(inCh,w) = 1; % there is input in this channel and this waveform 54 | end 55 | end 56 | end 57 | useMatrixCalculation = max(sum(inWaveforms,1)); % if more than 1 inputchannel is used in this waveform (max > 1) 58 | % matrix calculation needed, else each waveform has only one driving channel 59 | end 60 | % Perform least-squares estimation from inputs 61 | girf = zeros(nS,nOut,nIn); 62 | if nIn == 1 63 | inSOSInv = 1./sum(abs(inFreq).^2,3); 64 | for iOut = 1:nOut 65 | girf(:,iOut) = sum(outFreq(:,iOut,:).*conj(inFreq),3).*inSOSInv; 66 | end 67 | elseif useMatrixCalculation ==1 % only one driving channel for each waveform 68 | for iIn = 1:3 69 | indexWaveforms = find(inWaveforms(iIn,:)==1); % get waveforms which use this input channel 70 | inSOSInv = 1./sum(abs(inFreq(:,iIn,indexWaveforms)).^2,3); 71 | for iOut = 1:nOut 72 | girf(:,iOut,iIn) = sum(outFreq(:,iOut,indexWaveforms).*conj(inFreq(:,iIn,indexWaveforms)),3).*inSOSInv; 73 | end 74 | end 75 | else % matrix calculation, more than one inputchannel for one waveform 76 | for iF = 1:nS 77 | girf(iF,:,:) = outFreq(iF,:,:)*pinv(inFreq(iF,:,:)); 78 | end 79 | end 80 | 81 | 82 | girf(isnan(girf)) = 0; 83 | 84 | timeGC = toc(ticGC); 85 | fprintf(' done after %f seconds! \n', timeGC) 86 | 87 | % Set class properties 88 | this.isFreqDomainGirf = 1; 89 | this.girf = girf; 90 | this.freq = freq; 91 | this.ConvertDomain(); % convert to time domain as well 92 | end 93 | 94 | -------------------------------------------------------------------------------- /classes/@GirfProvider/GirfProvider.m: -------------------------------------------------------------------------------- 1 | classdef GirfProvider < GirfEssential 2 | % Class for handling GIRF computation 3 | % 4 | % This class handles all parameteres related to the calculation of a 5 | % measured Gradient or Shim Impulse Response Function. 6 | % 7 | % EXAMPLES: 8 | % girf = GirfProvider(); 9 | % girf = GirfProvider(myGirfEssential); 10 | % girf = GirfProvider(timeIn,in,timeOut,out); 11 | % girf = GirfProvider(timeIn,in,timeOut,out,inChannels,outBasis); 12 | % 13 | % Authors: Johanna Vannesjo (johanna.vannesjo@gmail.com), 14 | % Jennifer Nussbaum, Lars Kasper 15 | % Copyright (C) 2014 IBT, University of Zurich and ETH Zurich, 16 | % 2016 FMRIB centre, University of Oxford 17 | % 18 | % This file is part of a code package for GIRF computation and application. 19 | % The package is available under a BSD 3-clause license. Further info see: 20 | % https://github.com/MRI-gradient/girf 21 | % 22 | 23 | 24 | properties 25 | 26 | %%%% GIRF computation variables %%%%%%%%%%%%%%%%%%%% 27 | in; % [nSamples x nInChannels x nWaveforms] Input waveforms to gradient/shim channels in the time domain 28 | timeIn; % [nSamples x nWaveforms] time vector for input gradients 29 | 30 | out; % [nSamples x nOutBasis x nWaveforms] measured gradient/shim fields in the time domain 31 | timeOut; % [nSamples x nWaveforms] time vector for measured fields 32 | 33 | %%%% Flags and status %%%%%%%%%%%%%%%% 34 | resampleMethod; 35 | 36 | filterInfo; % Info about filtering that has been performed 37 | 38 | end 39 | properties (Dependent = true, SetAccess = private) 40 | inFreq; % system inputs in the frequency domain 41 | outFreq; % measured gradient/shim fields in the frequency domain 42 | 43 | dtIn; % gradient input sampling interval 44 | dtOut; % sampling interval of measured output 45 | end 46 | methods 47 | % Class constructor 48 | function this = GirfProvider(timeIn,in,timeOut,out,inChannels,outBasis) 49 | if nargin > 0 % Allow for empty constructor 50 | % Check consistency of data size 51 | if size(in,3) ~= size(out,3) 52 | error('Number of input and output waveforms must match') 53 | end 54 | if size(in,1) ~= size(timeIn,1) 55 | error('Number of samples in input waveform and input time vector must match') 56 | end 57 | if size(out,1) ~= size(timeOut,1) 58 | error('Number of samples in output waveform and output time vector must match') 59 | end 60 | 61 | % Fill up class properties from arguments 62 | this.timeIn = timeIn; 63 | this.in = in; 64 | this.timeOut = timeOut; 65 | this.out = out; 66 | 67 | % Optionally fill up channel information properties 68 | if nargin > 4 69 | if length(inChannels) ~= size(in,2) 70 | warning('Different number of input channels specified in input waveform and input channel information') 71 | end 72 | this.inChannels = inChannels; 73 | end 74 | if nargin > 5 75 | if length(outBasis) ~= size(out,2) 76 | warning('Different number of output basis terms specified in output waveform and output basis information') 77 | end 78 | this.outBasis = outBasis; 79 | end 80 | % if nargin > 6 81 | % if length(selfBasis) ~= size(in,2) || max(selfBasis) > size(out,2) 82 | % warning('Check consistency of self-term basis info!') 83 | % end 84 | % this.selfBasis = selfBasis; 85 | % end 86 | end 87 | end % Constructor 88 | 89 | % Get methods for dependent properties 90 | function inFreq = get.inFreq(this) 91 | inFreq = fftshift(fft(this.in),1); 92 | end 93 | function outFreq = get.outFreq(this) 94 | outFreq = fftshift(fft(this.out),1); 95 | end 96 | function dtIn = get.dtIn(this) 97 | dtIn = this.timeIn(2)-this.timeIn(1); 98 | end 99 | function dtOut = get.dtOut(this) 100 | dtOut = this.timeOut(2)-this.timeOut(1); 101 | end 102 | 103 | % Return GirfEssential object 104 | function girfEssential = GetGirfEssential(this) 105 | girfEssential = GirfEssential(); 106 | 107 | girfEssential.isFreqDomainGirf = this.isFreqDomainGirf; 108 | girfEssential.inChannels = this.inChannels; 109 | girfEssential.outBasis = this.outBasis; 110 | 111 | girfEssential.girf = this.girf; 112 | girfEssential.freq = this.freq; 113 | 114 | girfEssential.girfTime = this.girfTime; 115 | girfEssential.time = this.time; 116 | end 117 | end 118 | end -------------------------------------------------------------------------------- /classes/@GirfProvider/PeakElimination.m: -------------------------------------------------------------------------------- 1 | function PeakElimination(this, fCenter, fWidth, fReps, nFit) 2 | % Interpolates values around artefactual peaks in calculated GIRF 3 | % 4 | % USE 5 | % PeakElimination(fCenter,fWidth,reps) 6 | % 7 | % IN 8 | % fCenter [Hz] Center of artefactual peak 9 | % fWidth [Hz] Width of peak to be interpolated 10 | % fReps [Hz] How many peak harmonics to interpolate (default: 1) 11 | % nFit Polynomial order of fit (default: 1) 12 | % 13 | % OUT 14 | % GIRF [nr_samples nr_k] GIRF after peak interpolation 15 | % 16 | % Author: Johanna Vannesjo (johanna.vannesjo@gmail.com) 17 | % Copyright (C) 2014 IBT, University of Zurich and ETH Zurich, 18 | % 2016 FMRIB centre, University of Oxford 19 | % 20 | % This file is part of a code package for GIRF computation and application. 21 | % The package is available under a BSD 3-clause license. Further info see: 22 | % https://github.com/MRI-gradient/girf 23 | % 24 | 25 | 26 | fprintf('Starting peak interpolation...') 27 | ticVS = tic; 28 | 29 | % Convert GIRF if not already in frequency domain 30 | if isempty(this.girf) && ~isempty(this.girfTime) 31 | this.ConvertDomain('time2freq'); 32 | end 33 | 34 | %% Check input parameters 35 | girf = this.girf; 36 | f = this.freq; 37 | if abs(fCenter) > max(abs(f)) 38 | warning('Peak center frequency too high - no peak interpolation performed') 39 | return 40 | end 41 | if nargin < 4 42 | fReps = 1; 43 | end 44 | if nargin < 5 45 | nFit = 1; 46 | end 47 | 48 | %% 49 | nS = length(f); 50 | nC = floor(nS/2)+1; 51 | for iR = 1:fReps 52 | indsPeak = find(abs(f-iR*fCenter)size(k_data,1) 50 | k_data = k_data.'; 51 | end 52 | if size(t,2)>size(t,1) 53 | t = t.'; 54 | end 55 | if size(t,1) ~= size(k_data,1) 56 | error('Number of samples in the input data and time vector must agree') 57 | end 58 | 59 | if max(max(abs(imag(k_data))))==0 60 | is_real = 1; 61 | else 62 | is_real = 0; 63 | end 64 | 65 | %% shift data to fourier domain center by subtractig its linear phase 66 | %% (haeb) 67 | if ~is_real; 68 | linear_phase = zeros(size(k_data)); 69 | for cnt1 = 1:size(k_data,2) 70 | for cnt2 = 1:size(k_data,3) 71 | tmp = squeeze(unwrap(angle(k_data(:,cnt1,cnt2)))); 72 | b = regress(tmp, [ones(size(t(:,1))) t(:,1)]); 73 | % omega(:,cnt1,cnt2) = b(2); 74 | linear_phase(:,cnt1,cnt2) = b(2).*t(:,1)'; 75 | end 76 | end 77 | k_data = k_data .* exp(-1i*linear_phase); 78 | end 79 | 80 | %% make data an even function for DFT (haeb) 81 | k_data = [flipud(k_data); k_data]; 82 | 83 | %% transform data to fourier domain 84 | k_data = ifftshift(fft(fftshift(k_data,1)),1); 85 | dt = t(2)-t(1); 86 | nrs = size(k_data,1); 87 | df = 1/(nrs*dt); 88 | f = ([0:nrs-1]'-floor(nrs/2))*df; 89 | 90 | %% apply the filter in frequency space 91 | k_data = BW_window(k_data, f, BW, filterType, beta); 92 | 93 | %% transform data back to time domain 94 | k_data = ifftshift(ifft(fftshift(k_data,1)),1); 95 | 96 | %% remove symmetric part again 97 | k_data = k_data(size(k_data,1)/2+1:end,:,:); 98 | 99 | %% undo phase removal 100 | if ~is_real, k_data = k_data .* exp(1i*linear_phase); end 101 | 102 | %% remove falsely introduced imaginary parts 103 | if is_real, k_data = real(k_data); end 104 | 105 | 106 | 107 | -------------------------------------------------------------------------------- /utils/BW_window.m: -------------------------------------------------------------------------------- 1 | function [array, filter] = BW_window(array, f, BW, filterType, beta) 2 | % Applies selected bandwidth window to input array 3 | % 4 | % The Function BW_window applies a frequency-domain windowing to the data. 5 | % The filter BW corresponds to the FWHM. Available filters are Gaussian, 6 | % Blackman, Blackmanharris and Raised cosine. 7 | % 8 | % IN: 9 | % array [nr_samples x nr_orders] data to be windowed 10 | % f [nr_samples x 1] in [Hz] frequency vector of the array 11 | % BW [scalar] in [Hz] bandwidth at FWHM of the filter 12 | % filterType {'ga' 'bl' 'bh' 'rc'} the filter type (gaussian, blackman, blackman-harris or raised cosine) 13 | % beta [scalar] roll-off factor for raised cosine filter 14 | % 15 | % OUT: 16 | % array [nr_samples x nr_orders] windowed data 17 | % filter [nr_samples x 1] calculated filter 18 | % 19 | % 20 | % Authors: Johanna Vannesjo (johanna.vannesjo@gmail.com), 21 | % Copyright (C) 2014 IBT, University of Zurich and ETH Zurich, 22 | % 2016 FMRIB centre, University of Oxford 23 | % 24 | % This file is part of a code package for GIRF computation and application. 25 | % The package is available under a BSD 3-clause license. Further info see: 26 | % https://github.com/MRI-gradient/girf 27 | % 28 | 29 | 30 | %% Return if no filtering desired 31 | if isempty(BW) || BW==0 32 | return 33 | end 34 | 35 | %% Internal input 36 | % use the Blackman-Harris filter if not otherwise specified 37 | if nargin == 3 38 | filterType = 'bh'; 39 | end 40 | 41 | %% check and adjust format of input data 42 | if ismatrix(array) && size(array,2)>size(array,1) 43 | array = array.'; 44 | end 45 | 46 | if size(f,2)>size(f,1) 47 | f = f.'; 48 | end 49 | if size(f,1) ~= size(array,1) 50 | error('Number of samples in the input data and frequency vector must agree') 51 | end 52 | 53 | %% compute the frequency filter 54 | df = f(2)-f(1); 55 | cs = floor(length(f)/2)+1; 56 | filter = zeros(size(f)); 57 | switch filterType 58 | case 'ga' %%% Gaussian filter 59 | filter = exp(-((f/(BW/2)).^2)*log(2)); 60 | %disp(['-> a gaussian filter is used']) 61 | case 'rc' %%% Raised cosine filter 62 | filter = raised_cosine(f,1/BW,beta); 63 | case 'bl' %%% Blackman filter [FWHM = 405.1 out of 1000] 64 | ns = floor(BW/df*1000/405.1/2); 65 | b = blackman(2*ns+1); % force odd number of samples 66 | if length(b) <= length(f) 67 | filter(cs-ns:cs+ns) = b; 68 | else 69 | bi = (1:length(f))-cs+ns+1; 70 | filter = b(bi); 71 | warning('the chosen filter-BW is too wide for the Blackman filter') 72 | end 73 | %disp(['-> a Blackman filter is used']) 74 | case 'bh' %%% Blackman-Harris filter [FWHM = 342.8 out of 1000] 75 | ns = floor(BW/df*1000/342.8/2); 76 | bh = blackmanharris(2*ns+1); % force odd number of samples 77 | if length(bh) <= length(f) 78 | filter(cs-ns:cs+ns) = bh; 79 | else 80 | bi = (1:length(f))-cs+ns+1; 81 | filter = bh(bi); 82 | warning('the chosen filter-BW is too wide for the Blackman-Harris filter') 83 | end 84 | %disp(['-> a Blackman-Harris filter is used']) 85 | end 86 | 87 | %% apply the filter in frequency space 88 | array = array.*repmat(filter,[1 size(array,2) size(array,3)]); 89 | 90 | 91 | -------------------------------------------------------------------------------- /utils/CenteredPhase.m: -------------------------------------------------------------------------------- 1 | function phase = CenteredPhase(array) 2 | % Function to compute unwrapped phase of complex vector, with zero phase at zero 3 | % frequency (assuming fftshift) 4 | % 5 | % USE 6 | % phase = CenteredPhase(array) 7 | % 8 | % IN 9 | % array [n_samples x n_columns] complex-valued array 10 | % 11 | % OUT 12 | % phase [n_samples x n_columns] phase of array 13 | % 14 | % Author: Johanna Vannesjo (johanna.vannesjo@gmail.com) 15 | % Copyright (C) 2014 IBT, University of Zurich and ETH Zurich, 16 | % 2016 FMRIB centre, University of Oxford 17 | % 18 | % This file is part of a code package for GIRF computation and application. 19 | % The package is available under a BSD 3-clause license. Further info see: 20 | % https://github.com/MRI-gradient/girf 21 | % 22 | 23 | phase = unwrap(angle(array)); 24 | middle = floor(length(array)/2) + 1; 25 | phase = phase - repmat(phase(middle,:),length(phase),1); 26 | -------------------------------------------------------------------------------- /utils/CenteredTime.m: -------------------------------------------------------------------------------- 1 | function t = CenteredTime(dt, nrs) 2 | % Function to compute time vector, with zero at center (assuming fftshift) 3 | % 4 | % USE 5 | % t = CenteredTime(dt, nrs) 6 | % 7 | % IN 8 | % dt sampling interval 9 | % nrs number of samples 10 | % 11 | % OUT 12 | % t time vector, centered at zero (assuming fftshift) 13 | % 14 | % Author: Johanna Vannesjo (johanna.vannesjo@gmail.com) 15 | % Copyright (C) 2014 IBT, University of Zurich and ETH Zurich, 16 | % 2016 FMRIB centre, University of Oxford 17 | % 18 | % This file is part of a code package for GIRF computation and application. 19 | % The package is available under a BSD 3-clause license. Further info see: 20 | % https://github.com/MRI-gradient/girf 21 | % 22 | 23 | t = 0:dt:dt*(nrs-1); 24 | cs = floor(nrs/2) + 1; 25 | t = t-t(cs); 26 | -------------------------------------------------------------------------------- /utils/ComputeInputs.m: -------------------------------------------------------------------------------- 1 | function inputs = ComputeInputs(dtIn, tOut, params, pulseType, ipType) 2 | % Function to compute set of blip inputs 3 | % 4 | % Author: Johanna Vannesjo (johanna.vannesjo@gmail.com) 5 | % Copyright (C) 2014 IBT, University of Zurich and ETH Zurich, 6 | % 2016 FMRIB centre, University of Oxford 7 | % 8 | % This file is part of a code package for GIRF computation and application. 9 | % The package is available under a BSD 3-clause license. Further info see: 10 | % https://github.com/MRI-gradient/girf 11 | % 12 | 13 | % Check type of input pulse 14 | if isempty(pulseType) || ~iscell(pulseType) 15 | ex1 = ' girfo.pulseType = {''blips''}'; 16 | ex2 = ' girfo.pulseType = {''sweeps''}'; 17 | ex3 = ' girfo.pulseType = {''blips'' ''sweeps''}'; 18 | error('Please specify input pulse type as cell object, examples: \n %s \n %s \n %s',ex1,ex2,ex3) 19 | end 20 | 21 | inputsAll = []; 22 | for iP = 1:length(pulseType) 23 | switch pulseType{iP}; 24 | case {'blips' 'blip' 'Blips' 'Blip'} 25 | isBlips = 1; isSweeps = 0; 26 | case {'sweeps' 'sweep' 'Sweeps' 'Sweep'} 27 | isSweeps = 1; isBlips = 0; 28 | end 29 | 30 | % Set blip parameters 31 | if isBlips 32 | nPulses = length(params.dur); 33 | if params.fixedSlope 34 | params.amp = params.fixedSlope*params.dur; 35 | end 36 | if length(params.t0)= t0 & tOut <= t0+pulseDur); 99 | indsIn = ceil((tOut(indsOut)-t0)/dtIn)+1; 100 | inputs(indsOut,iIn) = pulseIn(indsIn); 101 | elseif strcmp(ipType,'interpolated') % calculate on input raster & do linear interpolation onto output raster 102 | indsOut = find(tOut >= tIn(1)+ t0 & tOut <= tIn(end)+ t0); 103 | inputs(indsOut,iIn) = interp1(tIn+t0,pulseIn,tOut(indsOut)); 104 | end 105 | end 106 | end 107 | % Concatenate all input pulses 108 | inputsAll = [inputsAll inputs]; 109 | end 110 | 111 | inputs = inputsAll; 112 | 113 | end 114 | 115 | 116 | -------------------------------------------------------------------------------- /utils/VariableSmoothing.m: -------------------------------------------------------------------------------- 1 | function [SIRF, fs, fMax, BWrange, vsBW] = VariableSmoothing(SIRF, f, fMax, BWrange, vsBW) 2 | % Performs frequency-dependent smoothing of GIRF/SIRF 3 | % 4 | % USE 5 | % [SIRF] = VariableSmoothing(SIRF, f, f2) 6 | % 7 | % IN 8 | % SIRF [nr_samples nr_k] SIRF matrix to be smoothed 9 | % f [nr_samples 1] frequency vector 10 | % fMax [Hz] maximum frequency to be smoothed & cut 11 | % BWrange [BWmin BWmax] [Hz] minimal and maximal bandwidth of smoothing kernel (optional) 12 | % vsBW [Hz] frequency region of narrower smoothing kernel (optional) 13 | % 14 | % OUT 15 | % SIRF [nr_samples nr_k] smoothed SIRF 16 | % fs [nr_samples 1] cut frequency vector 17 | % 18 | % Author: Johanna Vannesjo (johanna.vannesjo@gmail.com) 19 | % Copyright (C) 2014 IBT, University of Zurich and ETH Zurich, 20 | % 2016 FMRIB centre, University of Oxford 21 | % 22 | % This file is part of a code package for GIRF computation and application. 23 | % The package is available under a BSD 3-clause license. Further info see: 24 | % https://github.com/MRI-gradient/girf 25 | % 26 | 27 | fprintf('Starting variable smoothing...') 28 | ticVS = tic; 29 | 30 | %% Check input parameters 31 | if ~exist('BWrange','var') || isempty(BWrange) 32 | BW_Kmin = 0.05; % minimal bandwidth of smoothing kernel 33 | BW_Kmax = 10; % maximal bandwidth of smoothing kernel 34 | else 35 | BW_Kmin = BWrange(1); 36 | BW_Kmax = BWrange(2); 37 | end 38 | if ~exist('vsBW','var') || isempty(vsBW) 39 | vsBW = 40; % frequency region of narrower smoothing kernel 40 | end 41 | 42 | %% Create smoothing kernels (K) 43 | 44 | % Create time and frequency vector of kernel 45 | df = f(2)-f(1); 46 | Ts = 10/df; % sets oversampled frequency resolution of K (10 * T) 47 | dtk = 0.01/BW_Kmax; % sets oversampled time resolution of k (0.01 * dt) 48 | tk = CenteredTime(dtk,Ts/dtk); %-pb/2; 49 | fK = time2freq(tk); 50 | 51 | % Create parameters for time-domain window 52 | pb = 1/BW_Kmax; % flattop length in seconds 53 | beta = 1/3; % width of transition band 54 | TK = pb/(1-beta); % length of kernel 55 | BW = 1/TK; % FWHM bandwidth of kernel 56 | 57 | % Create kernel of widest bandwidth 58 | k = raised_cosine(tk, BW, beta).'; 59 | K = fftshift(fft(ifftshift(k))); 60 | K = K./max(abs(K)); % normalize in frequency domain 61 | 62 | % Cut freq-domain side lobes of kernel 63 | nSL = 3; % number of side lobes to include 64 | ind0 = find(abs(K)<1e-6); % find samples close to 0 65 | cs = ceil(length(ind0)/2); 66 | K = K(ind0(cs-nSL):ind0(cs+nSL+1)); 67 | fK = fK(ind0(cs-nSL):ind0(cs+nSL+1)); 68 | 69 | % Select SIRF frequency region to smooth 70 | csS = floor(size(SIRF,1)/2)+1; % center sample of SIRF 71 | nrf1 = fix(-fK(1)/df); % half-width of K within SIRF frequency vector 72 | nrf2 = fix(fK(end)/df); % half-width of K within SIRF frequency vector 73 | nrBW = floor(fMax/df); % SIRF region to smooth 74 | ind_SK = csS-nrf1:csS+nrBW+nrf2; % indices to smooth (only positive frequencies) 75 | f = f(ind_SK); 76 | SIRF = SIRF(ind_SK,:,:); 77 | 78 | % Create temporary to-be-smoothed SIRF vector 79 | nS = nrBW+1; 80 | nOut = size(SIRF, 2); 81 | nIn = size(SIRF,3); 82 | S_temp = zeros(nS,nOut,nIn); 83 | 84 | % Create scaled versions of kernel, to perform variable smoothing 85 | ft = f(1:1+nrf1+nrf2); 86 | vsr = BW_Kmax/BW_Kmin; % variable smoothing ratio 87 | sci = find(f 8 32 | AM = varargin{1}; 33 | else 34 | AM.type = 'none'; 35 | AM.slew = []; 36 | end 37 | if nargin > 9 38 | smoothing = varargin{2}; 39 | else 40 | smoothing.type = 'none'; 41 | end 42 | if nargin > 10 43 | slew = varargin{3}; 44 | else 45 | slew = []; 46 | end 47 | 48 | % Initialize sweep pulse 49 | % sweep = sin(phi0)*ones(size(t)); 50 | sweep = zeros(length(t),1); 51 | f_t = zeros(length(t),1); 52 | 53 | % Check if there is a wish to concatenate sweeps 54 | if iscell(type) 55 | n_segments = length(type); 56 | if n_segments > 1 57 | [sweep1, f_t1, phiEnd1] = sweeps(t, T_acq(1:end-1), f1, f2(1:end-1), phi0, 1, t_start, {type{(1:end-1)}}); 58 | sweep = sweep + sweep1; 59 | f_t = f_t + f_t1; 60 | f1 = f2(end-1); 61 | f2 = f2(end); 62 | phi0 = phiEnd1; 63 | t_start = t_start + sum(T_acq(1:end-1)); 64 | T_acq = T_acq(end); 65 | type = type{end}; 66 | else 67 | type = type{1}; 68 | end 69 | end 70 | 71 | % Single sweep calculation 72 | if ~ischar(type) 73 | n = type; 74 | else 75 | % Set parameters according to old chirp script 76 | switch type 77 | case {'beta' 'linear'} 78 | n = 2; 79 | case 'gamma' 80 | n = 3; 81 | case 'delta' 82 | n = 4; 83 | case 'betaAM' 84 | n = 2; 85 | AM.type = 'gaussian'; 86 | AM.A_max = 10; 87 | AM.width = 1; 88 | case 'gammaAM' 89 | n = 3; 90 | AM.type = 'gaussian'; 91 | AM.A_max = 10; 92 | AM.width = 1; 93 | case 'deltaAM' 94 | n = 4; 95 | AM.type = 'gaussian'; 96 | AM.A_max = 10; 97 | AM.width = 1; 98 | end 99 | end 100 | 101 | % shift time vector and find indices inside sweep 102 | t = t-t_start; 103 | t_inds = find(t>=0 & t<=T_acq); 104 | 105 | % calculate sweep 106 | beta = 2*pi*(f2-f1)./(n*T_acq.^(n-1)); 107 | sweep(t_inds) = sin( beta*t(t_inds).^n + 2*pi*f1*t(t_inds) + phi0); 108 | f_t(t_inds) = (n*beta/(2*pi))*t(t_inds).^(n-1) + f1; 109 | phiEnd = mod(beta*T_acq^n + 2*pi*f1*T_acq + phi0, 2*pi); 110 | % end 111 | 112 | % Scale sweep with possibly frequency-dependent amplitude 113 | AM_boost = ones(size(sweep)); 114 | switch AM.type 115 | case 'gaussian' 116 | AM_boost(t_inds) = (AM.A_max-1)*exp(-((f_t(t_inds)/AM.width).^2)) + 1; 117 | end 118 | 119 | AM_smooth = ones(size(sweep)); 120 | switch smoothing.type 121 | case 'erf' 122 | cf = f2 - smoothing.width/2; 123 | AM_smooth(t_inds) = 0.5 - 0.5*erf((f_t(t_inds)-cf)/(smoothing.width/3)); % Amplitude modulation for smoothing 124 | case 'time-defined' 125 | smooth_time = smoothing.width; 126 | smooth_ct = T_acq - smooth_time/2; 127 | AM_smooth(t_inds) = 0.5 - 0.5*erf((t(t_inds)-smooth_ct)/(smooth_time/3)); % Amplitude modulation for smoothing 128 | end 129 | 130 | A_t = A*AM_boost; 131 | if slew 132 | S_limit = abs(slew./(2*pi*f_t)); 133 | S_limit(isnan(S_limit)) = 0; 134 | A_t(t_inds) = sign(A)*min(abs(A_t(t_inds)),S_limit(t_inds)); 135 | end 136 | 137 | sweep = A_t.*AM_smooth.*sweep; 138 | % sweep = A_t.*sweep; 139 | if size(sweep,1) < size(sweep,2) 140 | sweep = sweep'; 141 | end 142 | 143 | 144 | 145 | 146 | 147 | -------------------------------------------------------------------------------- /utils/time2freq.m: -------------------------------------------------------------------------------- 1 | function [f, df, f_max] = time2freq(t) 2 | % Function to compute frequency vector corresponding to a time-vector 3 | % (assuming fftshift) 4 | % 5 | % USE 6 | % [f, df, f_max] = time2freq(t) 7 | % 8 | % IN 9 | % t [n_samples x 1] time vector [s] 10 | % 11 | % OUT 12 | % f [n_samples x 1] frequency vector [Hz] 13 | % df frequency resolution [Hz] 14 | % f_max bandwidth [Hz] 15 | % 16 | % Author: Johanna Vannesjo (johanna.vannesjo@gmail.com) 17 | % Copyright (C) 2014 IBT, University of Zurich and ETH Zurich, 18 | % 2016 FMRIB centre, University of Oxford 19 | % 20 | % This file is part of a code package for GIRF computation and application. 21 | % The package is available under a BSD 3-clause license. Further info see: 22 | % https://github.com/MRI-gradient/girf 23 | % 24 | 25 | nrs = length(t); 26 | dt = t(2)-t(1); 27 | f_max = 1/dt; 28 | df = f_max/nrs; 29 | f = ([0:nrs-1]'-floor(nrs/2))*df; 30 | 31 | -------------------------------------------------------------------------------- /utils/trapezoid.m: -------------------------------------------------------------------------------- 1 | function [grads] = trapezoid(t,ons,amp,dur,varargin) 2 | % Function to create trapezoidal pulse shapes from parameters 3 | % 4 | % USE 5 | % grads = trapezoid(t,ons,amp,dur,plateau,dur2) 6 | % 7 | % IN 8 | % t [n_samples x 1] time vector [s] 9 | % ons [1 x n_pulses] onset of pulse [s] 10 | % amp [1 x n_pulses] amplitude of pulse 11 | % dur [1 x n_pulses] duration of first slope 12 | % plateau [1 x n_puless] duration of pulse plateau (opt: varargin{1}) 13 | % dur2 [1 x n_pulses] duration of second slope (opt: varargin{2}) 14 | % 15 | % OUT 16 | % grads [n_samples x n_pulses] trapezoidal gradient pulse 17 | % 18 | % Author: Johanna Vannesjo (johanna.vannesjo@gmail.com) 19 | % Copyright (C) 2014 IBT, University of Zurich and ETH Zurich, 20 | % 2016 FMRIB centre, University of Oxford 21 | % 22 | % This file is part of a code package for GIRF computation and application. 23 | % The package is available under a BSD 3-clause license. Further info see: 24 | % https://github.com/MRI-gradient/girf 25 | % 26 | 27 | 28 | % Initialize parameters 29 | n_pulses = length(amp); % number of pulses 30 | ns = length(t); % number of samples 31 | if size(t,1)ons(iP),1,'first'); 49 | ts1 = find(t