KSFoundation  [April-2021]
A platform for simpler EPIC programming on GE MR systems
KS_SEQ_CONTROL Struct Reference

#include <KSFoundation.h>

Data Fields

int min_duration
 
int nseqinstances
 
int ssi_time
 
int duration
 
int momentstart
 
int rfscalingdone
 
KS_DESCRIPTION description
 
KS_SEQ_HANDLE handle
 
KS_GRADRFCTRL gradrf
 

Detailed Description

A multi-purpose controller struct for each sequence module to facilitate RF scaling, SAR calculations, sequence duration and sequence switching

KS_SEQ_CONTROL.png
KS_SEQ_CONTROL


Each sequence module in the pulse sequence should have a dedicated KS_SEQ_CONTROL struct associated with it. For example, one may have the following KS_SEQ_CONTROL structs in a pulse sequence, where there is one main sequence and an fatsat module plugin that technically are two sequences - or sequence modules - (via the use of KS_SEQLENGTH()):

  • mymainseqctrl - Sequence control for the main sequence, i.e. the only sequence that is storing data (the actual sequence)
  • fatsatseqctrl - Sequence control for the fatsat sequence module

The KS_SEQ_CONTROL struct has multiple uses and could be viewed as the administrator for each sequence module. Here is a list of uses and steps neccesary to achieve the intended functionality:

In cveval() on HOST:

  • Collection of information on how many times each KS_TRAP or KS_RF object is placed out in pulsegen() for the particular sequence module.
    • Preferrably, there is a sequence generating function that may be named e.g. seqmodulename_pg(), which contains all ks_pg_trap(), ks_pg_rf() (and other ks_pg_***()) calls that build up the sequence module by placing out its sequence objects. The KS_SEQ_CONTROL struct for the current sequence module should be the last argument to each ks_pg_***() call in seqmodulename_pg().
    • By running seqmodulename_pg() in cveval() the seqctrl.gradrf field will be updated with pointers to its sequence objects, allowing seqctrl to also know the number of occurrences of each trapezoid or RF.
    • This information is used for RF scaling, gradient heating and SAR calculations, after adding the sequence modules to one common KS_SEQ_COLLECTION struct using ks_eval_addtoseqcollection().
  • Somewhere in cveval(), the SSI time for the sequence modules needs to be set. This is the time the sequence module needs to update its waveforms from one playout to the next during scanning. Often 1 ms is enough, but for long sequence modules it may need to be increased, especially if debugging outputs are used. It can also be set lower, sometimes down to about 0.1 ms.
  • In cveval(), the seqmodulename_pg() function for each sequence module should be called after the ks_eval_***() calls setting up the sequence objects, but before the TR timing, RF scaling and SAR calculations. Inside each seqmodulename_pg() function the .duration and .min_duration fields of the KS_SEQ_CONTROL struct should be set to the actual minimum duration of the sequence module (the [us] value corresponding to the absolute position of the last gradient or RF waveform). See ks_eval_seqctrl_setminduration() in the example below.

On TGT:

  • In pulsegen(): The KS_SEQ_CONTROL struct is the 2nd arg to KS_SEQLENGTH(), which defines the hardware sequence duration (using the seqctrl.duration value) for the sequence module. Using KS_SEQLENGTH() instead of GE's SEQLENGTH() macro allows sequence module playouts, and switching between modules, in scan() using ks_scan_playsequence().
  • In scan(), and its psd-specific subroutines: The KS_SEQ_CONTROL struct is passed to ks_scan_playsequence() to switch to and play out this sequence module in real time during scanning. ks_scan_playsequence() also returns seqctrl.duration, allowing timing checks on HOST by dry-running scan functions in cveval(). E.g. the scan clock is set by GE's pitscan variable, which can be set by calling scanloop() in the simplified example below.

Example use of KS_SEQ_CONTROL with one main sequence module and one fatsat sequence module

@ipgexport
KS_SEQ_CONTROL mymainseqctrl;
KS_SEQ_CONTROL fatsatseqctrl;
@host
cveval() {
setup_sequenceobjects(); // see below
setup_fatsatsequenceobjects(); // details not shown in this example
// call each *seqmodule*_pg() function here and make sure each seqctrl.duration field > 0 for those sequence modules that should be used in the current setting.
mypsd_pg(); // also updates mymainseqctrl.gradrf, mymainseqctrl.min_duration = mymainseqctrl.duration > 0
myfatsat_pg(); // some fatsat sequence module that also updates fatsatseqctrl.gradrf, fatsatseqctrl.min_duration = fatsatseqctrl.duration > 0
// TR timing & SAR calcs here (for details, see the KS_SEQ_COLLECTION documentation)
pitscan = scanloop(); // Value of scan clock by dry-running the scan on HOST
}
void setup_sequenceobjects() {
ks_eval_selrf(&myselrf, "myexc"); // selective RF excitation
ks_eval_phaser(&myphaser, "myphaseenc"); // phase encoding
ks_eval_trap(&mydephaser, "readdephaser"); // read dephaser
ks_eval_readtrap(&myreadout, "myreadout"); // readout with trapezoid
// Init seqctrl
ks_init_seqcontrol(&mymainseqctrl); // Reset contents of KS_SEQ_CONTROL to KS_INIT_SEQ_CONTROL
strcpy(mymainseqctrl.description, "mymainseq"); // add description to it
mymainseqctrl.ssi_time = 1ms; // Optional: Set SSI time (default: 1 ms)
}
@pg
void mypsd_pg() {
tmploc.pos = 1ms;
ks_pg_selrf(&myselrf, tmploc, &mymainseqctrl); // play the RF pulse, beginning at 1ms into the sequence
tmploc.pos += myselrf.grad.duration + myselrf.postgrad.duration;
ks_pg_phaser(&myphaser, tmploc, &mymainseqctrl);
tmploc.pos += myphaser.grad.duration;
ks_pg_readtrap(&myreadout, tmploc, &mymainseqctrl);
tmploc.pos += myreadout.grad.duration; // time for the end of last sequence entry
#ifndef IPG // On HOST only (i.e. not IPG/TGT)
ks_eval_seqctrl_setminduration(&mymainseqctrl, tmploc.pos); // sets mymainseqctrl.duration = mymainseqctrl.min_duration = tmploc.pos + mymainseqctrl.ssi_time
#endif
}
mypsd_pg();
KS_SEQLENGTH(seqcore, mymainseqctrl);
myfatsat_pg();
KS_SEQLENGTH(seqfat, fatsatseqctrl);
}
// declare scanloop in @pg section instead of @rsp to allow it to be called on HOST too. This, to figure out how long one TR is.
float scanloop() {
float time = 0.0;
for (ky = 0; ky < opyres; ky++) {
for (i = 0; i < opslquant; i++) {
// ... do some phase encoding etc (not shown)...
time += ks_scan_playsequence(&fatsatseqctrl); // if fatsatseqctrl.duration > 0, it plays sequence in real time (on TGT) and returns the time in [us] equal to value of fatsatseqctrl.duration
time += ks_scan_playsequence(&mymainseqctrl); // if mymainseqctrl.duration > 0, it plays sequence in real time (on TGT) and returns the time in [us] equal to value of mymainseqctrl.duration
}
}
return time; // return the time in [us] for opslquant number of slices
}
@rsp
scan() {
scanloop();
}

Field Documentation

◆ min_duration

int min_duration

In [us]. The minimum possible sequence duration based on the end of the last gradient/RF waveform. Lower limit of the .duration field that must be manually set

◆ nseqinstances

int nseqinstances

Number of playouts in scan loop. ks_scan_playsequence() increments this on HOST for each call. Value is set to zero by ks_eval_seqcollection_resetninst()

◆ ssi_time

int ssi_time

In [us]. The SSI time (hardware update time) for the sequence module. Default is 1000 us

◆ duration

int duration

In [us]. The actual duration of the sequence module. Must be >= .min_duration, and is used in KS_SEQLENGTH() and for SAR check and TR validation (GEReq_eval_checkTR_SAR())

◆ momentstart

int momentstart

In [us]. Magnetic reference point for the excitation pulse (TE = 0) relative to the start of the sequence module. This field is set automatically by ks_pg_rf() if rf.role = KS_RF_ROLE_EXC, under the assumption that there is only one excitation RF pulse in each sequence module. If not, .momentstart should be set manually. Besides TE calculations in the sequence module's pg function itself, .momentstart for a main sequence is also used in KSInversion.e for TI timing.

◆ rfscalingdone

int rfscalingdone

Default FALSE (until GEReq_eval_rfscaling() sets it to TRUE). For safety reasons (and to make sure correct FA is set), ks_pg_rf() (and hence ks_pg_selrf()) refuses to place the RF pulse belonging to this sequence module if .rfscalingdone = FALSE.

◆ description

KS_DESCRIPTION description

Description of the sequence module

◆ handle

KS_SEQ_HANDLE handle

Internal use. Used to keep track of the global sequence index

◆ gradrf

KS_GRADRFCTRL gradrf

Internal use. Used to gather pointers to RF and gradient objects in the sequence module


The documentation for this struct was generated from the following file: