Skip to content

Commit

Permalink
Merge branch 'develop'
Browse files Browse the repository at this point in the history
  • Loading branch information
Jon-Fredrik Nielsen committed Feb 24, 2024
2 parents f34b3db + d7df62a commit ec3eaa3
Show file tree
Hide file tree
Showing 6 changed files with 140 additions and 81 deletions.
179 changes: 106 additions & 73 deletions +toppe/+utils/coppe.m
Original file line number Diff line number Diff line change
@@ -1,97 +1,130 @@
function coppe(varargin)
% function coppe(varargin)
% Function for **internal** University of Michigan fMRI lab use only to
% send toppe files automatically to our scanners. Can be generalized for
% other sites but the server names need to be updated.
%
% A rough copy of the bash script called in this function (pushtoppefiles)
% that we have on lab servers that can access the scanner is the
% following:
%
% #!/bin/bash
% # Support file for copying toppe scan files to the scanner, then unpacking them
% set -e
% IP=$(getscannerip)
% echo "Copying files to scanner..."
% ssh sdc@$IP 'scp fmrilab@toro:~/toppe_utils/toppe-scanfiles.tgz /usr/g/bin/; cd /usr/g/bin/; tar -xzf toppe-scanfiles.tgz'
%
% Function for **internal** University of Michigan fMRI lab use only to
% send toppe files automatically to our scanners. Can be generalized for
% other sites but the server names need to be updated.
%
% Input options:
% target which scanner to copy pulse sequence to, UM options: 'inside', 'outside'
% use_pw option to allow command line input, in the case a pw is needed
% cv CV number, used to determing where on the scanner to put files
% cv CV number, used to determing where on the scanner to put files
% target which scanner to copy pulse sequence to, UM options: 'inside', 'outside'
% force option to force overwrite of existing sequences corresponding
% to cv (default is 0, no overwrite)
% user user name on server to use for file transfer (defaults to current user name)
% use_pw option to allow command line input, in the case a pw is needed
% (default is 0)
% dir target directory to write to under /srv/nfs/psd/usr/psd on scanner
% (default is [username]/toppe[cv #])
% version toppe software version (5 for tv5, 6 for tv6)
%
% Packages toppe files into toppe-scanfiles.tgz, then copies it to the scanner
% Assumes you have SSH keys set up to log into romero/toro

import toppe.utils.*

arg.target = 'inside'; % Default to inside scanner
arg.use_pw = false; % assume we have ssh keys setup to not need a pw
arg.cv = []; % if no CV given, it will coppe to /usr/g/bin
arg = vararg_pair(arg, varargin);
% Assumes you have the following SSH keys set up:
% 1. ssh key from host --> server
% 2. ssh key from scanner --> host
%
% if SSH keys require a passcode, set use_pw = 1 to show prompt
%

fprintf('Making archive...');
[status,cmdout] = system('tar czf toppe-scanfiles.tgz modules.txt seqstamp.txt scanloop.txt *.mod'); fprintf('done!\n');
import toppe.utils.*
basedir = '/srv/nfs/psd/usr/psd'; % base directory on scanner

% Set default arguments
arg.target = 'inside';
arg.force = 0;
[~,arg.user] = system('echo -n $USER');
arg.use_pw = 0;
arg.cv = [];
arg.dir = 'auto';
arg.version = 6; %Toppe version
arg = vararg_pair(arg, varargin);

if status
error(cmdout)
end
% set the default target directory
if strcmpi(arg.dir,'auto')
arg.dir = sprintf('%s/toppe%d', arg.user, arg.cv);
end

% Create string for calling bash script on server
if isempty(arg.cv)
script2call = 'pushtoppefiles';
else
script2call = ['pushtoppefiles ', num2str(arg.cv)];
end
% get the host IP address
[~,host_IP] = system('wget -qO- ifconfig.me/ip');

% zip the sequence using tar
fprintf('Zipping toppe files using tar...');
if(arg.version < 6)
[status,cmdout] = system('tar czf toppe-scanfiles.tgz modules.txt seqstamp.txt scanloop.txt *.mod');
else
[status,cmdout] = system('tar czf toppe-scanfiles.tgz modules.txt seqstamp.txt scanloop.txt cores.txt toppeN.entry *.mod');
end
if status
error(cmdout)
else
fprintf(' SUCCESS\n');
end

try
% set server names
switch arg.target
case 'inside'
server_str = 'romero';
homedir_str = '/export/home/fmrilab';
server_str = 'epyc';
case 'outside'
server_str = 'toro';
homedir_str = '/home/fmrilab';
server_str = 'goliath';
otherwise
fprintf('Invalid target, valid targets are ''inside'' or ''outside''.\n');
error('Invalid target, valid targets are ''inside'' or ''outside''.\n');
end

%% create linux commands with correct server target and run

cmd1 = ['scp toppe-scanfiles.tgz fmrilab@',server_str,':~/toppe_utils/'];
cmd2 = ['ssh -q fmrilab@',server_str,' ',homedir_str,'/toppe_utils/', script2call];

% 1. Send toppe files to server
fprintf(['Copying to ',server_str,'...']);
if ~arg.use_pw
[st1, cmdo1] = system(cmd1);
else
[st1, cmdo1] = system(cmd1,'-echo');
% Set up the commands to run on the scanner
cmd_scanner = [];
if ~arg.force
% check for existing entry file
cmd_scanner = sprintf('%s if [[ -f %s/pulseq/toppe%d.entry ]]; then exit 15; fi;', ...
cmd_scanner, basedir, arg.cv);
end
if st1, error(cmdo1); end; fprintf('done!\n');
% check if directory exists, if it doesn't, create it
cmd_scanner = sprintf('%s if [ ! -d %s/%s ]; then mkdir -p %s/%s; fi;', ...
cmd_scanner, basedir, arg.dir, basedir, arg.dir);
% cd into the directory
cmd_scanner = sprintf('%s if ! cd %s/%s; then exit 16; fi;', ...
cmd_scanner, basedir, arg.dir);
% scp toppe files from host
cmd_scanner = sprintf('%s if ! scp -q %s@%s:%s/toppe-scanfiles.tgz ./; then exit 17; fi;', ...
cmd_scanner, arg.user, host_IP, pwd);
% unzip the tar file
cmd_scanner = sprintf('%s if ! tar -xzf toppe-scanfiles.tgz; then exit 18; fi; rm toppe-scanfiles.tgz;', ...
cmd_scanner);
% rename the entry file and move it to pulseq directory
cmd_scanner = sprintf('%s if ! mv toppeN.entry %s/pulseq/v%d/toppe%d.entry; then exit 19; fi;', ...
cmd_scanner, basedir, arg.version,arg.cv);
% replace the first line with the right directory
cmd_scanner = sprintf('%s if ! sed -i "1s#.*#%s/%s/#" %s/pulseq/v%d/toppe%d.entry; then exit 20; fi;', ...
cmd_scanner, basedir, arg.dir, basedir, arg.version, arg.cv);

% ssh into server, then into scanner, and run the scanner commands using bash
cmd_host = sprintf('ssh -q %s@%s ssh -q sdc@10.0.1.1 /bin/bash << EOF\n%s\nEOF', ...
arg.user, server_str, cmd_scanner);

% 2. Send toppe files from server to scanner computer and untar
fprintf('Copying to scanner and untaring ...');
if ~arg.use_pw
[st2, cmdo2] = system(cmd2);
% use -echo to show the bash output (in case pw is required)
if arg.use_pw
eval_args = {cmd_host, '-echo'};
else
[st2, cmdo2] = system(cmd2,'-echo');
eval_args = {cmd_host};
end
if st2, error(cmdo2); end; fprintf('done!\n');


%% Find minimum number of slices
disp('Files sucessfully copied. Finding # of slices...');
scanloop_struc = importdata('scanloop.txt');
scanloop_struc_data = scanloop_struc.data;
max_sli = scanloop_struc_data(2);
fprintf('Set # of slices on scanner to %d or greater.\n',max_sli+1);

disp('Ready to scan.');
catch
fprintf('\n...failed. Not ready to scan.\n');

% Copy the file to the scanner and unzip
fprintf('Copying to scanner and unzipping...');
[out,msg] = system(eval_args{:});
switch out
case 15
error('cv number %d is already taken, use force argument to force overwrite', arg.cv);
case 16
error('failed to cd into %s/%s\noutput:\n%s', basedir, arg.dir, msg)
case 17
error('failed to scp from host to scanner, are your keys set up right?\noutput:\n%s', msg)
case 18
error('failed to unzip the tar file\noutput:\n%s', msg)
case 19
error('failed to move the entry file\noutput:\n%s', msg)
case 0
fprintf(' SUCCESS\nsequence files written to %s/%s\n', basedir, arg.dir);
otherwise
error('unknown error\noutput:\n%s',msg)
end

end


26 changes: 26 additions & 0 deletions +toppe/+utils/getk.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
function [kxo, kxe] = getk(sysGE, readoutFile, nfid, del)
% Get kspace sample locations from .mod file
%
% Inputs:
% sysGE see toppe.systemspecs()
% readoutFile .mod file name containing ADC window
% nfid number of acquired samples in ADC window
%
% Option:
% del offset by this many samples (default: 0)
%
% Outputs:
% kxo [1 nfid], odd echo k-space, cycles/cm
% kxe [1 nfid], even echo k-space, cycles/cm

if nargin < 4
del = 0.0;
end

[rf,gx,gy,gz,desc,paramsint16,paramsfloat,hdr] = toppe.readmod(readoutFile);
kx = sysGE.raster*1e-6*sysGE.gamma*1e-4*cumsum(gx); % cycles/cm
kx = kx - kx(end)/2;
kx = kx((hdr.npre+1):(hdr.npre+nfid));
kxo = interp1(1:nfid, kx, (1:nfid) - 0.5 - del, 'linear', 'extrap');
kxe = interp1(1:nfid, kx, (1:nfid) + 0.5 + del, 'linear', 'extrap');
kxe = fliplr(kxe);
3 changes: 1 addition & 2 deletions +toppe/plotseq.m
Original file line number Diff line number Diff line change
Expand Up @@ -298,7 +298,6 @@

p = loop(n,1); % module id
w = loop(n,16); % waveform index

dur = 0;

% Pure delay block
Expand Down Expand Up @@ -330,7 +329,7 @@

% Calculate RF and gradient waveforms for this block
rho1 = [ia_rf/max_pg_iamp*abs(modules{p}.rf(:,w))];
th1 = [ia_rf/max_pg_iamp*angle(modules{p}.rf(:,w))];
th1 = angle(modules{p}.rf(:,w));
gxit = ia_gx/max_pg_iamp*modules{p}.gx(:,w);
gyit = ia_gy/max_pg_iamp*modules{p}.gy(:,w);
gzit = ia_gz/max_pg_iamp*modules{p}.gz(:,w);
Expand Down
8 changes: 4 additions & 4 deletions +toppe/preflightcheck.m
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ function preflightcheck(entryFile, seqstampFile, sysGE)
modArr = toppe.tryread(@toppe.readmodulelistfile, moduleListFile);

% Check that:
% - all .mod files use the same peak RF limit (e.g., 0.25 Gauss)
% - b1CheckFile represents the RF pulse with peak RF amplitude across the whole the sequence
% - all .mod files used to acquire data have the same number of ADC samples as readoutFilterFile.
[~,~,~,~,~,~,~,hdr] = toppe.readmod(b1CheckFile);
b1limit = hdr.b1max;
Expand All @@ -82,8 +82,8 @@ function preflightcheck(entryFile, seqstampFile, sysGE)
error(sprintf('Number of samples in %s and %s do not match (must be same across all .mod files containing ADC windows)', ...
readoutFilterFile, modArr{ii}.fname));
end
if hdr.b1max ~= b1limit
error(sprintf('B1 limit in %s does not match %s (must be the same across all .mod files)', ...
if modArr{ii}.hasRF & hdr.b1max > b1limit
error(sprintf('Peak B1 limit in %s cannot exceed the b1 scaling .mod file in %s', ...
modArr{ii}.fname, entryFile));
end
end
Expand Down Expand Up @@ -196,7 +196,7 @@ function preflightcheck(entryFile, seqstampFile, sysGE)
fprintf(fout, '%.4f\n', max(abs(rf))); % needed to scale max_seqsar in .e file
fprintf(fout, '%.4f\n', gmax); % max gradient across all .mod files (Gauss)
fprintf(fout, '%.4f\n', slewmax); % max slew across all .mod files (G/cm/ms)
fprintf(fout, '%.4f\n', b1limit); % hardware b1 limit (Gauss)
fprintf(fout, '%.4f\n', b1limit); % peak B1 in sequence (Gauss)

fclose(fout);

Expand Down
2 changes: 1 addition & 1 deletion +toppe/systemspecs.m
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
% Design choices (need not equal scanner limits)
sys.maxGrad = 4; % Gauss/cm
sys.maxSlew = 15; % Gauss/cm/ms
sys.maxRF = 0.25;
sys.maxRF = 0.15;
sys.rfDeadTime = 72; % us. Must be >= 72us
sys.rfRingdownTime = 54; % us. Must be >= 54us
sys.adcDeadTime = 40; % us. Must be >= 40us
Expand Down
3 changes: 2 additions & 1 deletion +toppe/writemod.m
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,8 @@ function writemod(sysGE, varargin)
dtycyc = length(find(abs(b1)>0.2236*max(abs(b1)))) / length(b1);
maxpw = dtycyc;
num = 1;
max_b1 = sysGE.maxRF; % Gauss. Full instruction amplitude (32766) should produce max_b1 RF amplitude,
max_b1 = sysGE.maxRF; % Gauss. Full instruction amplitude (32766) should produce max_b1 RF amplitude,
%max_b1 = max(abs(b1(:))); % Gauss
% as long as other RF .mod files (if any) use the same max_b1.
max_int_b1_sq = max( cumsum(abs(b1).^2)*dt*1e3 ); % Gauss^2 - ms
max_rms_b1 = sqrt(mean(abs(b1).^2)); % Gauss
Expand Down

0 comments on commit ec3eaa3

Please sign in to comment.