Skip to content
This repository has been archived by the owner on May 18, 2021. It is now read-only.

Rt/issue75 shim specs json #92

Draft
wants to merge 24 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
4409f83
add +img.read_nii.m from dev branch rt/+img
rtopfer May 22, 2020
fedca0d
Merge branch 'master' into rt/issue75-ShimSpecs-json
rtopfer May 25, 2020
a78565d
REF,DOC: Replace getnactivechannels() method with nChannels dependent…
rtopfer May 26, 2020
386664f
BUG: Replace calls to ShimUse.customdisplay (deprecated) with fprintf
rtopfer May 26, 2020
0089d5c
BUG: correct variable name
rtopfer May 26, 2020
fce7622
REF: Enable ShimSpecs object as optional input to ShimOpt() constructor
rtopfer May 26, 2020
04b75c3
NEW: +b0shim/+coils/+greg package, containing specs.json (converted f…
rtopfer May 26, 2020
b00fd74
NEW: package +valid for generic validation functions; add mustBeA.m—c…
rtopfer May 26, 2020
b88c7cf
NEW: b0shim.Specs class to create ShimSpecs object from json file (pa…
rtopfer May 26, 2020
2dbcb66
NEW: draft save_json() method in b0shim.Specs
rtopfer May 26, 2020
19f8527
DOC: b0shim.Specs reformat
rtopfer Jun 4, 2020
a6ad608
BUG: ShimUse.m : Replace curly brackets with parentheses
rtopfer Jun 8, 2020
93298ab
Merge branch 'master' into rt/issue75-ShimSpecs-json
rtopfer Jun 10, 2020
dfda2d4
DEL: +img folder (renamed on master to +imutils)
rtopfer Jun 11, 2020
89d7ebf
REF: Minor changes to Shim -Com,-Specs and -Opt classes for _Greg/_re…
rtopfer Jun 12, 2020
ce9a8b4
NEW: +parts classes to form Shim Specs config: Channel.m and Port.m
rtopfer Jun 15, 2020
091800e
REF: b0shim.Specs properties now include +parts.Channel, .Port
rtopfer Jun 15, 2020
02c45e2
DOC,NEW: +b0shim.parts (class docs) and Contents.m (package doc)
rtopfer Jun 15, 2020
f60aa6f
NEW: read_/write_json standalone functions added to ./misc
rtopfer Jun 16, 2020
dc54fb5
DOC: +b0shim/+parts/ Channel.m and Port.m
rtopfer Jun 16, 2020
9404bbe
REF, DOC: +b0shim.Specs simplify (move json I/O to standalone functio…
rtopfer Jun 16, 2020
0209c5e
REF: Rename Specs --> Config (the class bears little semblance to the…
rtopfer Jun 16, 2020
28c75ab
BUG: Add Parity property to b0shim.parts.Port
rtopfer Jun 17, 2020
c8224e2
NEW,DOCS: add example configuration scripts and documentation
rtopfer Jun 17, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions +b0shim/+coils/+greg/Contents.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
%b0shim.coil.greg 8-channel AC/DC 3T neck coil
Copy link
Member

@jcohenadad jcohenadad Jun 19, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

to look nice on matlab's doc:

Suggested change
%b0shim.coil.greg 8-channel AC/DC 3T neck coil
%% b0shim.coil.greg
% 8-channel AC/DC 3T neck coil

i suggest applying this format to the other Contents.m files. Also see shimming-toolbox/shimming-toolbox#134 (comment)

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure if this is the most appropriate thread to post this, but I found a very fast way to interpolate the shim coil profile maps into the grid used by the target volume (for example the EPI volume) to prepare the coil field maps for shim optimization calculations. I'm not sure how Ryan handled this in the original code.

unix('flirt -in coil_field_maps.nii -ref epi_target.nii -applyxfm -usesqform -out coil_field_maps_interp')

This seems to be ~100x faster than my previous approach which used scatteredInterpolant in Matlab. Matlab is extremely slow if you use "scattered" coordinates for the interpolation rather than monotonically increasing points such as those generated by meshgrid. The trick is to use the -usesqform flag in FLIRT, so that it will just apply a coordinate transform/interpolation without rotating or moving the image to register them.

However, I'm not sure if we want to commit to relying on FSL in the GUI. We might want to implement our own fast interpolator using the same methods as FLIRT.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you @jaystock, I moved the discussion to a specific issue thread shimming-toolbox/shimming-toolbox#159

%
% Nicknamed "greg", after its creator:
% [Germain G. et al., ISMRM 2016](https://cds.ismrm.org/protected/16MPresentations/abstracts/1154.html)
%
% [Topfer et al., ISMRM 2018](https://www.slideshare.net/neuropoly/integrated-rf-and-shim-coils-for-mri-105560384)
%
% <iframe src="//www.slideshare.net/slideshow/embed_code/key/lhvoyKuHLsgc8L" width="595" height="485" frameborder="0" marginwidth="0" marginheight="0" scrolling="no" style="border:1px solid #CCC; border-width:1px; margin-bottom:5px; max-width: 100%;" allowfullscreen> </iframe> <div style="margin-bottom:5px"> <strong> <a href="//www.slideshare.net/neuropoly/integrated-rf-and-shim-coils-for-mri-105560384" title=" Integrated RF and Shim coils for MRI" target="_blank"> Integrated RF and Shim coils for MRI</a> </strong> from <strong><a href="https://www.slideshare.net/neuropoly" target="_blank">NeuroPoly </a></strong> </div>
81 changes: 81 additions & 0 deletions +b0shim/+coils/+greg/specs.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
{
"Adc": {
"mVPerAdcCount": 2
},
"Id": {
"systemName": "Greg",
"channelNames": [
"Ch1",
"Ch2",
"Ch3",
"Ch4",
"Ch5",
"Ch6",
"Ch7",
"Ch8"
],
"channelUnits": [
"[A]",
"[A]",
"[A]",
"[A]",
"[A]",
"[A]",
"[A]",
"[A]"
]
},
"Amp": {
"maxCurrentPerChannel": [
2.5,
2.5,
2.5,
2.5,
2.5,
2.5,
2.5,
2.5
],
"maxCurrentPerBank": 20,
"nChannels": 8,
"nActiveChannels": 8,
"maxVoltagePerChannel": 2500,
"staticChannels": [
true,
true,
true,
true,
true,
true,
true,
true
],
"dynamicChannels": [
true,
true,
true,
true,
true,
true,
true,
true
]
},
"Com": {
"baudRate": 115200,
"readTimeout": 500,
"dataBits": 8,
"stopBits": 1,
"flowControl": "NONE",
"parity": "NONE",
"byteOrder": "bigEndian",
"txRxDelay": 0.005,
"updatePeriod": 0.1
},
"Dac": {
"resolution": 8,
"maxCurrent": 5,
"referenceVoltage": 1250,
"maximum": 26214
}
}
75 changes: 75 additions & 0 deletions +b0shim/+coils/+greg/write_config.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
function [ filename ] = write_config( filename )
%WRITE_CONFIG Writes system configuration file for the 8 ch. AC/DC neck coil
%
% filename = b0shim.coils.greg.write_config();
% b0shim.coils.greg.write_config( filename );
%
% Defines the system configuration and saves it to the coil's package folder under
% ```
% ./+b0shim/+coils/+greg/config.json
% ```
% b0
%
% and saves the system configuration for
% the 3T 8-channel AC/DC neck-coil
%
% See also
% [ b0shim.coils.greg.Contents ]( ./Contents.md )
arguments
filename(1,:) char = [mfilename('fullpath') filesep 'config_' datestr(date,'yymmdd') ];
end
warning('Untested commit - may contain bugs')

%% 1. Create an instance of the default configuration object
config = b0shim.Config;

%% 2. *As the object's properties are immutable*, convert to struct:
% NOTE: Conversion elicits a warning. Not a concern. Mute it momentarily)
warning('OFF', msgId);
config = struct( config );
warning('ON', msgId);

%% 3. Assign fields according to system specs

% System/package name
config.name = "greg";

%% Channel configuration

% Add 8 channels
config.channels(1:8) = b0shim.parts.Channel ;

% Assign channel properties
for iCh = 1 : numel(config.channels)
config.channels(iCh).name = strcat( "Ch. ", num2str(iCh) );
config.channels(iCh).units = "A"; % Amperes
config.limits(iCh) = [-2.5 2.5]; % [min. max.] in Amperes
config.positioning = "table-fixed"; % shim position is table-dependent
end

%% Configuration of serial port parameters
config.ports(1) = b0shim.parts.Port();

% NOTE: These parameters are actually set *in the source code of the Teensy
% microcontroller.*
% TODO: Rather than hardcode the values in two places, some consistency ought
% to be enforced between the C++ and MATLAB sources, e.g. by MATLAB parsing the
% .cpp/.hpp files, or the config.json values being transfered over serial, or
% some other alternative. (Same goes for the Arduino-based respiratory bellows...)

config.ports(1).BaudRate = 115200;
config.ports(1).DataBits = 8;
config.ports(1).StopBits = 1;
config.ports(1).Parity = "none"
config.ports(1).FlowControl = "none";
config.ports(1).ByteOrder = "big-endian";

%% Configure remaining properties related to the microcontroller

%% 4. With everything configured, convert back to object
config = b0shim.Config( config );

%% 5. Save as .json
write_json( config, filename )

end
82 changes: 82 additions & 0 deletions +b0shim/+parts/Channel.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
classdef Channel
%b0shim.parts.Channel Defines a shim coil element
%
% self = b0shim.parts.Channel()
%
% `Channel` provides a template description of a single coil element. The
% properties of a `Channel` object-array thereby describe an array of coils.
%
% See also
% [ b0shim.parts.Contents ](./Contents.md)
% [ b0shim.Config ](../Config.md)

properties

% Shim channel ID as a string-scalar.
%
% `name` defines the row names when tabulating optimization results
% in `b0shim.Opt.optimizeshimcurrents` (i.e. for print display).
%
% __EXAMPLE__
%
% % Generically, this could simply be
% `name = "Ch. 1"`;
%
% % Or, for a 2nd-order spherical harmonic term
% `name = "XY (B22)"`;
name(1,1) string = "Ch" ;

% Shim channel physical units as a string-scalar.
%
% `units` is used when tabulating optimization results in
% `b0shim.Opt.optimizeshimcurrents` (i.e. for print display).
%
% __EXAMPLE__
%
% % Units are often expressed in Amperes
% `units = "A"`;
%
% % Alternatively, for a 2nd-order spherical harmonic shim, units could be
% `units = "mT/m^2"`;
units(1,1) string = "A" ;

% Range of permitted values as 2-element vector.
%
% `limits` defines the default constraints during shim optimization:
% `limits(1)` specifies the minimum value;
% `limits(2)` specifies the maximum value.
%
% Both elements should be given in the corresponding `units`.
%
% __EXAMPLE__
%
% For an "AC/DC" multi-coil array, with `units="A"`, the value might be
% `limits = [-2.5 2.5];`.
limits(1,2) {mustBeNumeric,mustBeReal} ;

% Label specifying coil positioning mode as a string-scalar.
%
% Options for `positioning` are:
%
% - "iso-fixed": The coil is immobile relative to isocenter (e.g. a gradient coil)
%
% - "table-fixed": The coil position changes with the patient-table (e.g. spine shim array)
%
% - "": Otherwise (e.g. coils can be freely rearranged)
positioning(1,1) string {mustBeMember(positioning,["iso-fixed" "table-fixed" ""])} = "iso-fixed";
end

% =========================================================================
% =========================================================================
methods
% =========================================================================
function self = Channel( props )
return
end
% =========================================================================

end
% =========================================================================
% =========================================================================

end
12 changes: 12 additions & 0 deletions +b0shim/+parts/Contents.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
% b0shim.parts — Shim system components
%
% Classes in `+b0shim/+parts` define parameter templates for specific shim
% system components. Practically, the classes define properties of the general
% system configurations (namely, config.json files on disk and `b0shim.Config`
% objects in memory). The classes consist only of properties and their
% constructors likely won't need to be called except when creating a new
% configuration.
%
% Files
% Channel - A shim coil element
% Port - Serial port protocol parameters
67 changes: 67 additions & 0 deletions +b0shim/+parts/Port.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
classdef Port
%b0shim.parts.Port Serial port protocol parameters
%
% self = b0shim.parts.Port()
%
% `Port` properties are simply 6 of the 7 the parameters passed to MATLAB's
% `serialport` function when opening the communication line between a PC and
% the microcontroller directing the shim amplifiers. Hence, the properties are
% only relevant for hardware systems permitting direct control via MATLAB—USB
% (i.e. systems that subclass `b0shim.Com` to define a communication interface).
% For a host MRI, or a virtual shim array, this classdef is likely irrelevant.
%
% __NOTES__
%
% 1. While `Port` properties all correspond to `serialport()` arguments,
% some of the default assignments are different (`Port` defaults are informed
% by the microcontrollers in use at our lab).
%
% 2. As a safety feature, the mandatory first argument to `serialport()`—the
% device ID—is not included as a `Port` property: when connecting to a device,
% a user still needs to specify the port name. (The parameter, in any case,
% tends to vary as device names are determined by the OS.)
%
% See also
% [ serialport ](https://www.mathworks.com/help/matlab/ref/serialport.html)
% [ b0shim.parts.Contents ](./Contents.md)
% [ b0shim.Config ](../Config.md)

properties

% 2nd argument to serialport
BaudRate(1,1) uint32 {mustBeInteger,mustBePositive} = 9600;

% Name-value argument to serialport
DataBits(1,1) uint8 {mustBeMember(DataBits, [8 7 6 5] )} = 8;

% Name-value argument to serialport
Parity(1,1) string {mustBeMember(Parity, ["none" "even" "odd"])} = "none";

% Name-value argument to serialport
StopBits(1,1) single {mustBeMember(StopBits, [1 1.5 2] )} = 1;

% Name-value argument to serialport
FlowControl(1,1) string {mustBeMember(FlowControl, ["none" "hardware" "software"] )} = "none";

% Name-value argument to serialport
ByteOrder(1,1) string {mustBeMember(ByteOrder, ["little-endian" "big-endian"] )} = "big-endian";

% Name-value argument to serialport
Timeout(1,1) single {mustBeNumeric, mustBePositive} = 10;

end

% =========================================================================
% =========================================================================
methods
% =========================================================================
function self = Port( )
return
end
% =========================================================================

end
% =========================================================================
% =========================================================================

end
Loading