Compiling pulse sequences (PSDs) for the MR-scanner usually involves the following steps:
ddd
or some other graphical debugger) on HOST and TGTfprintf(stderr, "")
to see info in the WTools
main window (but learn to use e.g. the ddd
debugger, it's way better than printing). KSFoundation EPIC has also functions ks_error() and ks_dbg(), which also stores the output in text files (ks_errors.txt
and ks_debug.txt
)WTools
using [Analysis]->[MGD Sim]->[LoadCVs->Pulsegen->PlotPulse]/usr/g/bin
, so you must avoid overwriting these. A good practice is to put your own PSDs in /usr/g/research/<yourname>/
and make symolic links to them from /usr/g/bin
, where the MR-scanner expects PSDs to reside: Most code in a pulse sequence should run both in simulation (WTools) and on the MR-scanner. Exception to this include:
The ESE environment have C-preprocessing directives that are automatically set when compiling using psdqmake
:
The MR-scanner is really two computers, one for the user interface (HOST) and one for the real-time scanning (TGT, a.k.a. IPG). This is why the pulse sequence is split up into two binaries to be placed on the MR-scanner. In simulation (WTools), HOST is the EvalTool window and TGT is the MGDSim window.
When running psdqmake
, first the EPIC preprocessor takes the mypsd.e file (including all other *.e files mypsd.e refers to) and generates mypsd.host.c (HOST code) and mypsd.tgt.c (TGT code). If there is a compilation error, it will refer to either the .host.c
or the .tgt.c
file and point to some line number. One can open these files to see where the compilation error is, but one must sure NOT to modify these autogenerated c-files but instead the corresponding lines in mypsd.e.
int
or float
and there is an associate (hidden) _myvar
struct to each myvar
. UsePgenOnHost()
entry in the Imakefile) related to the generation of pulses (waveforms and trapezoids) on hardware. No real-time execution here. KSFoundation EPIC PSDs heavily relies on running @pg function in cveval() to figure out e.g. sequence durations and SAR limits. pi**
, see epic.h for a complete list or e.g. ksepi_init_UI() for an example. In WTools, this function is normally called every time the user modifies a value in EvalTool. On the MR-scanner, it will execute when the PSD is selected (but actually also a few times whenever a UI value is changed). epic_error()
(or ks_error()
for KSFoundation EPIC PSDs) with messages to the end user about e.g. errors in parameter combinations. In WTools, this function is normally called once only every time the user modifies a value in EvalTool. On the MR-scanner it will execute once only whenever a UI value is changed.rh**
) and other variables that are consequences of other parameter choices. See e.g. GEReq_predownload_setrecon_readphase() for an example. In WTools, this function is normally called once only every time the user modifies a value in EvalTool. On the MR-scanner it will execute once when the user presses [Save Rx].boffset()
. In KSFoundation EPIC, the sequence switching and playout is done via ks_scan_playsequence().A control variable may be an int or a float declared as (example with given numbers):
In this example, myvar
is declared as an int and initialized to 12, but behind the scenes a struct _myvar
will be declared with fields _myvar.minval = 2
, _myvar.maxval = 20
, _myvar.defval = 12
, and _myvar.descr = "My description of this variable"
. "VIS" means that one can search and find the variable in [Display CVs] on the MR-scanner. In addition, this _myvar
struct has the fields .existflag
and .fixedflag
.
The _struct should normally not be used directly. Instead, there are EPIC C-macros like cvmin(), cvmax(), cvdef(), cvmod() and cvoverride(), which takes myvar
as an input argument, not _myvar
. The idea behind min/max is to force a variable to be within a certain range. Often a UI error is passed to the end user if this is not the case, but sometimes the error is only shown as a "Download failure" when pressing [Scan].
The field .existflag
is automatically set to PSD_ON
(= TRUE
= 1) when the CV exist. For all CVs that belong to a UI menu button or field (called op***
), the UI sets the _op***.existflag = TRUE
when the user has selected it. To ask if the user has selected a CV myvar:
Tricker still, one may see the following look-a-like call, but with completely different result:
When the user sets an op**
(i.e. UI) CV, the .fixedflag
field is also set. If this field is set for a CV, any assignment in the PSD code will be ignored (!). For example, before the user has set the echo time (TE) corresponding to the CV opte
, the following code
will set TE to 15 ms. But as soon as the user has selected/modified it, _opte.fixedflag = TRUE
, and the very same assignment is ignored. They way this works it that the EPIC preprocessor (producing .host.c
and tgt.c
from the .e
file), changes the line opte = 15000;
to:
which effectively is the same thing as only assigning it to 15000 if .fixedflag = FALSE
.
Sometimes this fixedflag mechanism can be good even though it makes the coding different. But sometimes, one may want to override the user's selection/change of a CV, which is done by (here opte
as an example):
where the second argument is the forced new value. The third argument sets the _opte.fixedflag
field and the fourth argument set the _opte.existflag
field afterwards.
See /ESE_xxx/psd/psdsource/epic.h
for most pre-existing CVs, incl. op***
.
A C preprocessor uses defines to include/exclude code using
which is simply performed in-place in the code before the C-compiler sees it. A typical example in EPIC is to include/exclude code hunks for Simulation/Hardware or HOST/TGT.
However, before the C preprocessor acts on the .host.c
and .tgt.c
files generated from the .e
file (by the EPIC preprocessor), there are also directives for the EPIC preprocessor in the .e
file.
Analogously to #include for .c
files, there is an
for EPIC's .e
files. This includes the file somefiletoinclude.e
, but any section (@pg, @host, etc.) in that file will be moved to the corresponding sections rather than placed at the point of @inline.
There is also a way to conditionally include/exclude code in the .e file parsed by the EPIC preprocessor using %ifdef instead of #ifdef. There is however no %if like #if. The need for %ifdef is rare, but must be used when one need to exclude EPIC specific code, e.g. a CV declaration dependent on release versions, before the EPIC processing to *.c files.
For example, for KSFoundation EPIC PSDs, their Imakefile adds two defines based on the EPIC release. To make a pulse sequence compatible with multiple EPIC releases, we let the Imakefile create one define for the EPIC preprocessor and one define for the C preprocessor (example here for release 25.x):
%ifdef EPIC_RELEASE_IS_25x
if EPIC_RELEASE >= 25
These can be used in the sequence to handle quirks (e.g. change of # of arguments) in some functions, or new/obsolete functions, between releases. For KSFoundation EPIC PSDs, all release-specific stuff has already been placed in GERequired.e to make the PSDs compatible with multiple releases without clutter.
Home