KSFoundation  [April-2021]
A platform for simpler EPIC programming on GE MR systems
KSFoundation_host.c File Reference
#include <sys/stat.h>
#include <sys/types.h>

Functions

int ftruncate (int fd, off_t length)
 
STATUS inittargets (LOG_GRAD *lgrad, PHYS_GRAD *pgrad)
 
STATUS obloptimize_epi (LOG_GRAD *lgrad, PHYS_GRAD *pgrad, SCAN_INFO *scaninfotab, INT slquant, INT plane_type, INT coaxial, INT method, INT debug, INT *newgeo, INT srmode)
 
STATUS minseqrfamp (INT *Minseqrfamp, const INT numPulses, const RF_PULSE *rfpulse, const INT entry)
 
STATUS maxsar (INT *Maxseqsar, INT *Maxslicesar, DOUBLE *Avesar, DOUBLE *Cavesar, DOUBLE *Pksar, DOUBLE *B1rms, const INT numPulses, const RF_PULSE *rfpulse, const INT entry, const INT tr_val)
 
STATUS minseq (INT *p_minseqgrad, GRAD_PULSE *gradx, const INT gx_free, GRAD_PULSE *grady, const INT gy_free, GRAD_PULSE *gradz, const INT gz_free, const LOG_GRAD *log_grad, const INT seq_entry_index, const INT samp_rate, const INT min_tr, const INT e_flag, const INT debug_flag)
 
void ks_init_read (KS_READ *read)
 
void ks_init_trap (KS_TRAP *trap)
 
void ks_init_wait (KS_WAIT *wait)
 
void ks_init_wave (KS_WAVE *wave)
 
void ks_init_rf (KS_RF *rf)
 
void ks_init_sms_info (KS_SMS_INFO *sms_info)
 
void ks_init_selrf (KS_SELRF *selrf)
 
void ks_init_readtrap (KS_READTRAP *readtrap)
 
void ks_init_phaser (KS_PHASER *phaser)
 
void ks_init_epi (KS_EPI *epi)
 
void ks_init_dixon_asymreadwave (KS_DIXON_ASYMREADWAVE *asymreadwave)
 
void ks_init_dixon_dualreadtrap (KS_DIXON_DUALREADTRAP *dual_readtrap)
 
void ks_init_readwave (KS_READWAVE *readwave)
 
void ks_init_gradrfctrl (KS_GRADRFCTRL *gradrfctrl)
 
void ks_init_seqcontrol (KS_SEQ_CONTROL *seqcontrol)
 
void ks_init_seqcollection (KS_SEQ_COLLECTION *seqcollection)
 
STATUS ks_init_slewratecontrol (LOG_GRAD *loggrd, PHYS_GRAD *phygrd, float srfact)
 
STATUS ks_eval_addtoseqcollection (KS_SEQ_COLLECTION *seqcollection, KS_SEQ_CONTROL *seqctrl)
 
STATUS ks_eval_seqcollection_isadded (KS_SEQ_COLLECTION *seqcollection, KS_SEQ_CONTROL *seqctrl)
 
STATUS ks_eval_wait (KS_WAIT *wait, const char *const desc, int duration)
 
STATUS ks_eval_isirot (KS_ISIROT *isirot, const char *const desc, int isinumber)
 
STATUS ks_eval_read (KS_READ *read, const char *const desc)
 
STATUS ks_eval_trap_constrained (KS_TRAP *trap, const char *const desc, float ampmax, float slewrate, int minduration)
 
STATUS ks_eval_trap (KS_TRAP *trap, const char *const desc)
 
STATUS ks_eval_trap2 (KS_TRAP *trap, const char *const desc)
 
STATUS ks_eval_trap1 (KS_TRAP *trap, const char *const desc)
 
STATUS ks_eval_trap_constrained_time_maxarea (KS_TRAP *trap, const char *const desc, float ampmax, float slewrate, float maxarea, int derate_slewrate)
 
STATUS ks_eval_set_asym_padding (struct _readtrap_s *readtrap, float wanted_paddingarea_pre, float wanted_paddingarea_post)
 
STATUS ks_eval_trap1p (KS_TRAP *trap, const char *const desc)
 
STATUS ks_eval_readtrap_constrained (KS_READTRAP *readtrap, const char *const desc, float ampmax, float slewrate)
 
STATUS ks_eval_readtrap (KS_READTRAP *readtrap, const char *const desc)
 
STATUS ks_eval_readtrap2 (KS_READTRAP *readtrap, const char *const desc)
 
STATUS ks_eval_readtrap1 (KS_READTRAP *readtrap, const char *const desc)
 
STATUS ks_eval_readtrap_constrained_sample_duration (struct _readtrap_s *readtrap, const char *const desc, const float ampmax, const float slewrate, const int t_A, const int sample_duration, const float area_post, const float area_total)
 
int ks_eval_kynover_fromnex (int yres, float nex, int minkynover)
 
void ks_eval_phaseviewtable (KS_PHASER *phaser)
 
STATUS ks_eval_phaser_adjustres (KS_PHASER *phaser, const char *const desc)
 
STATUS ks_eval_phaser_setaccel (KS_PHASER *phaser, int min_acslines, float R)
 
STATUS ks_eval_phaser_constrained (KS_PHASER *phaser, const char *const phasername, float ampmax, float slewrate, int minduration)
 
STATUS ks_eval_phaser (KS_PHASER *phaser, const char *const phasername)
 
STATUS ks_eval_phaser2 (KS_PHASER *phaser, const char *const phasername)
 
STATUS ks_eval_phaser1 (KS_PHASER *phaser, const char *const phasername)
 
STATUS ks_eval_phaser1p (KS_PHASER *phaser, const char *const phasername)
 
STATUS ks_eval_wave (KS_WAVE *wave, const char *const desc, int res, int duration, KS_WAVEFORM newwave)
 
STATUS ks_eval_wave_file (KS_WAVE *wave, const char *const desc, int res, int duration, const char *const filename, const char *const format)
 
STATUS ks_eval_mirrorwave (KS_WAVE *wave)
 
STATUS ks_eval_rf_sinc (KS_RF *rf, const char *const desc, double bw, double tbw, float flip, int wintype)
 
STATUS ks_eval_rf_secant (KS_RF *rf, const char *const desc, float A0, float tbw, float bw)
 
STATUS ks_eval_rf_hard (KS_RF *rf, const char *const desc, int duration, float flip)
 
STATUS ks_eval_rf_hard_optimal_duration (KS_RF *rf, const char *const desc, int order, float flip, float offsetFreq)
 
STATUS ks_eval_rf_binomial (KS_RF *rf, const char *const desc, int offResExc, int nPulses, float flip, float offsetFreq)
 
STATUS ks_eval_rfstat (KS_RF *rf)
 
STATUS ks_eval_rf (KS_RF *rf, const char *const desc)
 
void ks_eval_rf_relink (KS_RF *rf)
 
STATUS ks_eval_seltrap (KS_TRAP *trap, const char *const desc, float slewrate, float slthick, float bw, int rfduration)
 
STATUS ks_eval_selrf_constrained (KS_SELRF *selrf, const char *const desc, float ampmax, float slewrate)
 
STATUS ks_eval_selrf (KS_SELRF *selrf, const char *const desc)
 
STATUS ks_eval_selrf2 (KS_SELRF *selrf, const char *const desc)
 
STATUS ks_eval_selrf1 (KS_SELRF *selrf, const char *const desc)
 
STATUS ks_eval_selrf1p (KS_SELRF *selrf, const char *const desc)
 
float ks_eval_findb1 (KS_SELRF *selrf, float max_b1, double scaleFactor, int sms_multiband_factor, int sms_phase_modulation_mode, float sms_slice_gap)
 
void ks_eval_transient_SPGR_FA_train_recursive (float *FA_train, float *MZ_train, int N, float E1, float target_MT, int n)
 
void ks_eval_transient_SPGR_FA_train_binary_search (float *FA_train, float *MZ_train, int N, float E1, float MTlo, float MThi, float total_FA)
 
STATUS ks_eval_check_FA_train (int N, float *FA_train, float *MZ_train, float total_FA)
 
STATUS ks_eval_transient_SPGR_FA_train (float *FA_train, int N, float TR, float T1, float total_FA)
 
STATUS ks_eval_sms_make_multiband (KS_SELRF *selrfMB, const KS_SELRF *selrf, const int sms_multiband_factor, const int sms_phase_modulation_mode, const float sms_slice_gap, int debug)
 
STATUS ks_eval_sms_get_phase_modulation (float *sms_phase_modulation, const int sms_multiband_factor, const int sms_phase_modulation_mode)
 
float ks_eval_sms_calc_slice_gap (int sms_multiband_factor, const SCAN_INFO *org_slice_positions, int nslices, float slthick, float slspace)
 
float ks_eval_sms_calc_caipi_area (int caipi_fov_shift, float sms_slice_gap)
 
STATUS ks_eval_stretch_rf (KS_RF *rf, float stretch_factor)
 
STATUS ks_eval_sms_make_pins (KS_SELRF *selrfPINS, const KS_SELRF *selrf, float sms_slice_gap)
 
STATUS ks_eval_sms_make_pins_dante (KS_SELRF *selrfPINS, const KS_SELRF *selrf, float sms_slice_gap)
 
int ks_eval_findNearestNeighbourIndex (float value, const float *x, int length)
 
void ks_eval_linear_interp1 (const float *x, int x_length, const float *y, const float *xx, int xx_length, float *yy)
 
STATUS ks_eval_trap2wave (KS_WAVE *wave, const KS_TRAP *trap)
 
STATUS ks_eval_append_two_waves (KS_WAVE *first_wave, KS_WAVE *second_wave)
 
STATUS ks_eval_concatenate_waves (int num_waves, KS_WAVE *target_wave, KS_WAVE **waves_to_append)
 
STATUS ks_eval_epi_constrained (KS_EPI *epi, const char *const desc, float ampmax, float slewrate)
 
STATUS ks_eval_epi_setinfo (KS_EPI *epi)
 
STATUS ks_eval_epi_maxamp_slewrate (float *ampmax, float *slewrate, int xres, float quietnessfactor)
 
STATUS ks_eval_epi (KS_EPI *epi, const char *const desc, float quietnessfactor)
 
GRAD_PULSE ks_eval_makegradpulse (KS_TRAP *trp, int gradchoice)
 
STATUS ks_eval_seqctrl_setminduration (KS_SEQ_CONTROL *seqctrl, int mindur)
 
STATUS ks_eval_seqctrl_setduration (KS_SEQ_CONTROL *seqctrl, int dur)
 
STATUS ks_eval_seqcollection_durations_setminimum (KS_SEQ_COLLECTION *seqcollection)
 
STATUS ks_eval_seqcollection_durations_atleastminimum (KS_SEQ_COLLECTION *seqcollection)
 
int ks_eval_gradrflimits (KS_SAR *sar, KS_SEQ_COLLECTION *seqcollection, float gheatfact)
 
int ks_eval_mintr (int nslices, KS_SEQ_COLLECTION *seqcollection, float gheatfact, int(*play_loop)(int, int, void **), int nargs, void **args)
 
int ks_eval_maxslicespertr (int TR, KS_SEQ_COLLECTION *seqcollection, float gheatfact, int(*play_loop)(int, int, void **), int nargs, void **args)
 
void ks_eval_seqcollection_resetninst (KS_SEQ_COLLECTION *seqcollection)
 
int ks_eval_seqcollection_gettotalduration (KS_SEQ_COLLECTION *seqcollection)
 
int ks_eval_seqcollection_gettotalminduration (KS_SEQ_COLLECTION *seqcollection)
 
STATUS ks_eval_seqcollection2rfpulse (RF_PULSE *rfpulse, KS_SEQ_COLLECTION *seqcollection)
 
unsigned int ks_calc_nextpow2 (unsigned int n)
 
int ks_calc_roundupms (int val)
 
STATUS ks_calc_filter (FILTER_INFO *echortf, int tsp, int duration)
 
int ks_calc_bw2tsp (float bw)
 
float ks_calc_tsp2bw (int tsp)
 
float ks_calc_nearestbw (float bw)
 
float ks_calc_lower_rbw (float rbw)
 
float ks_calc_higher_rbw (float rbw)
 
float ks_calc_max_rbw (float ampmax, float fov)
 
int ks_calc_trap_time2area (KS_TRAP *trap, float area)
 
int ks_calc_dixon_times_within_one_period (float t1, float t2, float B0)
 
float ks_calc_dixon_fat_nsa_2p (float t1, float t2, float B0, float F)
 
float ks_calc_dixon_water_nsa_2p (float t1, float t2, float B0, float W)
 
double ks_calc_dixon_cond_2p (double t1, double t2, double B0)
 
float ks_calc_dixon_mean_nsa_2p_ss_weighted (float t1, float t2, float B0, float w1, float w2)
 
float ks_calc_dixon_mean_nsa_2p_ss (float t1, float t2, float B0)
 
STATUS ks_calc_sliceplan (KS_SLICE_PLAN *slice_plan, int nslices, int slperpass)
 
STATUS ks_calc_sliceplan_interleaved (KS_SLICE_PLAN *slice_plan, int nslices, int slperpass, int ninterleaves)
 
STATUS ks_calc_sliceplan_sms (KS_SLICE_PLAN *slice_plan, int nslices, int slperpass, int multiband_factor)
 
int ks_calc_sms_min_gap (DATA_ACQ_ORDER *dacq, int nslices)
 
int ks_calc_slice_acquisition_order_smssingleacq_impl (DATA_ACQ_ORDER *dacq, int nslices, int interleave)
 
int ks_calc_slice_acquisition_order_smssingleacq (DATA_ACQ_ORDER *dacq, int nslices)
 
void ks_print_seqcollection (KS_SEQ_COLLECTION *seqcollection, FILE *fp)
 
void ks_print_sliceplan (const KS_SLICE_PLAN slice_plan, FILE *fp)
 
void ks_print_scaninfo (const SCAN_INFO *scan_info, int nslices, const char *desc, FILE *fp)
 
void ks_print_waveform (const KS_WAVEFORM waveform, const char *filename, int res)
 
void ks_print_read (KS_READ a, FILE *fp)
 
void ks_print_trap (KS_TRAP t, FILE *fp)
 
void ks_print_readtrap (KS_READTRAP r, FILE *fp)
 
void ks_print_phaser (KS_PHASER p, FILE *fp)
 
void ks_print_gradrfctrl (KS_GRADRFCTRL gradrfctrl, FILE *fp)
 
void ks_print_epi (KS_EPI s, FILE *fp)
 
void ks_print_rfpulse (RF_PULSE rfpulse, FILE *fp)
 
void ks_print_rf (KS_RF r, FILE *fp)
 
void ks_print_selrf (KS_SELRF r, FILE *fp)
 
int ks_eval_clear_readwave (KS_READWAVE *readwave)
 
int ks_eval_clear_dualreadtrap (KS_DIXON_DUALREADTRAP *dual_read)
 
int ks_file_exist (char *filename)
 
void ks_plot_host_slicetime_path (char *path)
 
void ks_plot_host_slicetime_fullfile (char *fullfile)
 
void ks_plot_host_slicetime_delete ()
 
void ks_plot_host_slicetime_begin ()
 
void ks_plot_host_slicetime_endofslicegroup (const char *desc, const KS_PLOT_SLICEGROUP_MODE mode)
 
void ks_plot_host_slicetime_endofpass (KS_PLOT_PASS_MODE pass_mode)
 
void ks_plot_host_slicetime (KS_SEQ_CONTROL *ctrl, int nslices, float *slicepos_mm, float slthick_mm, KS_PLOT_EXCITATION_MODE excmode)
 
void ks_plot_host_slicetime_end ()
 
void ks_plot_host (KS_SEQ_COLLECTION *seqcollection, KS_PHASEENCODING_PLAN *plan)
 
void ks_plot_host_seqctrl (KS_SEQ_CONTROL *ctrl, KS_PHASEENCODING_PLAN *plan)
 
void ks_plot_host_seqctrl_manyplans (KS_SEQ_CONTROL *ctrl, KS_PHASEENCODING_PLAN **plans, const int num_plans)
 

Variables

int cfswgut
 
int cfswrfut
 
int cfsrmode
 
int cfgcoiltype
 
float GAM
 
int rhkacq_uid
 
int ks_plot_filefmt
 
int ks_plot_kstmp
 
char ks_psdname [256]
 
int gradHeatMethod
 
int gradDCsafeMethod
 
int minseqcable_t
 
int minseqbusbar_t
 

Detailed Description

This file contains functions only accessible on HOST.

Function Documentation

◆ ftruncate()

int ftruncate ( int  fd,
off_t  length 
)

◆ inittargets()

STATUS inittargets ( LOG_GRAD *  lgrad,
PHYS_GRAD *  pgrad 
)

◆ obloptimize_epi()

STATUS obloptimize_epi ( LOG_GRAD *  lgrad,
PHYS_GRAD *  pgrad,
SCAN_INFO *  scaninfotab,
INT  slquant,
INT  plane_type,
INT  coaxial,
INT  method,
INT  debug,
INT *  newgeo,
INT  srmode 
)

◆ minseqrfamp()

STATUS minseqrfamp ( INT *  Minseqrfamp,
const INT  numPulses,
const RF_PULSE *  rfpulse,
const INT  entry 
)

◆ maxsar()

STATUS maxsar ( INT *  Maxseqsar,
INT *  Maxslicesar,
DOUBLE *  Avesar,
DOUBLE *  Cavesar,
DOUBLE *  Pksar,
DOUBLE *  B1rms,
const INT  numPulses,
const RF_PULSE *  rfpulse,
const INT  entry,
const INT  tr_val 
)

◆ minseq()

STATUS minseq ( INT *  p_minseqgrad,
GRAD_PULSE *  gradx,
const INT  gx_free,
GRAD_PULSE *  grady,
const INT  gy_free,
GRAD_PULSE *  gradz,
const INT  gz_free,
const LOG_GRAD *  log_grad,
const INT  seq_entry_index,
const INT  samp_rate,
const INT  min_tr,
const INT  e_flag,
const INT  debug_flag 
)

◆ ks_init_read()

void ks_init_read ( KS_READ read)

Resets a KS_READ sequence object to its default value (KS_INIT_READ)

Parameters
[out]readPointer to KS_READ
Returns
void
54  {
55  KS_READ defacq = KS_INIT_READ;
56  *read = defacq;
57 }
Core sequence object that handles a data acquisition window
Definition: KSFoundation.h:730
#define KS_INIT_READ
Definition: KSFoundation.h:213

◆ ks_init_trap()

void ks_init_trap ( KS_TRAP trap)

Resets a KS_TRAP sequence object to its default value (KS_INIT_TRAP)

Parameters
[out]trapPointer to KS_TRAP
Returns
void
58  {
59  KS_TRAP deftrap = KS_INIT_TRAP;
60  *trap = deftrap;
61 }
Core sequence object for making trapezoids on X,Y,Z, and OMEGA boards
Definition: KSFoundation.h:578
#define KS_INIT_TRAP
Definition: KSFoundation.h:212

◆ ks_init_wait()

void ks_init_wait ( KS_WAIT wait)

Resets a KS_WAIT sequence object to its default value (KS_INIT_WAIT)

Parameters
[out]waitPointer to KS_WAIT
Returns
void
62  {
63  KS_WAIT defwait = KS_INIT_WAIT;
64  *wait = defwait;
65 }
Core sequence object that adds wait periods in the pulse sequence (see ks_eval_wait(), ks_pg_wait()). Can be placed on any sequence board
Definition: KSFoundation.h:456
#define KS_INIT_WAIT
Definition: KSFoundation.h:210

◆ ks_init_wave()

void ks_init_wave ( KS_WAVE wave)

Resets a KS_WAVE sequence object to its default value (KS_INIT_WAVE)

Parameters
[out]wavePointer to KS_WAVE
Returns
void
66  {
67  KS_WAVE defwave = KS_INIT_WAVE;
68  *wave = defwave;
69 }
Core sequence object making arbitrary waveforms on any board (using float data format)
Definition: KSFoundation.h:664
#define KS_INIT_WAVE
Definition: KSFoundation.h:227

◆ ks_init_rf()

void ks_init_rf ( KS_RF rf)

Resets a KS_RF sequence object to its default value (KS_INIT_RF)

Parameters
[out]rfPointer to KS_RF
Returns
void
70  {
71  KS_RF defrf = KS_INIT_RF;
72  *rf = defrf;
73 }
#define KS_INIT_RF
Definition: KSFoundation.h:239
Composite sequence object for RF (with optional OMEGA & THETA pulses)
Definition: KSFoundation.h:938

◆ ks_init_sms_info()

void ks_init_sms_info ( KS_SMS_INFO sms_info)

Resets a KS_SMS_INFO sequence object to its default value (KS_INIT_SMS_INFO)

Parameters
[out]sms_infoPointer to KS_SMS_INFO
Returns
void
74  {
75  KS_SMS_INFO defsms_info = KS_INIT_SMS_INFO;
76  *sms_info = defsms_info;
77 }
Internal typedef struct that is a part of the KS_SELRF typedef struct, used to store information abou...
Definition: KSFoundation.h:1367
#define KS_INIT_SMS_INFO
Definition: KSFoundation.h:250

◆ ks_init_selrf()

void ks_init_selrf ( KS_SELRF selrf)

Resets a KS_SELRF sequence object to its default value (KS_INIT_SELRF)

Parameters
[out]selrfPointer to KS_SELRF
Returns
void
78  {
79  KS_SELRF defselrf = KS_INIT_SELRF;
80  *selrf = defselrf;
81 }
Composite sequence object for slice-selective RF
Definition: KSFoundation.h:1484
#define KS_INIT_SELRF
Definition: KSFoundation.h:240

◆ ks_init_readtrap()

void ks_init_readtrap ( KS_READTRAP readtrap)

Resets a KS_READTRAP sequence object to its default value (KS_INIT_READTRAP)

Parameters
[out]readtrapPointer to KS_READTRAP
Returns
void
82  {
83  KS_READTRAP defread = KS_INIT_READTRAP;
84  *readtrap = defread;
85 }
#define KS_INIT_READTRAP
Definition: KSFoundation.h:215
Composite sequence object for data readout using a trapezoid gradient
Definition: KSFoundation.h:1573

◆ ks_init_phaser()

void ks_init_phaser ( KS_PHASER phaser)

Resets a KS_PHASER sequence object to its default value (KS_INIT_PHASER)

Parameters
[out]phaserPointer to KS_PHASER
Returns
void
86  {
87  KS_PHASER defphaser = KS_INIT_PHASER;
88  *phaser = defphaser;
89 }
#define KS_INIT_PHASER
Definition: KSFoundation.h:218
Composite sequence object for phase encoding using a trapezoid gradient
Definition: KSFoundation.h:1675

◆ ks_init_epi()

void ks_init_epi ( KS_EPI epi)

Resets a KS_EPI sequence object to its default value (KS_INIT_EPI)

Parameters
[out]epiPointer to KS_EPI
Returns
void
90  {
91  KS_EPI defepi = KS_INIT_EPI;
92  *epi = defepi;
93 }
#define KS_INIT_EPI
Definition: KSFoundation.h:223
Composite sequence object for EPI readout
Definition: KSFoundation.h:1836

◆ ks_init_dixon_asymreadwave()

void ks_init_dixon_asymreadwave ( KS_DIXON_ASYMREADWAVE asymreadwave)

Resets a KS_DIXON_ASYMREADWAVE sequence object to its default value (KS_INIT_DIXON_ASYMREADWAVE)

Parameters
[in,out]asymreadwavePointer to KS_DIXON_ASYMREADWAVE
Returns
void
95  {
97  *asymreadwave = def_asymreadwave;
98 }
#define KS_INIT_DIXON_ASYMREADWAVE
Definition: KSFoundation.h:230
Definition: KSFoundation.h:1851

◆ ks_init_dixon_dualreadtrap()

void ks_init_dixon_dualreadtrap ( KS_DIXON_DUALREADTRAP dual_readtrap)

Resets a KS_DIXON_DUALREADTRAP sequence object to its default value (KS_INIT_DIXON_DUALREADTRAP)

Parameters
[in,out]dual_readtrapPointer to KS_DIXON_DUALREADTRAP
Returns
void
100  {
102  *dual_readtrap = def_dual_readtrap;
103 }
#define KS_INIT_DIXON_DUALREADTRAP
Definition: KSFoundation.h:231
Definition: KSFoundation.h:1862

◆ ks_init_readwave()

void ks_init_readwave ( KS_READWAVE readwave)
104  {
105  KS_READWAVE def_readwave = KS_INIT_READWAVE;
106  *readwave = def_readwave;
107 }
Definition: KSFoundation.h:1591
#define KS_INIT_READWAVE
Definition: KSFoundation.h:229

◆ ks_init_gradrfctrl()

void ks_init_gradrfctrl ( KS_GRADRFCTRL gradrfctrl)

Resets KS_GRADRFCTRL to its default value (KS_INIT_GRADRFCTRL)

Parameters
[out]gradrfctrlPointer to KS_GRADRFCTRL
Returns
void
108  {
109  KS_GRADRFCTRL defgradrf = KS_INIT_GRADRFCTRL;
110  *gradrfctrl = defgradrf;
111 }
typedef struct that is a part of the KS_SEQ_CONTROL typedef struct, used internally to collect gradie...
Definition: KSFoundation.h:965
#define KS_INIT_GRADRFCTRL
Definition: KSFoundation.h:243

◆ ks_init_seqcontrol()

void ks_init_seqcontrol ( KS_SEQ_CONTROL seqcontrol)

Resets KS_SEQ_CONTROL to its default value (KS_INIT_SEQ_CONTROL)

Parameters
[out]seqcontrolPointer to KS_INIT_SEQ_CONTROL
Returns
void
112  {
113  KS_SEQ_CONTROL defseqcontrol = KS_INIT_SEQ_CONTROL;
114  *seqcontrol = defseqcontrol;
115 }
#define KS_INIT_SEQ_CONTROL
Definition: KSFoundation.h:245
A multi-purpose controller struct for each sequence module to facilitate RF scaling, SAR calculations, sequence duration and sequence switching
Definition: KSFoundation.h:1131

◆ ks_init_seqcollection()

void ks_init_seqcollection ( KS_SEQ_COLLECTION seqcollection)

Resets KS_SEQ_COLLECTION to its default value (KS_INIT_SEQ_COLLECTION)

Parameters
[out]seqcollectionPointer to KS_INIT_SEQ_COLLECTION
Returns
void
117  {
118  KS_SEQ_COLLECTION defseqcollection = KS_INIT_SEQ_COLLECTION;
119  *seqcollection = defseqcollection;
120 }
#define KS_INIT_SEQ_COLLECTION
Definition: KSFoundation.h:247
KS_SEQ_COLLECTION seqcollection
Definition: ksepi.e:80
Collection handle of all sequence modules involved in the pulse sequence, used for RF scaling...
Definition: KSFoundation.h:1299

◆ ks_init_slewratecontrol()

STATUS ks_init_slewratecontrol ( LOG_GRAD *  loggrd,
PHYS_GRAD *  phygrd,
float  srfact 
)

Changes existing (globally used) loggrd and phygrd structs (set up by inittargets() in e.g. GEReq_init_gradspecs())

If srfact < 1.0, both phygrd and loggrd are updated, and the gradients will be switched propotionally slower. This can be used to make the acquistion quieter. If srfact > 1.0, only loggrd will be updated. This could be used as an attempt to switch the gradients faster, but should be used with care as the risk for peripheral nerve stimulation (PNS) increases and the gradients may trip.

Parameters
[in,out]loggrdPointer to LOG_GRAD
[in,out]phygrdPointer to PHYS_GRAD
[in]srfactSlewrate factor (< 1.0 means less acquistic noise and reduced risk for PNS)
Returns
void
123  {
124 
125  if (fabs(srfact) < FLT_EPSILON) {
126  return ks_error("%s: slewrate factor can not be zero", __FUNCTION__);
127  }
128 
129  if (srfact < 1.0 && phygrd != NULL) {
130  phygrd->xrt = (int) ((float) phygrd->xrt / srfact);
131  phygrd->yrt = (int) ((float) phygrd->yrt / srfact);
132  phygrd->zrt = (int) ((float) phygrd->zrt / srfact);
133  }
134 
135  if (loggrd != NULL) {
136  loggrd->xrt = (int) ((float) loggrd->xrt / srfact);
137  loggrd->yrt = (int) ((float) loggrd->yrt / srfact);
138  loggrd->zrt = (int) ((float) loggrd->zrt / srfact);
139  }
140 
141  return SUCCESS;
142 }
STATUS ks_error(const char *format,...) __attribute__((format(printf
Common error message function for HOST and TGT
PHYS_GRAD phygrd
LOG_GRAD loggrd

◆ ks_eval_addtoseqcollection()

STATUS ks_eval_addtoseqcollection ( KS_SEQ_COLLECTION seqcollection,
KS_SEQ_CONTROL seqctrl 
)

Adds a sequence module (KS_SEQ_CONTROL) to the KS_SEQ_COLLECTION struct for later RF scaling and SAR calculations

The KS_SEQ_CONTROL object will be ignored if added previously or if its .duration field is zero. A zero duration of a KS_SEQ_CONTROL indicates that this sequence module should not be used with the current settings

Parameters
[in,out]seqcollectionPointer to the KS_SEQ_COLLECTION for the sequence
[in]seqctrlPointer to KS_SEQ_CONTROL
Return values
STATUSSUCCESS or FAILURE
149  {
150  int i;
151 
152  if (seqcollection == NULL) {
153  return ks_error("%s: seqcollection is NULL", __FUNCTION__);
154  }
155 
156  if (seqctrl->duration == 0) {
157  /* Return early if seqctrl.duration = 0 so we are not adding sequence modules with zero duration to the collection.
158  This is in concert with:
159  - not performing createseq() in KS_SEQLENGTH if seqctrl.duration = 0
160  - not playing sequence modules in scan() using ks_scan_playsequence() if seqctrl.duration = 0
161 
162  It is the task of the PG function (e.g. <seqmodule>_pg()) of the current sequence module to set seqctrl.duration > 0 based on the waveform content in that module
163  and <seqmodule>_pg() should therefore be called before this function.
164  Preferred method is to add the following to the end of <seqmodule>_pg():
165  #ifndef IPG
166  // HOST only:
167  ks_eval_seqctrl_setminduration(&seqctrl, tmploc.pos); // tmploc.pos now corresponds to the end of last gradient in the sequence
168  #endif
169  */
170  return SUCCESS;
171  }
172 
174  return ks_error("%s: Number of sequence modules has been exceeded (max: %d)", __FUNCTION__, KS_MAXUNIQUE_SEQUENCES);
175  }
176 
178  return ks_error("%s - (%s): seqcollection.mode = KS_SEQ_COLLECTION_LOCKED. Module must be added before GEReq_eval_TR(), GEReq_eval_rfscaling(), and GEReq_eval_checkTR_SAR()", __FUNCTION__, seqctrl->description);
179  }
180 
181  /* check for previous registrations of this sequence module. If found, return */
182  if (seqcollection->numseq > 0) {
183  for (i = 0; i < seqcollection->numseq; i++) {
184  if (seqcollection->seqctrlptr[i] == seqctrl)
185  return SUCCESS;
186  }
187  }
188 
189  /* register the sequence module */
191 
192  return SUCCESS;
193 }
STATUS ks_error(const char *format,...) __attribute__((format(printf
Common error message function for HOST and TGT
int32_t i
Definition: KSFoundation_tgt.c:1389
int mode
Definition: KSFoundation.h:1301
int duration
Definition: KSFoundation.h:1135
KS_SEQ_CONTROL * seqctrlptr[KS_MAXUNIQUE_SEQUENCES]
Definition: KSFoundation.h:1306
#define KS_MAXUNIQUE_SEQUENCES
Definition: KSFoundation.h:190
KS_SEQ_COLLECTION seqcollection
Definition: ksepi.e:80
KS_DESCRIPTION description
Definition: KSFoundation.h:1141
Definition: KSFoundation.h:1974
int numseq
Definition: KSFoundation.h:1300

◆ ks_eval_seqcollection_isadded()

STATUS ks_eval_seqcollection_isadded ( KS_SEQ_COLLECTION seqcollection,
KS_SEQ_CONTROL seqctrl 
)

Checks whether a sequence module (KS_SEQ_CONTROL) is a part of the KS_SEQ_COLLECTION struct

Parameters
[in]seqcollectionPointer to the KS_SEQ_COLLECTION for the sequence
[in]seqctrlPointer to KS_SEQ_CONTROL
Return values
STATUSSUCCESS or FAILURE
197  {
198  int i;
199 
200  if (seqctrl->duration == 0) {
201  return SUCCESS; /* it is not added, but we should still not complain as .duration = 0
202  means that it won't be used anyway */
203  }
204 
205  /* check for previous registrations of this sequence module. If found, return */
206  if (seqcollection->numseq > 0) {
207  for (i = 0; i < seqcollection->numseq; i++) {
208  if (seqcollection->seqctrlptr[i] == seqctrl)
209  return SUCCESS;
210  }
211  }
212 
213  return FAILURE;
214 
215 }
int32_t i
Definition: KSFoundation_tgt.c:1389
int duration
Definition: KSFoundation.h:1135
KS_SEQ_CONTROL * seqctrlptr[KS_MAXUNIQUE_SEQUENCES]
Definition: KSFoundation.h:1306
KS_SEQ_COLLECTION seqcollection
Definition: ksepi.e:80
int numseq
Definition: KSFoundation.h:1300

◆ ks_eval_wait()

STATUS ks_eval_wait ( KS_WAIT wait,
const char *const  desc,
int  maxduration 
)

Sets up a wait pulse using a KS_WAIT sequence object

This function should be called in cveval() and defines a wait pulse whose duration can be changed at scan-time up to .duration [us].

If ks_eval_wait() returns FAILURE, one must also make sure cveval() returns FAILURE, otherwise an error message created in ks_eval_wait() will not be seen in the UI.

Parameters
[out]waitPointer to the KS_WAIT object to be set up
[in]descA description (text string) of the KS_WAIT object. This description is used for pulse sequence generation (seen in the file generated by TimeHist in MGD Sim/WTools)
[in]maxdurationThe desired maximum duration in [us] of the KS_WAIT object
Return values
STATUSSUCCESS or FAILURE
219  {
220 
221  ks_init_wait(wait);
222 
223  if (desc == NULL || desc[0] == ' ') {
224  return ks_error("ks_eval_wait desc (2nd arg) cannot be NULL or begin with a space");
225  } else {
226  strncpy(wait->description, desc, KS_DESCRIPTION_LENGTH - 1);
227  }
228 
229 
230  wait->duration = duration;
231 
232  /* initialize counters to 0 */
233  wait->base.ninst = 0;
234  wait->base.ngenerated = 0;
235  wait->base.next = NULL;
236 
237  return SUCCESS;
238 }
void ks_init_wait(KS_WAIT *wait)
Resets a KS_WAIT sequence object to its default value (KS_INIT_WAIT)
Definition: KSFoundation_host.c:62
STATUS ks_error(const char *format,...) __attribute__((format(printf
Common error message function for HOST and TGT
int ninst
Definition: KSFoundation.h:409
KS_DESCRIPTION description
Definition: KSFoundation.h:458
#define KS_DESCRIPTION_LENGTH
Definition: KSFoundation.h:185
void * next
Definition: KSFoundation.h:411
KS_BASE base
Definition: KSFoundation.h:457
int duration
Definition: KSFoundation.h:459
int ngenerated
Definition: KSFoundation.h:410

◆ ks_eval_isirot()

STATUS ks_eval_isirot ( KS_ISIROT isirot,
const char *const  desc,
int  isinumber 
)

Sets up a KS_ISIROT object to be used for real-time coordinate rotations

See also ks_pg_isirot() and ks_scan_isirotate().

Parameters
[out]isirotPointer to the KS_ISIROT object to be set up
[in]descA description (text string) of the KS_ISIROT object
[in]isinumberA free ISI interrupt number in range [4,7]
Return values
STATUSSUCCESS or FAILURE
242  {
243  STATUS status;
244  char tmpdesc[KS_DESCRIPTION_LENGTH];
245 
246  if ((isinumber < 4) || (isinumber > 7)) {
247  return ks_error("%s: ISI number out of range (should be 4-7)", __FUNCTION__);
248  }
249 
250  sprintf(tmpdesc, "%s_isirotfun", desc);
251  status = ks_eval_wait(&isirot->waitfun, tmpdesc, RUP_GRD(KS_ISI_time));
252  if (status != SUCCESS) return status;
253 
254  sprintf(tmpdesc, "%s_isirotupdate", desc);
255  status = ks_eval_wait(&isirot->waitrot, tmpdesc, RUP_GRD(KS_ISI_rotupdatetime));
256  if (status != SUCCESS) return status;
257 
258  isirot->isinumber = isinumber;
259  isirot->duration = isirot->waitfun.duration + isirot->waitrot.duration;
260  isirot->counter = 0;
261  isirot->numinstances = 0;
262 
263  return SUCCESS;
264 }
int isinumber
Definition: KSFoundation.h:473
STATUS ks_error(const char *format,...) __attribute__((format(printf
Common error message function for HOST and TGT
#define KS_DESCRIPTION_LENGTH
Definition: KSFoundation.h:185
#define KS_ISI_rotupdatetime
Definition: KSFoundation.h:133
KS_WAIT waitfun
Definition: KSFoundation.h:470
STATUS ks_eval_wait(KS_WAIT *wait, const char *const desc, int duration)
Sets up a wait pulse using a KS_WAIT sequence object
Definition: KSFoundation_host.c:219
int counter
Definition: KSFoundation.h:475
int duration
Definition: KSFoundation.h:459
int numinstances
Definition: KSFoundation.h:476
KS_WAIT waitrot
Definition: KSFoundation.h:471
#define KS_ISI_time
Definition: KSFoundation.h:132
int duration
Definition: KSFoundation.h:474

◆ ks_eval_read()

STATUS ks_eval_read ( KS_READ read,
const char *const  desc 
)

Sets up a data acquisition window using a KS_READ sequence object

This function should be called in cveval() and defines a data acquisition window of a certain duration with some receiver bandwidth. Before calling this function, the following fields in the KS_READ object must be set:

  • .rbw: The desired receiver bandwidth / FOV in [kHz] (max: 250)
  • .duration: The duration in [us]

ks_eval_read() will validate the desired rBW and round it to the nearest valid value. Also the duration will be rounded up to fit a whole number of samples in the acquisition window.

To use an acquisition window with a gradient, see KS_READTRAP and ks_eval_readtrap()

If ks_eval_read() returns FAILURE, one must also make sure cveval() returns FAILURE, otherwise an error message created in ks_eval_read() will not be seen in the UI.

Parameters
[in,out]readPointer to the KS_READ object to be set up
[in]descA description (text string) of the KS_READ object. This description is used for pulse sequence generation (seen in the file generated by TimeHist in MGD Sim/WTools)
Return values
STATUSSUCCESS or FAILURE
270  {
271  char tmpdesc[KS_DESCRIPTION_LENGTH];
272  int duration;
273  float rbw;
274  int tsp;
275  int override_R1;
276  int noutputs;
277 
278  /* store desired inputs before resetting */
279  duration = read->duration;
280  rbw = read->rbw;
281  override_R1 = read->override_R1;
282  strcpy(tmpdesc, desc); /* copy in case the read->description has been passed in before ks_init_read wipes it */
283 
284  /* Reset all fields (after saving the desired info) */
285  ks_init_read(read);
286 
287  /* put back the input fields */
288  read->duration = duration;
289  read->rbw = ks_calc_nearestbw(rbw); /* round to nearest valid rBW */
290  read->override_R1 = override_R1;
291  tsp = ks_calc_bw2tsp(read->rbw);
292  strncpy(read->description, tmpdesc, KS_DESCRIPTION_LENGTH - 1);
293  read->description[KS_DESCRIPTION_LENGTH - 1] = 0;
294 
295  /* don't allow empty description or a description with leading space */
296  if (read->description == NULL || read->description[0] == ' ') {
297  return ks_error("ks_eval_read: read name (2nd arg) cannot be NULL or leading space");
298  }
299 
300  if (read->rbw <= 0) {
301  return ks_error("ks_eval_read: field 'rbw' (1st arg) is not set");
302  }
303  if (read->duration <= 2) {
304  return ks_error("ks_eval_read: field 'duration' (1st arg) must be a positive number in [us]");
305  }
306 
307  noutputs = CEIL_DIV(read->duration, tsp);
308  read->duration = noutputs * tsp;
309 
310  /* calculate read->filter (FILTER_INFO) */
311  if (ks_calc_filter(&read->filt, tsp, read->duration) == FAILURE)
312  return FAILURE;
313 
314  return SUCCESS;
315 }
float ks_calc_nearestbw(float bw)
Round receiver bandwidth to nearest valid value
Definition: KSFoundation_host.c:4740
void ks_init_read(KS_READ *read)
Resets a KS_READ sequence object to its default value (KS_INIT_READ)
Definition: KSFoundation_host.c:54
STATUS ks_calc_filter(FILTER_INFO *echortf, int tsp, int duration)
Sets up GE&#39;s FILTER_INFO struct based on dwell (sample point) time and duration of an acquisition win...
Definition: KSFoundation_host.c:4701
LONG override_R1
Definition: KSFoundation.h:737
STATUS ks_error(const char *format,...) __attribute__((format(printf
Common error message function for HOST and TGT
float rbw
Definition: KSFoundation.h:734
KS_DESCRIPTION description
Definition: KSFoundation.h:732
#define KS_DESCRIPTION_LENGTH
Definition: KSFoundation.h:185
int ks_calc_bw2tsp(float bw)
Convert receiver bandwidth to dwell time
Definition: KSFoundation_host.c:4722
int duration
Definition: KSFoundation.h:733
FILTER_INFO filt
Definition: KSFoundation.h:735

◆ ks_eval_trap_constrained()

STATUS ks_eval_trap_constrained ( KS_TRAP trap,
const char *const  desc,
float  ampmax,
float  slewrate,
int  minduration 
)

Sets up a trapezoid using a KS_TRAP sequence object with gradient constraints specified as input arguments

Before calling this function, the following field in the KS_TRAP object must be set:

  • .area: Desired gradient area in units of [(G/cm) * us]

ks_eval_trap_constrained() will make a trapezoid pulse with the desired area, constrained by the specified maximum gradient amplitude, slewrate, and minimum plateau time (args 3-5).

If ks_eval_trap_constrained() returns FAILURE, one must also make sure cveval() returns FAILURE, otherwise an error message created in ks_eval_trap_constrained() will not be seen in the UI.

There are three wrapper functions to this function (ks_eval_trap(), ks_eval_trap1(), ks_eval_trap2()) that have reduced number of input arguments, and calls ks_eval_trap_constrained() with different preset gradient constraints. ks_eval_trap() makes fewest assumptions, making it the most general choice.

Parameters
[in,out]trapPointer to the KS_TRAP object to be set up
[in]descA description (text string) of the KS_TRAP object. This description is used for pulse sequence generation (seen in the file generated by TimeHist in MGD Sim/WTools)
[in]ampmaxThe maximum allowed gradient amplitude ([G/cm])
[in]slewrateThe maximum allowed slewrate ([(G/cm) / us]). Value of 0.01 corresponds to 100 mT/m/s
[in]mindurationOptional minimum duration ([us]). A zero value is used to disregard this constraint
Return values
STATUSSUCCESS or FAILURE
319  {
320  double triangletrapezoid_area;
321  double maxramp_area;
322  double requested_area;
323  int maxramp_duration;
324  int sign_reqarea;
325  int minplateautime = KS_MINPLATEAUTIME;
326  char tmpdesc[KS_DESCRIPTION_LENGTH];
327 
328  /* store desired inputs before resetting the trap object */
329  requested_area = (double) trap->area;
330  strcpy(tmpdesc, desc); /* copy in case the trap->description has been passed in before ks_init_trap wipes it */
331 
332  /* Reset all fields and waveforms (after saving the desired area) */
333  ks_init_trap(trap);
334 
335  /* put back the input fields */
336  trap->area = requested_area;
337 
338  strncpy(trap->description, tmpdesc, KS_DESCRIPTION_LENGTH - 1);
339  trap->description[KS_DESCRIPTION_LENGTH - 1] = 0;
340 
341  /* don't allow empty description or a description with leading space */
342  if (trap->description == NULL || trap->description[0] == ' ') {
343  return ks_error("ks_eval_trap: trap name (2nd arg) cannot be NULL or leading space");
344  }
345 
346  if (areSame(requested_area, 0)) {
347  return SUCCESS;
348  }
349 
350  /* sign control: Store the sign of the requested area. Ignore all other signs */
351  sign_reqarea = (requested_area < 0) ? -1 : 1;
352  requested_area = fabs(requested_area);
353  slewrate = fabs(slewrate);
354  ampmax = fabs(ampmax);
355 
356  /* make sure the minimum duration is divisible by 8us */
357  minduration = RUP_FACTOR(minduration, 2 * GRAD_UPDATE_TIME);
358 
359 
360  /* area for one ramp from zero to max amplitude ('ampmax') */
361  maxramp_duration = RUP_GRD(ceil((double) ampmax / (double) slewrate)); /* [usec] */
362  maxramp_area = (double) ampmax * ((double) maxramp_duration) / 2.0; /* [G/cm * usec] */
363 
364  /* area for a trapezoid with a plateau time of 'minplateautime' and maximum amplitude ('ampmax') */
365  triangletrapezoid_area = ((double) minplateautime * (double) ampmax) + (2.0 * maxramp_area);
366 
367  if (requested_area > triangletrapezoid_area) {
368  /* ________......ampmax
369  / \
370  / \
371  / \
372  __/ \__
373 
374  We need max target amplitude and use a plateau time > minplateautime to reach required area. [amp = ampmax] */
375 
376  trap->ramptime = maxramp_duration;
377  trap->plateautime = (int) ceil((requested_area - 2.0 * maxramp_area) / (double) ampmax);
378 
379  /* Round *up* to nearest 8us to make the plateau contain an even # of points (which is always nice) */
380  trap->plateautime = RUP_FACTOR(trap->plateautime, 2 * GRAD_UPDATE_TIME);
381 
382 
383  } else {
384  /*
385  ......... ampmax
386 
387  _ amp
388  / \
389  / \
390  __/ \__
391 
392  We don't need max amplitude and will use a minimum plateau time of minplateautime
393  [amp = ramptime * slewrate] */
394 
395  trap->plateautime = minplateautime; /* Our minimum plateau time */
396 
397  /* Quadratic solution in 'ramptime':
398  slewrate*(ramptime)^2 + slewrate*minplateautime*(ramptime) - requested_area = 0
399  ...where: ramptime = unknownamp / slewrate <=> unknownamp = ramptime * slewrate */
400  trap->ramptime = ((int) ceil(pow(requested_area / (double) slewrate + ((double) minplateautime * (double) minplateautime) / 4.0, 0.5))) - minplateautime / 2;
401 
402  /* Round *up* to nearest GRAD_UPDATE_TIME (unit: usec) */
403  trap->ramptime = RUP_GRD(trap->ramptime);
404  }
405 
406 
407  /* amp (G/cm). Make the sign of the amp equal to that of the requested_area */
408  trap->amp = (float) (sign_reqarea * requested_area / ((double) (trap->ramptime + trap->plateautime)));
409 
410  /* amp watchdog */
411  if (fabs(trap->amp) > ampmax)
412  return ks_error("ks_eval_trap: amp (%.3f) of trap '%s' exceeds ampmax (%.3f)", trap->amp, trap->description, ampmax);
413 
414  /* store the duration */
415  trap->duration = trap->ramptime * 2 + trap->plateautime;
416 
417  /* if the duration is smaller than the minimum duration, increase the ramp times and reduce the gradient amp accordingly */
418  if (trap->duration < minduration) {
419  trap->ramptime += (minduration - trap->duration)/2;
420  trap->duration = trap->ramptime * 2 + trap->plateautime;
421  trap->amp = (float) (sign_reqarea * requested_area / ((double) (trap->ramptime + trap->plateautime)));
422  }
423 
424 
425  return SUCCESS;
426 
427 }
int plateautime
Definition: KSFoundation.h:584
#define areSame(a, b)
Definition: KSFoundation.h:119
STATUS ks_error(const char *format,...) __attribute__((format(printf
Common error message function for HOST and TGT
void ks_init_trap(KS_TRAP *trap)
Resets a KS_TRAP sequence object to its default value (KS_INIT_TRAP)
Definition: KSFoundation_host.c:58
#define KS_DESCRIPTION_LENGTH
Definition: KSFoundation.h:185
float area
Definition: KSFoundation.h:582
float amp
Definition: KSFoundation.h:581
KS_DESCRIPTION description
Definition: KSFoundation.h:580
#define KS_MINPLATEAUTIME
Definition: KSFoundation.h:137
int duration
Definition: KSFoundation.h:585
int ramptime
Definition: KSFoundation.h:583

◆ ks_eval_trap()

STATUS ks_eval_trap ( KS_TRAP trap,
const char *const  desc 
)

Sets up a trapezoid using a KS_TRAP sequence object with preset gradient constraints

Before calling this function, the following field in the KS_TRAP object must be set:

  • .area: Desired gradient area in units of [(G/cm) * us]

This is a wrapper function to ks_eval_trap_constrained() with gradient constraints set to allow this KS_TRAP to be simultaneously played on XGRAD, YGRAD, ZGRAD for any slice angulation.

Parameters
[in,out]trapPointer to the KS_TRAP object to be set up
[in]descA description (text string) of the KS_TRAP object. This description is used for pulse sequence generation (seen in the file generated by TimeHist in MGD Sim/WTools)
Return values
STATUSSUCCESS or FAILURE
434  {
435 
437 
438 }
float ks_syslimits_ampmax(LOG_GRAD loggrd)
Returns the maximum gradient amplitude that can be used on all gradient boards simultaneously
Definition: KSFoundation_common.c:201
STATUS ks_eval_trap_constrained(KS_TRAP *trap, const char *const desc, float ampmax, float slewrate, int minduration)
Sets up a trapezoid using a KS_TRAP sequence object with gradient constraints specified as input argu...
Definition: KSFoundation_host.c:319
LOG_GRAD loggrd
float ks_syslimits_slewrate(LOG_GRAD loggrd)
Returns the maximum slewrate that can be used on all gradient boards simultaneously
Definition: KSFoundation_common.c:248

◆ ks_eval_trap2()

STATUS ks_eval_trap2 ( KS_TRAP trap,
const char *const  desc 
)

Sets up a trapezoid using a KS_TRAP sequence object with preset gradient constraints

Before calling this function, the following field in the KS_TRAP object must be set:

  • .area: Desired gradient area in units of [(G/cm) * us]

This is a wrapper function to ks_eval_trap_constrained() with gradient constraints set to allow this KS_TRAP to be simultaneously played on up to two (2) logical gradients for any slice angulation.

Parameters
[in,out]trapPointer to the KS_TRAP object to be set up
[in]descA description (text string) of the KS_TRAP object. This description is used for pulse sequence generation (seen in the file generated by TimeHist in MGD Sim/WTools)
Return values
STATUSSUCCESS or FAILURE
440  {
441 
443 
444 }
STATUS ks_eval_trap_constrained(KS_TRAP *trap, const char *const desc, float ampmax, float slewrate, int minduration)
Sets up a trapezoid using a KS_TRAP sequence object with gradient constraints specified as input argu...
Definition: KSFoundation_host.c:319
LOG_GRAD loggrd
float ks_syslimits_ampmax2(LOG_GRAD loggrd)
Returns the maximum gradient amplitude that can be used on two gradient boards simultaneously
Definition: KSFoundation_common.c:205
float ks_syslimits_slewrate2(LOG_GRAD loggrd)
Returns the maximum slewrate that can be used on two gradient boards simultaneously
Definition: KSFoundation_common.c:252

◆ ks_eval_trap1()

STATUS ks_eval_trap1 ( KS_TRAP trap,
const char *const  desc 
)

Sets up a trapezoid using a KS_TRAP sequence object with preset gradient constraints

Before calling this function, the following field in the KS_TRAP object must be set:

  • .area: Desired gradient area in units of [(G/cm) * us]

This is a wrapper function to ks_eval_trap_constrained() with gradient constraints set to allow this KS_TRAP to only be played out on one (1) logical gradient at a time. No gradient may be active on another board while this trapezoid is played out.

Parameters
[in,out]trapPointer to the KS_TRAP object to be set up
[in]descA description (text string) of the KS_TRAP object. This description is used for pulse sequence generation (seen in the file generated by TimeHist in MGD Sim/WTools)
Return values
STATUSSUCCESS or FAILURE
446  {
447 
449 
450 }
STATUS ks_eval_trap_constrained(KS_TRAP *trap, const char *const desc, float ampmax, float slewrate, int minduration)
Sets up a trapezoid using a KS_TRAP sequence object with gradient constraints specified as input argu...
Definition: KSFoundation_host.c:319
float ks_syslimits_slewrate1(LOG_GRAD loggrd)
Returns the maximum slewrate that can be used on one gradient board at a time
Definition: KSFoundation_common.c:256
LOG_GRAD loggrd
float ks_syslimits_ampmax1(LOG_GRAD loggrd)
Returns the maximum gradient amplitude that can be used on one gradient board at a time...
Definition: KSFoundation_common.c:209

◆ ks_eval_trap_constrained_time_maxarea()

STATUS ks_eval_trap_constrained_time_maxarea ( KS_TRAP trap,
const char *const  desc,
float  ampmax,
float  slewrate,
float  maxarea,
int  derate_slewrate 
)
452  {
453  int requested_time;
454  int maxramp_duration;
455  char tmpdesc[KS_DESCRIPTION_LENGTH];
456  int sign = (maxarea < 0) ? -1 : 1;
457  strcpy(tmpdesc, desc); /* copy in case the trap->description has been passed in before ks_init_trap wipes it */
458 
459  /* make sure the minimum duration is divisible by 8us */
460  requested_time = RDN_FACTOR(trap->duration, 2 * GRAD_UPDATE_TIME);
461  /* Reset all fields and waveforms (after saving the desired area) */
462  ks_init_trap(trap);
463  strncpy(trap->description, tmpdesc, KS_DESCRIPTION_LENGTH - 1);
464  trap->description[KS_DESCRIPTION_LENGTH - 1] = 0;
465 
466  /* don't allow empty description or a description with leading space */
467  if (trap->description == NULL || trap->description[0] == ' ') {
468  return ks_error("%s: trap name (2nd arg) cannot be NULL or leading space", __FUNCTION__);
469  }
470  if (requested_time <= 0) {
471  return SUCCESS;
472  }
473 
474  slewrate = fabs(slewrate); /* [(G/cm) / us] */
475  ampmax = fabs(ampmax); /* [G/cm] */
476  maxramp_duration = RUP_GRD(ceil((double) ampmax / (double) slewrate)); /* [usec] */
477  if ((2*maxramp_duration + KS_MINPLATEAUTIME) > requested_time) {
478  /* Not enough time to make it to the plateau */
480  trap->ramptime = (requested_time - KS_MINPLATEAUTIME) / 2;
481  trap->amp = slewrate * trap->ramptime;
482  } else {
483  /* Enough time to reach plateau */
484  trap->amp = ampmax;
485  trap->ramptime = maxramp_duration;
486  trap->plateautime = (requested_time - 2 * trap->ramptime);
487  }
488 
489  trap->area = (trap->plateautime + trap->ramptime) * trap->amp * sign;
490  if (fabs(trap->area) > fabs(maxarea)) {
491  if (derate_slewrate == 1) {
492  trap->amp = maxarea / (trap->plateautime + trap->ramptime);
493  } else {
494  trap->ramptime = RUP_GRD( (int) ((requested_time - sqrtf(requested_time*requested_time - 4*maxarea/slewrate)) / 2.0));
495  trap->amp = trap->ramptime * slewrate;
496  trap->plateautime = (requested_time - 2 * trap->ramptime);
497  }
498  /* Adjust the amplitude to never go above maxarea */
499  trap->amp *= fabs(maxarea / ((trap->plateautime + trap->ramptime) * trap->amp));
500  trap->area = (trap->plateautime + trap->ramptime) * trap->amp * sign;
501  }
502 
503  trap->duration = trap->ramptime * 2 + trap->plateautime;
504  if (trap->duration != requested_time) {
505  ks_error("%s: Duration mismatch. this should not happen", __FUNCTION__);
506  }
507  return SUCCESS;
508 }
int plateautime
Definition: KSFoundation.h:584
STATUS ks_error(const char *format,...) __attribute__((format(printf
Common error message function for HOST and TGT
void ks_init_trap(KS_TRAP *trap)
Resets a KS_TRAP sequence object to its default value (KS_INIT_TRAP)
Definition: KSFoundation_host.c:58
#define KS_DESCRIPTION_LENGTH
Definition: KSFoundation.h:185
float area
Definition: KSFoundation.h:582
float amp
Definition: KSFoundation.h:581
KS_DESCRIPTION description
Definition: KSFoundation.h:580
#define KS_MINPLATEAUTIME
Definition: KSFoundation.h:137
int duration
Definition: KSFoundation.h:585
int ramptime
Definition: KSFoundation.h:583

◆ ks_eval_set_asym_padding()

STATUS ks_eval_set_asym_padding ( struct _readtrap_s *  readtrap,
float  wanted_paddingarea_pre,
float  wanted_paddingarea_post 
)
510  {
511  if (readtrap->rampsampling) {
512  ks_error("%s: No support for rampsampled readouts with asymmetric padding", __FUNCTION__);
513  return FAILURE;
514  }
515  float previous_paddingarea = readtrap->paddingarea;
516  float amp = readtrap->grad.amp;
517  float ramp_area = readtrap->grad.ramptime * amp/ 2.0;
518 
519  float actual_pre_area = isNotSet(wanted_paddingarea_pre) ? previous_paddingarea : FMax(2, ramp_area, wanted_paddingarea_pre);
520  float actual_post_area = isNotSet(wanted_paddingarea_post) ? previous_paddingarea : FMax(2, ramp_area, wanted_paddingarea_post);
521 
522  int plateau_delay_pre = (int)((actual_pre_area - ramp_area)/ amp);
523  int plateau_delay_post = (int)((actual_post_area - ramp_area)/ amp);
524 
525  readtrap->grad.plateautime = RUP_GRD(readtrap->acq.duration + plateau_delay_pre + plateau_delay_post);
526  readtrap->acqdelay = RUP_FACTOR(readtrap->grad.ramptime + plateau_delay_pre, 2);
527 
528  /* Update convinient fields */
529  readtrap->area2center += actual_pre_area - previous_paddingarea - ramp_area;
530  readtrap->paddingarea = actual_pre_area - ramp_area;
531  readtrap->grad.duration = RUP_GRD(2*readtrap->grad.ramptime + readtrap->grad.plateautime);
532  readtrap->time2center = RUP_GRD((int) ((readtrap->area2center - ramp_area) / amp + readtrap->grad.ramptime));
533  return SUCCESS;
534 }
STATUS ks_error(const char *format,...) __attribute__((format(printf
Common error message function for HOST and TGT
#define isNotSet(a)
Definition: KSFoundation.h:124

◆ ks_eval_trap1p()

STATUS ks_eval_trap1p ( KS_TRAP trap,
const char *const  desc 
)

Sets up a trapezoid using a KS_TRAP sequence object with physical gradient constraints (invariant to slice angulation)

Before calling this function, the following field in the KS_TRAP object must be set:

  • .area: Desired gradient area in units of [(G/cm) * us]

This is a wrapper function to ks_eval_trap_constrained() with physical gradient constraints set to allow this KS_TRAP to only be played out on one (1) logical gradient at a time. No gradient may be active on another board while this trapezoid is played out.

Parameters
[in,out]trapPointer to the KS_TRAP object to be set up
[in]descA description (text string) of the KS_TRAP object. This description is used for pulse sequence generation (seen in the file generated by TimeHist in MGD Sim/WTools)
Return values
STATUSSUCCESS or FAILURE
536  {
537 
539 
540 }
float ks_syslimits_ampmax1p(LOG_GRAD loggrd)
Returns the maximum gradient amplitude on the physical gradient axes
Definition: KSFoundation_common.c:214
STATUS ks_eval_trap_constrained(KS_TRAP *trap, const char *const desc, float ampmax, float slewrate, int minduration)
Sets up a trapezoid using a KS_TRAP sequence object with gradient constraints specified as input argu...
Definition: KSFoundation_host.c:319
LOG_GRAD loggrd
float ks_syslimits_slewrate1p(LOG_GRAD loggrd)
Returns the maximum gradient amplitude on the physical gradient axes
Definition: KSFoundation_common.c:261

◆ ks_eval_readtrap_constrained()

STATUS ks_eval_readtrap_constrained ( KS_READTRAP readtrap,
const char *const  desc,
float  ampmax,
float  slewrate 
)

Sets up an acquisition window with a trapezoid, subject to gradient constraints specified as input arguments

Before calling this function, the following fields in the KS_READTRAP object must be set:

  1. .fov: Desired image Field-of-View (FOV) in [mm] along the sequence board one intends to place the KS_READTRAP on (typically XGRAD)
  2. .res: Number of pixels within the FOV
  3. .acq.rbw: Receiver bandwidth (rBW) in [kHz/FOV]. Maximum is 250
  4. .rampsampling: If non-zero, data acquisition will be performed on the ramps of the trapezoid as well. This reduces the readout time, especially for high rBWs.
  5. .acqdelay: Needs to be set only if .rampsampling = 1. The .acqdelay (in [us]) will dictate the time from the start of the ramp-up until data acquisition will begin
  6. .nover: Number of 'overscans'. If 0, a full echo will be generated (filling k-space). If > 0, fractional echo (i.e. shorter TE) will be set up with .nover number of extra sample points beyond .res/2. The total number of samples will be .res/2 + .nover. Values of .nover between 1 and about 16 should be avoided since half Fourier reconstruction methods will likely have difficulties.

If the KS_READTRAP object is initialized with KS_INIT_READTRAP, the fields .nover, .rampsampling and .acqdelay will all be zero (steps 4-6 can be skipped)

Based on the information in the KS_READTRAP object, ks_eval_readtrap_constrained() will set up its .grad trapezoid (KS_TRAP) and its acquisition window .acq (KS_READ), constrained by the specified maximum gradient amplitude, slewrate, and minimum plateau time (args 3-5).

If .rampsampling = 1, ks_eval_readtrap_constrained() will also copy the shape of .grad to .omega (also a KS_TRAP). This is needed for FOV-offsets in the readout direction when data acquisition is done on both the ramps and on the plateau, and ks_pg_readtrap() and ks_scan_offsetfov() will handle the .omega KS_TRAP so that the intended FOV shift in [mm] will be performed.

ks_eval_readtrap_constrained() will also fill in the convenience fields .area2center and .time2center properly. .area2center is useful to use as .area for a read dephaser, and .time2center makes sequence timing calculations easier. Both fields take partial/full Fourier (.nover) into account.

If ks_eval_readtrap_constrained() returns FAILURE, one must also make sure cveval() returns FAILURE, otherwise an error message created in ks_eval_readtrap_constrained() will not be seen in the UI.

There are three wrapper functions to this function (ks_eval_readtrap(), ks_eval_readtrap1(), ks_eval_readtrap2()) that have reduced number of input arguments, and calls ks_eval_readtrap_constrained() with different preset gradient constraints. ks_eval_readtrap() makes fewest assumptions, making it the most general choice.

Parameters
[in,out]readtrapPointer to the KS_READTRAP object to be set up
[in]descA description (text string) of the KS_READTRAP object. This description is used for pulse sequence generation (seen in the file generated by TimeHist in MGD Sim/WTools)
[in]ampmaxThe maximum allowed gradient amplitude ([G/cm])
[in]slewrateThe maximum allowed slewrate ([(G/cm) / us]). Value of 0.01 corresponds to 100 mT/m/s
Return values
STATUSSUCCESS or FAILURE
545  {
546  float minfov;
547  int tsp = 0;
548  float readpixelarea;
549  int nreadpixels;
550  float nonacq_readarea;
551  float ampmax_kspace_speedlimit;
552 
553  /* This function is for setting up readout trapezoid with fixed amplitude based on FOV and rBW */
554 
555  if (desc == NULL || desc[0] == ' ') {
556  return ks_error("ks_eval_readtrap: desc (2nd arg) cannot be NULL");
557  }
558  if (areSame(readtrap->acq.rbw, 0) || isNotSet(readtrap->acq.rbw)) {
559  return ks_error("ks_eval_readtrap: field 'acq.rbw' (1st arg) is not set");
560  }
561 
562  /* reset KS_TRAP's */
563  ks_init_trap(&readtrap->grad);
564  ks_init_trap(&readtrap->omega);
565 
566 
567  /* round rBW to nearest valid value */
568  readtrap->acq.rbw = ks_calc_nearestbw(readtrap->acq.rbw);
569  tsp = ks_calc_bw2tsp(readtrap->acq.rbw); /* us per sample point */
570 
571  minfov = ks_calc_minfov(ampmax, tsp);
572 
573  /* Programmer's error */
574  if (readtrap->res % 2 || readtrap->res <= 0 || readtrap->res > 2048) {
575  return ks_error("ks_eval_readtrap(%s): field 'res' must be even and in the range 2-2048", desc);
576  }
577  if (readtrap->fov < 10 || readtrap->fov > 600) {
578  return ks_error("ks_eval_readtrap(%s): field 'fov' must be in the range 10-600 mm", desc);
579  }
580  if (abs(readtrap->nover) > readtrap->res / 2) {
581  return ks_error("ks_eval_readtrap(%s): field 'nover' may not exceed res/2", desc);
582  }
583  if (readtrap->acq.rbw > 250) {
584  return ks_error("ks_eval_readtrap(%s): rBW cannot exceed +/- 250 kHz/FOV", desc);
585  }
586  /* Operator's error */
587  if (readtrap->fov < minfov && readtrap->rampsampling == 0) {
588  return ks_error("%s: Please increase the FOV to %.1f [cm] or decrease the rBW", desc, minfov / 10.0);
589  }
590 
591  if (abs(readtrap->nover)) {
592  nreadpixels = readtrap->res / 2 + abs(readtrap->nover);
593  } else {
594  nreadpixels = readtrap->res;
595  }
596  if (nreadpixels % 2) {
597  return ks_error("ks_eval_readtrap(%s): number of sampling points must be even (%d)", desc, nreadpixels);
598  }
599 
600 
601  if (readtrap->rampsampling == 1) { /* rampsampling */
602 
603  /* Gradient area necessary to move one pixel in k-space in the read direction */
604  readpixelarea = ks_calc_fov2gradareapixel(readtrap->fov); /* [(G/cm)*usec] */
605 
606  /* Speed limit the readout (i.e. put a cap on the readout amplitude) based on the FOV and the chosen rBW */
607  ampmax_kspace_speedlimit = FMin(2, readpixelarea / tsp, ampmax);
608 
609  /* delay of ACQ start relative to the beginning of the attack ramp.
610  For rampsampled cases, this value should be at least one dwell time (tsp) */
611  if (readtrap->acqdelay < 2 * tsp) {
612  readtrap->acqdelay = 2 * tsp;
613  }
614 
615  readtrap->acqdelay = RUP_FACTOR(readtrap->acqdelay, 2);
616 
617  /* gradient area before and after the acq window */
618  nonacq_readarea = slewrate /* [G/cm/usec] */ * (readtrap->acqdelay) * (readtrap->acqdelay) /* [usec^2] */; /* from both sides of the readout */
619 
620  /* required gradient area: gradient area needed during the acq window + nonacq_readarea */
621  readtrap->grad.area = (readpixelarea * nreadpixels) + nonacq_readarea;
622 
623  if (ks_eval_trap_constrained(&readtrap->grad, desc, ampmax_kspace_speedlimit, slewrate, 0) == FAILURE)
624  return FAILURE;
625 
626 
627  if (readtrap->grad.ramptime > readtrap->acqdelay) {
628  /* rampsampling attempt ok, continue calculating area2center and time2center */
629 
630  if (readtrap->nover > 0) { /* partial Fourier on the first part of the readout */
631  readtrap->area2center = readpixelarea * readtrap->nover + nonacq_readarea / 2;
632  } else { /* Full Fourier, or partial Fourier on the last part of the readout */
633  readtrap->area2center = readpixelarea * readtrap->res / 2 + nonacq_readarea / 2;
634  }
635 
636  /* area = (s*t^2)/2 [G/cm/usec] * [usec^2] */
637  if (readtrap->area2center < slewrate * (readtrap->grad.ramptime * readtrap->grad.ramptime / 2.0)) {
638  /* k-space center is on the attack ramp */
639  readtrap->time2center = (int) sqrt(readtrap->area2center * 2.0 / slewrate);
640  } else {
641  float arealeftonplateau = readtrap->area2center - (readtrap->grad.ramptime * readtrap->grad.amp / 2.0);
642  readtrap->time2center = (int) readtrap->grad.ramptime + (arealeftonplateau / readtrap->grad.amp); /* whole ramp + some plateau time */
643  }
644 
645  /* round up the readout window time to the nearest multiple of '2*tsp' to always make filt.outputs even */
646  readtrap->acq.duration = RUP_FACTOR(readtrap->grad.duration - readtrap->acqdelay * 2, (int) (2 * tsp));
647 
648  /* update 'acqdelay' due to this potential roundoff */
649  readtrap->acqdelay = (readtrap->grad.duration - readtrap->acq.duration) / 2;
650 
651  } else {
652 
653  readtrap->rampsampling = 0; /* let's skip ramp sampling then (and be caught by the next non-rampsampled case) */
654 
655  }
656 
657  } /* rampsampling */
658 
659 
660 
661 
662  if (readtrap->rampsampling == 0) { /* No rampsampling */
663 
664  if (desc[0] == ' ') {
665  ks_init_trap(&readtrap->grad);
666  return ks_error("ks_eval_readtrap: desc (2nd arg) cannot begin with a space");
667  } else {
668  strncpy(readtrap->grad.description, desc, KS_DESCRIPTION_LENGTH - 1);
669  readtrap->grad.description[KS_DESCRIPTION_LENGTH - 1] = 0;
670  }
671 
672  /* Error if requested constraints cannot be fullfilled */
673  if (readtrap->fov < minfov) {
674  return ks_error("%s: %s - Please increase the FOV to %.1f [cm] or decrease the rBW", __FUNCTION__, desc, minfov / 10.0);
675  }
676 
677  readtrap->grad.amp = ((float) (1.0 / (GAM * tsp * 1e-6) * (10.0 / readtrap->fov))); /* [G/cm] */
678  readtrap->grad.ramptime = RUP_GRD(readtrap->grad.amp / slewrate); /* [usec] */
679  readtrap->acq.duration = RUP_GRD(nreadpixels * tsp);
680 
681  /* adjust plateau time to account for padding */
682  const float ramparea = readtrap->grad.ramptime * readtrap->grad.amp / 2.0;
683  const int extradelay = RUP_GRD(FMax(2, readtrap->paddingarea - ramparea, 0.0) / readtrap->grad.amp);
684  readtrap->grad.plateautime = readtrap->acq.duration + 2*extradelay; /* [usec] */ /* Nkx * dwell time */
685 
686  readtrap->grad.duration = readtrap->grad.plateautime + readtrap->grad.ramptime * 2; /* [usec] */
687  readtrap->grad.area = (readtrap->grad.plateautime + readtrap->grad.ramptime) * readtrap->grad.amp; /* rounding effects */
688 
689  /* ACQ starts after the attack ramp */
690  readtrap->acqdelay = readtrap->grad.ramptime + extradelay;
691 
692  const int pixelstocenter = readtrap->nover > 0 ?
693  readtrap->nover : /* partial Fourier on the first part of the readout */
694  readtrap->res/2; /* Full Fourier, or partial Fourier on the last part of the readout */
695 
696  readtrap->area2center = (readtrap->grad.ramptime/2 + tsp * pixelstocenter + extradelay) * readtrap->grad.amp;
697  readtrap->time2center = readtrap->grad.ramptime + tsp * pixelstocenter + extradelay;
698 
699  } /* no rampsampling */
700 
701 
702 
703 
704  /* round up to nearest multiple of GRAD_UPDATE_TIME (4us) */
705  readtrap->time2center = RUP_GRD(readtrap->time2center);
706 
707 
708  /* setup acq window */
709  /* readtrap->acq.rbw is set > 0 by the calling function */
710  KS_DESCRIPTION read_description;
711  sprintf(read_description, "%s.echo", readtrap->grad.description);
712  if (ks_eval_read(&readtrap->acq, read_description) == FAILURE) {
713  return FAILURE;
714  }
715 
716  /* for rampsampling, create an omega waveform (for FOV shifts in the freq (read) direction for rampsampling) */
717  if (readtrap->rampsampling) {
718  readtrap->omega = readtrap->grad; /* copy the shape of the readout */
719  readtrap->omega.amp = 1.0; /* just unity value for conformance. It does not matter during scanning, since ks_scan_omegatrap_hz() ignores this value */
720  readtrap->omega.area = 0.0; /* Omega area has no meaning */
721  sprintf(readtrap->omega.description, "%s.omega", readtrap->grad.description);
722  } else {
723  readtrap->omega.duration = 0;
724  }
725 
726 
727  return SUCCESS;
728 }
int plateautime
Definition: KSFoundation.h:584
float ks_calc_nearestbw(float bw)
Round receiver bandwidth to nearest valid value
Definition: KSFoundation_host.c:4740
#define areSame(a, b)
Definition: KSFoundation.h:119
float ks_calc_fov2gradareapixel(float fov)
Calculates the gradient area needed to move one pixel in k-space
Definition: KSFoundation_common.c:346
float ks_calc_minfov(float ampmax, int tsp)
Calculates the minimum readout FOV
Definition: KSFoundation_common.c:319
STATUS ks_error(const char *format,...) __attribute__((format(printf
Common error message function for HOST and TGT
void ks_init_trap(KS_TRAP *trap)
Resets a KS_TRAP sequence object to its default value (KS_INIT_TRAP)
Definition: KSFoundation_host.c:58
float fov
Definition: KSFoundation.h:1576
STATUS ks_eval_trap_constrained(KS_TRAP *trap, const char *const desc, float ampmax, float slewrate, int minduration)
Sets up a trapezoid using a KS_TRAP sequence object with gradient constraints specified as input argu...
Definition: KSFoundation_host.c:319
float rbw
Definition: KSFoundation.h:734
KS_TRAP grad
Definition: KSFoundation.h:1586
float paddingarea
Definition: KSFoundation.h:1581
float GAM
#define KS_DESCRIPTION_LENGTH
Definition: KSFoundation.h:185
KS_TRAP omega
Definition: KSFoundation.h:1587
int res
Definition: KSFoundation.h:1577
int ks_calc_bw2tsp(float bw)
Convert receiver bandwidth to dwell time
Definition: KSFoundation_host.c:4722
STATUS ks_eval_read(KS_READ *read, const char *const desc)
Sets up a data acquisition window using a KS_READ sequence object
Definition: KSFoundation_host.c:270
int duration
Definition: KSFoundation.h:733
float area
Definition: KSFoundation.h:582
float amp
Definition: KSFoundation.h:581
KS_DESCRIPTION description
Definition: KSFoundation.h:580
int nover
Definition: KSFoundation.h:1579
int rampsampling
Definition: KSFoundation.h:1578
KS_READ acq
Definition: KSFoundation.h:1574
int duration
Definition: KSFoundation.h:585
char KS_DESCRIPTION[KS_DESCRIPTION_LENGTH]
Definition: KSFoundation.h:261
int time2center
Definition: KSFoundation.h:1585
float area2center
Definition: KSFoundation.h:1584
#define isNotSet(a)
Definition: KSFoundation.h:124
int acqdelay
Definition: KSFoundation.h:1580
int ramptime
Definition: KSFoundation.h:583

◆ ks_eval_readtrap()

STATUS ks_eval_readtrap ( KS_READTRAP readtrap,
const char *const  desc 
)

Sets up an acquisition window with a trapezoid with preset gradient constraints

This is a wrapper function to ks_eval_readtrap_constrained() with gradient constraints set to allow this KS_READTRAP to simultaneously be played on XGRAD, YGRAD, ZGRAD for any slice angulation. This is the generic and safest configuration, but for readouts with small FOV and high rBW, the gradient limits may be reached, especially for double oblique slices. See ks_eval_readtrap_constrained() for details on fields that need to be set before calling this function.

Parameters
[in,out]readtrapPointer to the KS_READTRAP object to be set up
[in]descA description (text string) of the KS_READTRAP object. This description is used for pulse sequence generation (seen in the file generated by TimeHist in MGD Sim/WTools)
Return values
STATUSSUCCESS or FAILURE
733  {
734 
735  return ks_eval_readtrap_constrained(readtrap, desc,
737 
738 }
float ks_syslimits_ampmax(LOG_GRAD loggrd)
Returns the maximum gradient amplitude that can be used on all gradient boards simultaneously
Definition: KSFoundation_common.c:201
STATUS ks_eval_readtrap_constrained(KS_READTRAP *readtrap, const char *const desc, float ampmax, float slewrate)
Sets up an acquisition window with a trapezoid, subject to gradient constraints specified as input ar...
Definition: KSFoundation_host.c:545
LOG_GRAD loggrd
float ks_syslimits_slewrate(LOG_GRAD loggrd)
Returns the maximum slewrate that can be used on all gradient boards simultaneously
Definition: KSFoundation_common.c:248

◆ ks_eval_readtrap2()

STATUS ks_eval_readtrap2 ( KS_READTRAP readtrap,
const char *const  desc 
)

Sets up an acquisition window with a trapezoid with preset gradient constraints

This is a wrapper function to ks_eval_readtrap_constrained() with gradient constraints set to allow this KS_TRAP to simultaneously be played on up to two (2) logical gradients for any slice angulation. For double oblique slice angulations, this function will allow for smaller FOVs and higher rBWs than ks_eval_readtrap().

See ks_eval_readtrap_constrained() for details on fields that need to be set before calling this function.

Parameters
[in,out]readtrapPointer to the KS_READTRAP object to be set up
[in]descA description (text string) of the KS_READTRAP object. This description is used for pulse sequence generation (seen in the file generated by TimeHist in MGD Sim/WTools)
Return values
STATUSSUCCESS or FAILURE
741  {
742 
743  return ks_eval_readtrap_constrained(readtrap, desc,
745 
746 }
STATUS ks_eval_readtrap_constrained(KS_READTRAP *readtrap, const char *const desc, float ampmax, float slewrate)
Sets up an acquisition window with a trapezoid, subject to gradient constraints specified as input ar...
Definition: KSFoundation_host.c:545
LOG_GRAD loggrd
float ks_syslimits_ampmax2(LOG_GRAD loggrd)
Returns the maximum gradient amplitude that can be used on two gradient boards simultaneously
Definition: KSFoundation_common.c:205
float ks_syslimits_slewrate2(LOG_GRAD loggrd)
Returns the maximum slewrate that can be used on two gradient boards simultaneously
Definition: KSFoundation_common.c:252

◆ ks_eval_readtrap1()

STATUS ks_eval_readtrap1 ( KS_READTRAP readtrap,
const char *const  desc 
)

Sets up an acquisition window with a trapezoid with preset gradient constraints

This is a wrapper function to ks_eval_readtrap_constrained() with gradient constraints set to allow this KS_TRAP to only be played out on one (1) logical gradient at a time. No gradient may be active on another board while this trapezoid is played out. For double oblique slice angulations, this function will allow for smaller FOVs and higher rBWs than ks_eval_readtrap() and ks_eval_readtrap2(). See ks_eval_readtrap_constrained() for details on fields that need to be set before calling this function.

Parameters
[in,out]readtrapPointer to the KS_READTRAP object to be set up
[in]descA description (text string) of the KS_READTRAP object. This description is used for pulse sequence generation (seen in the file generated by TimeHist in MGD Sim/WTools)
Return values
STATUSSUCCESS or FAILURE
748  {
749 
750  return ks_eval_readtrap_constrained(readtrap, desc,
752 
753 }
float ks_syslimits_slewrate1(LOG_GRAD loggrd)
Returns the maximum slewrate that can be used on one gradient board at a time
Definition: KSFoundation_common.c:256
STATUS ks_eval_readtrap_constrained(KS_READTRAP *readtrap, const char *const desc, float ampmax, float slewrate)
Sets up an acquisition window with a trapezoid, subject to gradient constraints specified as input ar...
Definition: KSFoundation_host.c:545
LOG_GRAD loggrd
float ks_syslimits_ampmax1(LOG_GRAD loggrd)
Returns the maximum gradient amplitude that can be used on one gradient board at a time...
Definition: KSFoundation_common.c:209

◆ ks_eval_readtrap_constrained_sample_duration()

STATUS ks_eval_readtrap_constrained_sample_duration ( struct _readtrap_s *  readtrap,
const char *const  desc,
const float  ampmax,
const float  slewrate,
const int  t_A,
const int  sample_duration,
const float  area_post,
const float  area_total 
)
762  {
763  int nreadpixels;
764  float amp;
765  int t_Sr_pre = 0;
766  int t_Sp = 0;
767  int t_Sr_post = 0;
768  int t_Ep = 0;
769  int t_Er = 0;
770  int time_to_center = 0;
771  /* reset KS_TRAP's */
772  ks_init_trap(&readtrap->grad);
773  ks_init_trap(&readtrap->omega);
774 
775  /* Description */
776  strncpy(readtrap->grad.description, desc, KS_DESCRIPTION_LENGTH - 1);
777 
778  /* Programmer's error */
779  if (readtrap->res % 2 || readtrap->res <= 0 || readtrap->res > 2048) {
780  return ks_error("ks_eval_readtrap(%s): field 'res' must be even and in the range 2-2048", desc);
781  }
782  if (readtrap->fov < 10 || readtrap->fov > 600) {
783  return ks_error("ks_eval_readtrap(%s): field 'fov' must be in the range 10-600 mm", desc);
784  }
785  if (abs(readtrap->nover) > readtrap->res / 2) {
786  return ks_error("ks_eval_readtrap(%s): field 'nover' may not exceed res/2", desc);
787  }
788  if (abs(readtrap->nover)) {
789  nreadpixels = readtrap->res / 2 + abs(readtrap->nover);
790  } else {
791  nreadpixels = readtrap->res;
792  }
793  if (nreadpixels % 2) {
794  return ks_error("ks_eval_readtrap(%s): number of sampling points must be even (%d)", desc, nreadpixels);
795  }
796 
797  float readpixelarea = ks_calc_fov2gradareapixel(readtrap->fov);
798 
799  /* First assume that both ramps can be used for data sampling. */
800  /* delay of ACQ start relative to the beginning of the attack ramp.
801  For rampsampled cases, this value should be at least one dwell time (tsp) */
802 
803  /* A is the ramp area where no sampling occurs */
804  float A = 0.5 * slewrate * t_A * t_A;
805 
806  /* Calculate the required trail time, i.e. the time from end of sampling until end of gradient */
807  int t_ErA = sqrt(2*area_post/slewrate);
808 
809  /* tE_r is the time from end of sampling until start of A */
810  t_Er = t_ErA - t_A;
811 
812  /* Total duration */
813  int t_total = sample_duration + t_ErA;
814 
815  /* Sample area = B + C */
816  /* Sample area to center is B plus the part of C up to k-space center */
817  float sample_area = readpixelarea * nreadpixels; /* G/cm * us*/
818  float area_to_center = A + readpixelarea * abs(readtrap->nover); /* Assumes center-out sampling */
819 
820  /* The total area is known, as well as the duration and slewrate. Calculate the ramp time */
821  int t_ramp = (t_total - sqrtf(t_total*t_total - 4.0 * area_total/slewrate)) / 2.0;
822  t_ramp = RUP_FACTOR(t_ramp, 4);
823  /* Check the dual ramp sampling assumption */
824  if (t_ErA > t_ramp) {
825  /* There is not enough ramp area to cover the required area_post.
826  Recalculate t_ramp as there will only be one ramp sampled side: */
827  t_Sr_pre = sample_duration - t_A - sqrtf( (slewrate*(sample_duration*sample_duration - t_A*t_A) -2*sample_area)/ slewrate);
828  if (t_Sr_pre < 0) { return FAILURE; }
829  t_ramp = RUP_FACTOR(t_A + t_Sr_pre, 4);
830  t_Sr_pre = t_ramp - t_A;
831  amp = slewrate * t_ramp;
832  float area_ramp = t_ramp * amp / 2;
833  float area_plateau = area_total - 2*area_ramp;
834  int plateau_time = RUP_FACTOR( (int) (area_plateau / amp), 4);
835  t_Sp = sample_duration - t_ramp;
836  t_Ep = (area_post - area_ramp) / amp;
837 
838  readtrap->grad.ramptime = t_ramp;
839  readtrap->grad.plateautime = plateau_time;
840 
841 
842  /* Doesn't matter */
843  t_Sr_post = 0;
844  t_Er = t_Sr_pre;
845  t_ErA = t_ramp; /* Doesn't matter */
846  t_total = 2*t_ramp + t_Sp + t_Ep; /* Doesn't matter */
847 
848  ks_error("%s: Single ramp sample case", __FUNCTION__);
849  /* Calculate time to center */
850  if (area_ramp > area_to_center) { /* Echo occurs on ramp up */
851  time_to_center = sqrtf(2*area_to_center/slewrate);
852  if (time_to_center < t_A) {
853  ks_error("%s: Echo occurs before sampling (ramptime: %.2f, time_to_center %.2f)", __FUNCTION__, t_A/1000.0f, time_to_center/1000.0f);
854  return FAILURE;
855  }
856  } else { /* Echo occurs on plateau */
857  float plateau_time_to_center = (area_to_center - area_ramp) / amp;
858  time_to_center = t_ramp + plateau_time_to_center;
859  }
860  } else { /* Dual ramp sampling holds */
861  /* Double ramp sampled case - No extra area on plateau */
862  ks_error("%s: Double ramp sample case", __FUNCTION__);
863  amp = slewrate * t_ramp;
864  float area_ramp = t_ramp * amp / 2;
865  float area_plateau = area_total - 2*area_ramp;
866  t_Sp = RUP_FACTOR( (int) (area_plateau / amp), 4);
867  t_Sr_pre = t_ramp - t_A;
868  t_Sr_post = t_ramp - t_A - t_Er;
869  t_Ep = 0;
870  readtrap->grad.ramptime = RUP_FACTOR(t_ramp, 4);
871  readtrap->grad.plateautime = RUP_FACTOR(t_Sp + t_Ep, 4);
872  /* t_Er already set */
873 
874  area_plateau = t_Sp * amp;
875  if (area_ramp > area_to_center) { /* Echo occurs on ramp up */
876  time_to_center = sqrtf(2*area_to_center/slewrate);
877  if (time_to_center < t_A) {
878  ks_error("%s: Echo occurs before sampling (ramptime: %.2f, time_to_center %.2f)", __FUNCTION__, t_A/1000.0f, time_to_center/1000.0f);
879  return FAILURE;
880  }
881  } else if (area_to_center <= (area_ramp + area_plateau)) { /* Echo occurs on plateau */
882  float plateau_time_to_center = (area_to_center - area_ramp) / amp;
883  time_to_center = t_ramp + plateau_time_to_center;
884  } else if (area_to_center <= (2*area_ramp + area_plateau)) { /* Echo occurs on ramp down */
885  float rem_area = area_to_center - area_plateau - area_ramp;
886  float time_to_center_from_ramp_down = amp/slewrate - sqrtf( powf(amp/slewrate,2) - 2*rem_area/slewrate);
887  time_to_center = t_ramp + t_Sp + time_to_center_from_ramp_down;
888  } else {
889  ks_error("%s: There is no echo occurring on this waveform", __FUNCTION__);
890  return FAILURE;
891  }
892  }
893 
894  if (amp > ampmax) {
895  ks_error("%s: Amp limit exceeded (%.2f > %.2f)", __FUNCTION__, amp, ampmax);
896  return FAILURE;
897  }
898 
899  /* Setup object properties */
900  readtrap->acqdelay = t_A;
901  readtrap->grad.amp = amp;
902  readtrap->grad.duration = 2*readtrap->grad.ramptime + readtrap->grad.plateautime;
903  readtrap->grad.area = (readtrap->grad.ramptime + readtrap->grad.plateautime) * amp;
904  ks_dbg("%s: Area mismatch (%.2f vs %.2f (%.5f))", __FUNCTION__, area_total, readtrap->grad.area, area_total / readtrap->grad.area);
905 
906  float dwelltime = 1.0/(4257.59 * readtrap->fov * 0.1 * amp * 0.000001);
907  dwelltime = 2.0;
908  readtrap->acq.rbw = ks_calc_tsp2bw((int)dwelltime);
909  dwelltime = ks_calc_bw2tsp(readtrap->acq.rbw);
910  readtrap->area2center = area_to_center;
911  readtrap->time2center = time_to_center;
912  readtrap->acq.duration = RUP_FACTOR(t_Sr_pre + t_Sp + t_Sr_post, (int) (2 * dwelltime));
913  if (ks_eval_read(&readtrap->acq, "read") == SUCCESS) ;
914  /* Acquisition window */
915  /* if (ks_eval_read(&readtrap->acq, "echo") == FAILURE) { return FAILURE; } */
916 
917  /* Omega board*/
918  readtrap->omega = readtrap->grad; /* copy the shape of the readout */
919  readtrap->omega.amp = 1.0; /* just unity value for conformance. It does not matter during scanning, since ks_scan_omegatrap_hz() ignores this value */
920  readtrap->omega.area = 0.0; /* Omega area has no meaning */
921  sprintf(readtrap->omega.description, "%s.omega", readtrap->grad.description);
922 
923  return SUCCESS;
924 }
float ks_calc_tsp2bw(int tsp)
Convert dwell time to receiver bandwidth
Definition: KSFoundation_host.c:4734
float ks_calc_fov2gradareapixel(float fov)
Calculates the gradient area needed to move one pixel in k-space
Definition: KSFoundation_common.c:346
STATUS ks_error(const char *format,...) __attribute__((format(printf
Common error message function for HOST and TGT
void ks_init_trap(KS_TRAP *trap)
Resets a KS_TRAP sequence object to its default value (KS_INIT_TRAP)
Definition: KSFoundation_host.c:58
#define KS_DESCRIPTION_LENGTH
Definition: KSFoundation.h:185
int ks_calc_bw2tsp(float bw)
Convert receiver bandwidth to dwell time
Definition: KSFoundation_host.c:4722
STATUS ks_eval_read(KS_READ *read, const char *const desc)
Sets up a data acquisition window using a KS_READ sequence object
Definition: KSFoundation_host.c:270
STATUS STATUS ks_dbg(const char *format,...) __attribute__((format(printf
Common debug message function for HOST and TGT

◆ ks_eval_kynover_fromnex()

int ks_eval_kynover_fromnex ( int  yres,
float  nex,
int  minkynover 
)
1854  {
1855  int kynover;
1856 
1857  if (nex < 1) {
1858  kynover = yres * (nex - 0.5);
1859  kynover = ((kynover + 1) / 2) * 2; /* round to nearest even number */
1860  if (kynover < minkynover) {
1861  kynover = minkynover; /* protect against too few overscans */
1862  }
1863  kynover = IMin(2, kynover, yres / 2);
1864  } else {
1865  kynover = 0;
1866  }
1867 
1868  return kynover;
1869 
1870 }

◆ ks_eval_phaseviewtable()

void ks_eval_phaseviewtable ( KS_PHASER phaser)

Sets up which lines to acquire for a KS_PHASER object

This function sets the following fields in a KS_PHASER object

  1. .numlinestoacq: Number of phase encoding lines to acquire
  2. .linetoacq[]: Array, where the first .numlinestoacq elements denotes which lines to acquire with indices corresponding to a fully sampled k-space. The top-most line has index 0 and the bottom-most line has index (.res-1).

These two fields are set based on .res, .nover, .nacslines, and .R in the KS_PHASER object. If .nover < 0, partial Fourier will be set up, but with flipped k-space coverage

Parameters
[in,out]phaserPointer to the KS_PHASER object to be set up
Returns
void
1875  {
1876  int i, index, firstline, lastline, halfacslines;
1877  short viewonflag[KS_MAX_PHASEDYN];
1878 
1879  /* reset arrays */
1880  for (i = 0; i < KS_MAX_PHASEDYN; i++) {
1881  phaser->linetoacq[i] = 0;
1882  viewonflag[i] = 0;
1883  }
1884 
1885  if (phaser->nover == 0) {
1886  firstline = 0;
1887  lastline = phaser->res - 1;
1888  } else if (phaser->nover > 0) { /* fractional ky upper half */
1889  firstline = 0;
1890  lastline = phaser->res / 2 + phaser->nover - 1;
1891  } else {
1892  firstline = phaser->res / 2 + phaser->nover; /* NOTE: phase->nover here negative ! */
1893  lastline = phaser->res - 1;
1894  }
1895 
1896  if ((lastline - firstline + 1) % 2) {
1897  /* /vobs/vre/recon/rcapps/rc_filter.cpp requires that rhdayres is odd (according to ARC.e) */
1898  ks_error("%s: #k-space lines *spanned* must be even",__FUNCTION__);
1899  }
1900 
1901  int acscenter = (phaser->res / 2) + phaser->acsshift;
1902 
1903  /* lower half of k-space: mark the lines to acquire with '1' */
1904  halfacslines = phaser->nacslines / 2;
1905  for (i = acscenter; i <= lastline; i++) {
1906  if (((i + 1) % phaser->R) == 1 || phaser->R == 1) {
1907  viewonflag[i] = 1;
1908  } else if (halfacslines > 0) {
1909  viewonflag[i] = 1;
1910  halfacslines--;
1911  }
1912  }
1913 
1914  /* upper half of k-space: mark the lines to acquire with '1' */
1915  halfacslines = phaser->nacslines - (phaser->nacslines / 2) + halfacslines;
1916  for (i = acscenter - 1; i >= 0; i--) {
1917  if (((i + 1) % phaser->R) == 1 || phaser->R == 1) {
1918  viewonflag[i] = 1;
1919  } else if (halfacslines > 0) {
1920  viewonflag[i] = 1;
1921  halfacslines--;
1922  }
1923  }
1924 
1925  /* sweep kspace linearly and save the line numbers to acquire.
1926  firstline/lastline handles full/partial Fourier */
1927  index = 0;
1928  for (i = firstline; i <= lastline; i++) {
1929  if (viewonflag[i]) {
1930  phaser->linetoacq[index++] = i;
1931  }
1932  }
1933  phaser->numlinestoacq = index;
1934 
1935 }
int R
Definition: KSFoundation.h:1680
int res
Definition: KSFoundation.h:1678
int nacslines
Definition: KSFoundation.h:1681
STATUS ks_error(const char *format,...) __attribute__((format(printf
Common error message function for HOST and TGT
int numlinestoacq
Definition: KSFoundation.h:1684
int32_t i
Definition: KSFoundation_tgt.c:1389
int linetoacq[KS_MAX_PHASEDYN]
Definition: KSFoundation.h:1685
int nover
Definition: KSFoundation.h:1679
int acsshift
Definition: KSFoundation.h:1682
#define KS_MAX_PHASEDYN
Definition: KSFoundation.h:191

◆ ks_eval_phaser_adjustres()

STATUS ks_eval_phaser_adjustres ( KS_PHASER phaser,
const char *const  desc 
)

Adjusts the phase encoding resolution to the nearest valid value

Phase resolution must be divisible by .R and be even, and there are additional requirements for partial ky Fourier scans. Moreover, for parallel imaging using ASSET, only the acquired lines are stored in the data (compressed BAM), resulting in an R times lower value for rhnframes.

Here are the rules:

  • Resolution (and overscan) choice must guarantee that (rhhnover + rhnframes) is an even number
  • Full Fourier, ASSET:
    • rhnframes = res / R
    • rhhnover = 0
    • => res / R must be even
  • Partial Fourier:
    • rhnframes = (res / 2) / R
    • rhhnover = overscans/R
    • => res / (2 * R) and (res / (2 * R) + overscans) must be even
      • overscans/R and (res/2)/R must be integers
      • => res must be divisible by (4 * R)
      • => overscans must be divisible by (2 * R)
Parameters
[in,out]phaserPointer to KS_PHASER
[in]descA description (text string) of the KS_PHASER object, only used for error msg
Return values
STATUSSUCCESS or FAILURE
1939  {
1940 
1941  int kspacelines_noacc;
1942 
1943  if (phaser->nover == 0)
1944  kspacelines_noacc = phaser->res;
1945  else
1946  kspacelines_noacc = phaser->res / 2 + abs(phaser->nover);
1947 
1948  if (kspacelines_noacc < phaser->R) {
1949  /* Number of k-space lines less than R */
1950  return ks_error("%s(%s): R cannot exceed %d", __FUNCTION__, desc, kspacelines_noacc);
1951  } else if (kspacelines_noacc == phaser->R) {
1952  /* Number of k-space lines equal to R. Used for EPI when R is used as shots to produce ETL = 1.
1953  Just make sure that res is even */
1954  if (phaser->res % 2) {
1955  return ks_error("%s(%s): res must be even", __FUNCTION__, desc);
1956  } else {
1957  return SUCCESS;
1958  }
1959  }
1960 
1961  if (phaser->nover != 0) {
1962  /* partial Fourier */
1963  const int sign_nover = (phaser->nover >= 0) ? 1 : -1;
1964  phaser->nover = abs(phaser->nover);
1965  int invalidnover = TRUE;
1966 
1967  /* round res and nover to nearest valid values */
1968  if (phaser->R % 2) { /* R is odd */
1969  phaser->res = RUP_FACTOR(phaser->res - 2 * phaser->R, 4 * phaser->R);
1970  phaser->nover = RUP_FACTOR(phaser->nover - phaser->R, 2 * phaser->R) * sign_nover;
1971  invalidnover = phaser->nover < (2 * phaser->R) || phaser->nover > (phaser->res / 2);
1972  } else { /* R is even */
1973  phaser->res = RUP_FACTOR(phaser->res - phaser->R, 2 * phaser->R);
1974  phaser->nover = RUP_FACTOR(phaser->nover - phaser->R/2, phaser->R) * sign_nover;
1975  invalidnover = phaser->nover < phaser->R || phaser->nover > (phaser->res / 2);
1976  }
1977 
1978  if (invalidnover) {
1979  return ks_error("%s(%s): Valid #overscans not found", __FUNCTION__, desc);
1980  }
1981 
1982  } else {
1983  /* full Fourier */
1984 
1985  phaser->res = RUP_FACTOR(phaser->res - phaser->R, 2 * phaser->R);
1986 
1987  }
1988 
1989  return SUCCESS;
1990 }
int R
Definition: KSFoundation.h:1680
int res
Definition: KSFoundation.h:1678
STATUS ks_error(const char *format,...) __attribute__((format(printf
Common error message function for HOST and TGT
int nover
Definition: KSFoundation.h:1679

◆ ks_eval_phaser_setaccel()

STATUS ks_eval_phaser_setaccel ( KS_PHASER phaser,
int  min_acslines,
float  R 
)

[ks_eval_phaser_setaccel description]

Parameters
[in,out]phaserPointer to KS_PHASER
[in]min_acslinesMinimum # of ACS lines
[in]R(floating point) acceleration (taken e.g. from the UI opaccel_ph_stride)
Return values
STATUSSUCCESS or FAILURE
1995  {
1996 
1997  if (R <= 1.0) {
1998  phaser->R = 1.0;
1999  phaser->nacslines = 0;
2000  return SUCCESS;
2001  }
2002 
2003  /* Get integer acceleration and store the Rfraction for ARC mode */
2004  double Rfraction = ceil(R) - R;
2005  if (Rfraction > 0.9999) { /* handle roundoff issues for e.g. R=2.0000001 */
2006  Rfraction = 0.0;
2007  }
2008  phaser->R = floor(R + Rfraction + 0.5); /* round-up R to nearest integer */
2009 
2010  if (min_acslines == 0) {
2011  /* ASSET mode */
2012 
2013  /* phaser.nacslines = 0 triggers ASSET scan mode in GEReq_predownload_setrecon_accel() */
2014  phaser->nacslines = 0;
2015 
2016  } else {
2017  /* ARC mode */
2018 
2019  double totlines_spanned = (phaser->nover != 0) ? (phaser->res / 2 + abs(phaser->nover)) : phaser->res;
2020  double acqlines_R = totlines_spanned / phaser->R;
2021  double acqlines_floorR = totlines_spanned / FMax(2, 1.0, floor(R));
2022 
2023  /* adapt #acs lines based on the fraction of the acceleration factor, but never go below min_acslines */
2024  phaser->nacslines = IMax(2, (int) ((acqlines_floorR - acqlines_R) * Rfraction), min_acslines);
2025 
2026  }
2027 
2028  return SUCCESS;
2029 }
int R
Definition: KSFoundation.h:1680
int res
Definition: KSFoundation.h:1678
int nacslines
Definition: KSFoundation.h:1681
int nover
Definition: KSFoundation.h:1679

◆ ks_eval_phaser_constrained()

STATUS ks_eval_phaser_constrained ( KS_PHASER phaser,
const char *const  desc,
float  ampmax,
float  slewrate,
int  minplateautime 
)

Sets up a trapezoid for phase encoding, subject to gradient constraints specified as input arguments

Before calling this function, the following fields in KS_PHASER must be set up:

  1. .fov: The desired image Field-of-View (FOV) in [mm] along the sequence board one intends to place the KS_PHASER on (typically YGRAD, for 3D also ZGRAD)
  2. .res: The number of pixels within the FOV
  3. .nover: Number of 'overscans'. If 0, a full-Fourier k-space will be set up. If > 0, partial Fourier will be set up (shorter scan time) where .nover number of extra k-space lines beyond .res/2. If < 0, partial Fourier will be set up, but with flipped k-space coverage. The total number of samples will be .res/2 + abs(.nover). Absolute values of .nover between 1 and about 16 should be avoided since half Fourier reconstruction methods will likely have difficulties
  4. .R: The parallel imaging acceleration factor
  5. .nacslines: If .R > 1, the value of .nacslines is the number of calibration lines around k-space center for parallel imaging calibration
  6. .areaoffset: It is possible to embed a static gradient area in the phase encoding functionality of KS_PHASER. This is useful for 3D imaging, where the area needed for the rephaser for the slice select gradient can be put into .areaoffset. This makes the KS_PHASER to act as both slice rephaser and slice phase encoding gradient, which can shorten TE

If the KS_PHASER object is initialized with KS_INIT_PHASER, .nover, .nacslines and .areaoffset will be zero, and .R will be 1 (steps 3-6 can be skipped)

Based on the information in the KS_PHASER object, ks_eval_phaser_constrained() will set up the .grad trapezoid (KS_TRAP). By internally calling ks_eval_phaseviewtable(), .numlinestoacq, and the array .linetoacq[] are also set up. The field .numlinestoacq is an integer corresponding to the actual number of lines to acquire, and also the number of elements to read in .linetoacq[], containing the complete list of phase encoding lines to acquire (honoring partial Fourier, parallel imaging acceleration and ACS lines)

If ks_eval_phaser_constrained() returns FAILURE, one must also make sure cveval() returns FAILURE, otherwise an error message created in ks_eval_phaser_constrained() will not be seen in the UI

There are three wrapper functions to this function (ks_eval_phaser(), ks_eval_phaser1(), ks_eval_phaser2()) that have reduced number of input arguments, and calls ks_eval_phaser_constrained() with different preset gradient constraints. ks_eval_phaser() makes fewest assumptions, making it the most general choice

Parameters
[in,out]phaserPointer to the KS_PHASER object to be set up
[in]descA description (text string) of the KS_PHASER object. This description is used for pulse sequence generation (seen in the file generated by TimeHist in MGD Sim/WTools)
[in]ampmaxThe maximum allowed gradient amplitude ([G/cm])
[in]slewrateThe maximum allowed slewrate ([(G/cm) / us]). Value of 0.01 corresponds to 100 mT/m/s
[in]minplateautimeThe minimum plateau time of the trapezoid ([us])
Return values
STATUSSUCCESS or FAILURE
2036  {
2037  float phasepixelarea;
2038 
2039  ks_init_trap(&phaser->grad);
2040 
2041  if (phaser->fov < 5 || phaser->fov > 600) {
2042  return ks_error("%s(%s): field 'fov' must be in the range 5-600 mm", __FUNCTION__, phasername);
2043  }
2044 
2045  /* adjust res and possibly nover to allow both ARC and ASSET scans with full and partial ky Fourier */
2046  if (ks_eval_phaser_adjustres(phaser, phasername) == FAILURE) {
2047  return FAILURE;
2048  }
2049 
2050  phasepixelarea = ks_calc_fov2gradareapixel(phaser->fov); /* [(G/cm)*usec] */
2051 
2052  /* area required to reach the outermost phase encoding step (incl. areaoffset)
2053  N.B.: The *sign* of the area is controlled in scan using ks_scan_phaser_toline(), hence on HOST all phase encoding gradient will be positive, unlike TGT */
2054  phaser->grad.area = ((phaser->res - 1.0) / 2.0) * phasepixelarea + fabs(phaser->areaoffset); /* .areaoffset is used in 3D imaging to embed e.g. a slice sel. rephaser in the phase encoding gradient */
2055 
2056  /* setup phaser trap */
2057  if (!areSame(phaser->grad.area, 0.0)) {
2058  if (ks_eval_trap_constrained(&phaser->grad, phasername, ampmax, slewrate, minduration) == FAILURE)
2059  return FAILURE;
2060  }
2061 
2062  /* setup phaser->linetoacq[] */
2063  ks_eval_phaseviewtable(phaser);
2064 
2065 
2066  return SUCCESS;
2067 
2068 }
int res
Definition: KSFoundation.h:1678
#define areSame(a, b)
Definition: KSFoundation.h:119
KS_TRAP grad
Definition: KSFoundation.h:1676
float ks_calc_fov2gradareapixel(float fov)
Calculates the gradient area needed to move one pixel in k-space
Definition: KSFoundation_common.c:346
STATUS ks_eval_phaser_adjustres(KS_PHASER *phaser, const char *const desc)
Adjusts the phase encoding resolution to the nearest valid value
Definition: KSFoundation_host.c:1939
STATUS ks_error(const char *format,...) __attribute__((format(printf
Common error message function for HOST and TGT
void ks_init_trap(KS_TRAP *trap)
Resets a KS_TRAP sequence object to its default value (KS_INIT_TRAP)
Definition: KSFoundation_host.c:58
STATUS ks_eval_trap_constrained(KS_TRAP *trap, const char *const desc, float ampmax, float slewrate, int minduration)
Sets up a trapezoid using a KS_TRAP sequence object with gradient constraints specified as input argu...
Definition: KSFoundation_host.c:319
float areaoffset
Definition: KSFoundation.h:1683
float area
Definition: KSFoundation.h:582
float fov
Definition: KSFoundation.h:1677
void ks_eval_phaseviewtable(KS_PHASER *phaser)
Sets up which lines to acquire for a KS_PHASER object
Definition: KSFoundation_host.c:1875

◆ ks_eval_phaser()

STATUS ks_eval_phaser ( KS_PHASER phaser,
const char *const  desc 
)

Sets up an acquisition window with a trapezoid with preset gradient constraints

This is a wrapper function to ks_eval_phaser_constrained() with gradient constraints set to allow this KS_PHASER to simultaneously be played on XGRAD, YGRAD, ZGRAD for any slice angulation. This is the generic and safest configuration, but the duration of the KS_PHASER object may increase for double oblique slices

See ks_eval_phaser_constrained() for details on fields that need to be set before calling this function

Parameters
[in,out]phaserPointer to the KS_PHASER object to be set up
[in]descA description (text string) of the KS_PHASER object. This description is used for pulse sequence generation (seen in the file generated by TimeHist in MGD Sim/WTools)
Return values
STATUSSUCCESS or FAILURE
2073  {
2074 
2076 
2077 }
float ks_syslimits_ampmax(LOG_GRAD loggrd)
Returns the maximum gradient amplitude that can be used on all gradient boards simultaneously
Definition: KSFoundation_common.c:201
STATUS ks_eval_phaser_constrained(KS_PHASER *phaser, const char *const phasername, float ampmax, float slewrate, int minduration)
Sets up a trapezoid for phase encoding, subject to gradient constraints specified as input arguments...
Definition: KSFoundation_host.c:2035
LOG_GRAD loggrd
float ks_syslimits_slewrate(LOG_GRAD loggrd)
Returns the maximum slewrate that can be used on all gradient boards simultaneously
Definition: KSFoundation_common.c:248

◆ ks_eval_phaser2()

STATUS ks_eval_phaser2 ( KS_PHASER phaser,
const char *const  desc 
)

Sets up an acquisition window with a trapezoid with preset gradient constraints

This is a wrapper function to ks_eval_phaser_constrained() with gradient constraints set to allow this KS_PHASER to simultaneously be played on up to two (2) logical gradients for any slice angulation. For double oblique slice angulations, this function may allow for shorter duration than ks_eval_phaser()

See ks_eval_phaser_constrained() for details on fields that need to be set before calling this function

Parameters
[in,out]phaserPointer to the KS_PHASER object to be set up
[in]descA description (text string) of the KS_PHASER object. This description is used for pulse sequence generation (seen in the file generated by TimeHist in MGD Sim/WTools)
Return values
STATUSSUCCESS or FAILURE
2079  {
2080 
2082 
2083 }
STATUS ks_eval_phaser_constrained(KS_PHASER *phaser, const char *const phasername, float ampmax, float slewrate, int minduration)
Sets up a trapezoid for phase encoding, subject to gradient constraints specified as input arguments...
Definition: KSFoundation_host.c:2035
LOG_GRAD loggrd
float ks_syslimits_ampmax2(LOG_GRAD loggrd)
Returns the maximum gradient amplitude that can be used on two gradient boards simultaneously
Definition: KSFoundation_common.c:205
float ks_syslimits_slewrate2(LOG_GRAD loggrd)
Returns the maximum slewrate that can be used on two gradient boards simultaneously
Definition: KSFoundation_common.c:252

◆ ks_eval_phaser1()

STATUS ks_eval_phaser1 ( KS_PHASER phaser,
const char *const  desc 
)

Sets up an acquisition window with a trapezoid with preset gradient constraints

This is a wrapper function to ks_eval_phaser_constrained() with gradient constraints set to allow this KS_PHASER to only be played out on one (1) logical gradient at a time. No gradient may be active on another board while this trapezoid is played out. For double oblique slice angulations, this function may allow for shorter duration than ks_eval_phaser() and ks_eval_phaser2()

See ks_eval_phaser_constrained() for details on fields that need to be set before calling this function

Parameters
[in,out]phaserPointer to the KS_PHASER object to be set up
[in]descA description (text string) of the KS_PHASER object. This description is used for pulse sequence generation (seen in the file generated by TimeHist in MGD Sim/WTools)
Return values
STATUSSUCCESS or FAILURE
2085  {
2086 
2088 
2089 }
STATUS ks_eval_phaser_constrained(KS_PHASER *phaser, const char *const phasername, float ampmax, float slewrate, int minduration)
Sets up a trapezoid for phase encoding, subject to gradient constraints specified as input arguments...
Definition: KSFoundation_host.c:2035
float ks_syslimits_slewrate1(LOG_GRAD loggrd)
Returns the maximum slewrate that can be used on one gradient board at a time
Definition: KSFoundation_common.c:256
LOG_GRAD loggrd
float ks_syslimits_ampmax1(LOG_GRAD loggrd)
Returns the maximum gradient amplitude that can be used on one gradient board at a time...
Definition: KSFoundation_common.c:209

◆ ks_eval_phaser1p()

STATUS ks_eval_phaser1p ( KS_PHASER phaser,
const char *const  desc 
)

Sets up an acquisition window with a trapezoid with physical gradient constraints (invariant to slice angulation)

This is a wrapper function to ks_eval_phaser_constrained() with physical gradient constraints

Parameters
[in,out]phaserPointer to the KS_PHASER object to be set up
[in]descA description (text string) of the KS_PHASER object. This description is used for pulse sequence generation (seen in the file generated by TimeHist in MGD Sim/WTools)
Return values
STATUSSUCCESS or FAILURE
2091  {
2092 
2094 
2095 }
STATUS ks_eval_phaser_constrained(KS_PHASER *phaser, const char *const phasername, float ampmax, float slewrate, int minduration)
Sets up a trapezoid for phase encoding, subject to gradient constraints specified as input arguments...
Definition: KSFoundation_host.c:2035
float ks_syslimits_ampmax1p(LOG_GRAD loggrd)
Returns the maximum gradient amplitude on the physical gradient axes
Definition: KSFoundation_common.c:214
LOG_GRAD loggrd
float ks_syslimits_slewrate1p(LOG_GRAD loggrd)
Returns the maximum gradient amplitude on the physical gradient axes
Definition: KSFoundation_common.c:261

◆ ks_eval_wave()

STATUS ks_eval_wave ( KS_WAVE wave,
const char *const  desc,
int  res,
int  duration,
KS_WAVEFORM  waveform 
)

Sets up a KS_WAVE object based on a waveform (KS_WAVEFORM) in memory

ks_eval_wave() copies the content of a KS_WAVEFORM into a KS_WAVE sequence object. KS_WAVEFORM (5th arg) is a typedef for a float array with a fixed length of KS_MAXWAVELEN elements. Hence, a KS_WAVEFORM, or float array, must first be created and filled with some wave contents before calling this function. res elements are copied from the input KS_WAVEFORM to the field .waveform in the KS_WAVE object (1st arg). duration (4th arg) must be divisible by res (3rd arg) and at least 2x res.

When using (i.e. calling ks_pg_wave()) the waveform for:

  1. RF or OMEGA, nothing needs to be done in terms of amplitude scaling. For RF, amplitude scaling is handled via the KS_RF object, and for OMEGA the number of [Hz] are set directly in run-time (scan())
  2. THETA, the waveform needs to be scaled to units of [degrees]
  3. XGRAD, YGRAD, or ZGRAD, the waveform needs to be scaled to units of [G/cm]
Parameters
[out]waveKS_WAVE object to be created
[in]descA description (text string) of the KS_WAVE object. This description is used for pulse sequence generation (seen in the file generated by TimeHist in MGD Sim/WTools)
[in]resResolution of the waveform, i.e. number of elements from KS_WAVEFORM (5th arg) to read
[in]durationDesired duration in [us] of the waveform (must be divisible with res)
[in]waveformKS_WAVEFORM (float array of max length KS_MAXWAVELEN) with existing waveform content
Return values
STATUSSUCCESS or FAILURE
2097  {
2098  KS_DESCRIPTION tmpdesc;
2099 
2100  strncpy(tmpdesc, desc, KS_DESCRIPTION_LENGTH / 2);
2101  /* don't allow NULL or a description with leading space */
2102  if (desc == NULL || desc[0] == ' ') {
2103  return ks_error("ks_eval_wave: wave name (2nd arg) cannot be NULL or leading space");
2104  }
2105 
2106  if (duration < 0 || res < 0) {
2107  return ks_error("ks_eval_wave(%s): 'res' and 'duration' must be positive", desc);
2108  }
2109  if (res > KS_MAXWAVELEN) {
2110  return ks_error("ks_eval_wave(%s): 'res' cannot exceed %d", wave->description, KS_MAXWAVELEN);
2111  }
2112  if (res % 2) {
2113  return ks_error("ks_eval_wave(%s): 'res' must be even", wave->description);
2114  }
2115  if (duration > 0 && (duration % res)) {
2116  return ks_error("ks_eval_wave(%s): 'duration' (%d) [us] must be divisible by 'res' (%d)", desc, duration, res);
2117  }
2118  if (duration > 0 && (duration / res) < 2) {
2119  return ks_error("ks_eval_wave(%s): 'duration' (%d) [us] must be at least 2x 'res' (%d)", desc, duration, res);
2120  }
2121 
2122  /* reset wave object */
2123  ks_init_wave(wave);
2124 
2125  /* fill in wave object */
2126  strcpy(wave->description, tmpdesc);
2127  wave->res = res;
2128  wave->duration = duration;
2129 
2130  /* copy the waveform */
2131  memcpy(wave->waveform, newwave, sizeof(float) * wave->res);
2132 
2133 
2134 
2135  return SUCCESS;
2136 
2137 }
STATUS ks_error(const char *format,...) __attribute__((format(printf
Common error message function for HOST and TGT
#define KS_DESCRIPTION_LENGTH
Definition: KSFoundation.h:185
KS_DESCRIPTION description
Definition: KSFoundation.h:666
void ks_init_wave(KS_WAVE *wave)
Resets a KS_WAVE sequence object to its default value (KS_INIT_WAVE)
Definition: KSFoundation_host.c:66
char KS_DESCRIPTION[KS_DESCRIPTION_LENGTH]
Definition: KSFoundation.h:261
int res
Definition: KSFoundation.h:667
KS_WAVEFORM waveform
Definition: KSFoundation.h:669
int duration
Definition: KSFoundation.h:668
#define KS_MAXWAVELEN
Definition: KSFoundation.h:186

◆ ks_eval_wave_file()

STATUS ks_eval_wave_file ( KS_WAVE wave,
const char *const  desc,
int  res,
int  duration,
const char *const  filename,
const char *const  format 
)

Sets up a KS_WAVE object based on a waveform that resides on disk

ks_eval_wave_file() reads waveforms on disk into a KS_WAVE. The data format (arg 6) may currently be one of:

  1. "GE": GE's file format for external pulses (.rho, .gz, etc.). The data will be divided by a factor of MAX_PG_WAMP (32766) before copied to KS_WAVE
  2. "short": Plain short integer format. The data will be divided by a factor of MAX_PG_WAMP (32766) before copied to KS_WAVE
  3. "float": Plain float format. Data will be read in as is and copied to KS_WAVE

When the first two formats are used the field .waveform in KS_WAVE will contain data in the range [-1.0,1.0]. res elements are read from disk in the specified format into the field .waveform in the KS_WAVE object (1st arg). duration (4th arg) must be divisible by res (3rd arg) and at least 2x res.

When using (i.e. calling ks_pg_wave()) the waveform for:

  1. RF or OMEGA, nothing needs to be done in terms of amplitude scaling. For RF, amplitude scaling is handled via the KS_RF object, and for OMEGA the number of [Hz] are set directly in run-time (scan())
  2. THETA, the waveform needs to be scaled to units of [degrees]
  3. XGRAD, YGRAD, or ZGRAD, the waveform needs to be scaled to units of [G/cm]
Parameters
[out]waveKS_WAVE object to be created
[in]descA description (text string) of the KS_WAVE object. This description is used for pulse sequence generation (seen in the file generated by TimeHist in MGD Sim/WTools)
[in]resKS_WAVE object to be created
[in]durationKS_WAVE object to be created
[in]filenameFilename on disk
[in]formatString denoting the data format on disk. Valid values are: "ge" (reads GE's *.rho files with 32byte header), "short", and "float", the latter two being header-less
Return values
STATUSSUCCESS or FAILURE
2140  {
2141  KS_DESCRIPTION tmpdesc;
2142  int i;
2143 
2144  strncpy(tmpdesc, desc, KS_DESCRIPTION_LENGTH / 2);
2145  /* don't allow empty description or a description with leading space */
2146  if (desc == NULL || desc[0] == ' ') {
2147  return ks_error("ks_eval_wave_file: wave name (2nd arg) cannot be NULL or leading space");
2148  }
2149  if (res % 2) {
2150  return ks_error("ks_eval_wave_file(%s): 'res' must be even", wave->description);
2151  }
2152  if (duration > 0 && (duration % res)) {
2153  return ks_error("ks_eval_wave_file(%s): 'duration' (%d) [us] must be divisible by 'res' (%d)", desc, duration, res);
2154  }
2155  if (duration > 0 && (duration / res) < 2) {
2156  return ks_error("ks_eval_wave_file(%s): 'duration' (%d) [us] must be at least 2x 'res' (%d)", desc, duration, res);
2157  }
2158  if (duration < 0 || duration > 4 * KS_MAXWAVELEN) {
2159  return ks_error("ks_eval_wave_file(%s): 'duration' [us] must be in the range [0, %d]", desc, 4 * KS_MAXWAVELEN);
2160  }
2161 
2162  /* reset wave object */
2163  ks_init_wave(wave);
2164 
2165  /* fill in wave object */
2166  strcpy(wave->description, tmpdesc);
2167  wave->res = res;
2168  wave->duration = duration;
2169 
2170  /* read from file */
2171  if (!strncasecmp(format, "ge", 2)) { /* short format with 32-byte header and big-endian short int */
2172  KS_IWAVE iwave;
2173  uextwave(iwave, res, (char *) filename);
2174  for (i = 0; i < res; i++)
2175  wave->waveform[i] = (float) iwave[i] / (float) MAX_PG_WAMP;
2176  } else if (!strncasecmp(format, "short", 5)) {
2177  KS_IWAVE iwave;
2178  FILE *fp;
2179  int n;
2180  if ((fp = fopen(filename, "r")) == NULL)
2181  return ks_error("ks_eval_wave_file (%s): Error opening file '%s'", wave->description, filename);
2182  if ((n = fread(iwave, sizeof(short), res, fp)) < res)
2183  return ks_error("ks_eval_wave_file (%s): Only %d out of %d elements read from file '%s'", wave->description, n, res, filename);
2184  fclose(fp);
2185  for (i = 0; i < res; i++)
2186  wave->waveform[i] = (float) iwave[i] / (float) MAX_PG_WAMP;
2187  } else if (!strncasecmp(format, "float", 5)) {
2188  FILE *fp;
2189  int n;
2190  if ((fp = fopen(filename, "r")) == NULL)
2191  return ks_error("ks_eval_wave_file (%s): Error opening file '%s'", wave->description, filename);
2192  if ((n = fread(wave->waveform, sizeof(float), res, fp)) < res)
2193  return ks_error("ks_eval_wave_file (%s): Only %d out of %d elements read from file '%s'", wave->description, n, res, filename);
2194  fclose(fp);
2195  } else {
2196  return ks_error("ks_eval_wave_file (%s): 'format' (5th arg) must be 'ge','short' or 'float'", wave->description);
2197  }
2198 
2199 
2200  return SUCCESS;
2201 
2202 } /* ks_eval_wave_file */
short KS_IWAVE[KS_MAXWAVELEN]
Definition: KSFoundation.h:263
STATUS ks_error(const char *format,...) __attribute__((format(printf
Common error message function for HOST and TGT
int32_t i
Definition: KSFoundation_tgt.c:1389
#define KS_DESCRIPTION_LENGTH
Definition: KSFoundation.h:185
KS_DESCRIPTION description
Definition: KSFoundation.h:666
void ks_init_wave(KS_WAVE *wave)
Resets a KS_WAVE sequence object to its default value (KS_INIT_WAVE)
Definition: KSFoundation_host.c:66
char KS_DESCRIPTION[KS_DESCRIPTION_LENGTH]
Definition: KSFoundation.h:261
int res
Definition: KSFoundation.h:667
KS_WAVEFORM waveform
Definition: KSFoundation.h:669
int duration
Definition: KSFoundation.h:668
#define KS_MAXWAVELEN
Definition: KSFoundation.h:186

◆ ks_eval_mirrorwave()

STATUS ks_eval_mirrorwave ( KS_WAVE wave)

Flips the contents of the KS_WAVEFORM in a KS_WAVE object

ks_eval_mirrorwave() replaces the KS_WAVEFORM with its (temporally) mirrored version. This can be used to e.g. mirror a minimum phase excitation pulse for fast recovery and T1-optimization in FSE sequences (ksfse.e).#pragma endregion See also ks_eval_stretch_rf().

Parameters
[in,out]waveKS_WAVE object, whose KS_WAVEFORM to be mirrored
Return values
STATUSSUCCESS or FAILURE
2206  {
2207  KS_WAVEFORM tmpwave;
2208  int i;
2209 
2210  memcpy(tmpwave, wave->waveform, sizeof(float) * wave->res);
2211 
2212  for (i = 0; i < wave->res; i++) {
2213  wave->waveform[i] = tmpwave[wave->res-1 - i];
2214  }
2215  return SUCCESS;
2216 }
int32_t i
Definition: KSFoundation_tgt.c:1389
float KS_WAVEFORM[KS_MAXWAVELEN]
Definition: KSFoundation.h:262
int res
Definition: KSFoundation.h:667
KS_WAVEFORM waveform
Definition: KSFoundation.h:669

◆ ks_eval_rf_sinc()

STATUS ks_eval_rf_sinc ( KS_RF rf,
const char *const  desc,
double  bw,
double  tbw,
float  flip,
int  wintype 
)

Sets up a KS_RF object with a Sinc pulse shape

For small flip angles, the Fourier transform of the RF envelope is a good approximation of the final slice profile and SLR designed pulses may not be necessary. For these cases, this function can be used to create a Sinc RF pulse (as a KS_RF object) with a desired bandwidth (BW), time-bandwidth-product (TBW) and window function (e.g. KS_RF_SINCWIN_HAMMING, see ks_enum_sincwin). An increased TBW results in more Sinc lobes and sharper slice profiles, and the minimum value is 2. The duration of the RF pulse (.rfwave.duration) increases when BW decreases or TBW increases

Parameters
[out]rfKS_RF object to be created
[in]descA description (text string) of the KS_RF object. This description is used for pulse sequence generation (seen in the file generated by TimeHist in MGD Sim/WTools)
[in]bwBandwidth of the RF pulse [Hz]
[in]tbwTime-bandwidth-product of the RF pulse. Higher value gives sharper slice profiles but increases the duration
[in]flipNominal flip angle of the RF pulse
[in]wintypeWindow function over the Sinc. See ks_enum_sincwin for enums names that steer the window choice
Return values
STATUSSUCCESS or FAILURE
2227  {
2228  KS_DESCRIPTION tmpdesc;
2229  int i;
2230  double x, window, rfenvelope;
2231  int duration = RUP_GRD(tbw / bw * 1e6); /* [us]. We round up to nearest multiple of 4 (GRAD_UPDATE_TIME) to better align to gradients */
2232  int res = duration / RF_UPDATE_TIME; /* # sample points. RF_UPDATE_TIME = 2 => res always even */
2233 
2234  /* don't allow empty description or a description with leading space */
2235  if (desc == NULL || desc[0] == ' ') {
2236  return ks_error("ks_eval_rf_sinc: RF name (2nd arg) cannot be NULL or leading space");
2237  }
2238  strncpy(tmpdesc, desc, KS_DESCRIPTION_LENGTH/2);
2239  tmpdesc[KS_DESCRIPTION_LENGTH/2] = 0;
2240 
2241  /* reset RF object */
2242  ks_init_rf(rf);
2243 
2244  /* fill in wave object */
2245  sprintf(rf->rfwave.description, "%s.wave", tmpdesc);
2246 
2247  if (bw < 2 || bw > 100000) {
2248  return ks_error("ks_eval_rf_sinc(%s): 'bw' (%g, 3rd arg) must be in range [2,100000] Hz", rf->rfwave.description, bw);
2249  }
2250  if (tbw < 2 || tbw > 100) {
2251  return ks_error("ks_eval_rf_sinc(%s): 'tbw' (%g, 4th arg) must be in range [2,100]", rf->rfwave.description, tbw);
2252  }
2253  if (res > KS_MAXWAVELEN || res < 4) {
2254  return ks_error("ks_eval_rf_sinc(%s): combination of 'bw' and 'tbw' resulted in a resolution outside the valid range [4, %d]", rf->rfwave.description, KS_MAXWAVELEN);
2255  }
2256  if (flip <= 0 || flip > 10000.0) {
2257  return ks_error("ks_eval_rf_sinc(%s): 'flip' must be in range (0, 10000]", rf->rfwave.description);
2258  }
2259 
2260  rf->rfwave.res = res;
2261  rf->rfwave.duration = duration;
2262  rf->iso2end = duration / 2;
2263  rf->start2iso = duration / 2;
2264  rf->bw = bw;
2265  rf->flip = flip;
2266 
2267  /* generate waveform with max 1.0 */
2268  for (i = 0 ; i < (res - 1) ; i++) {
2269  x = 2.0 * i / (res - 1) - 1.0;
2270 
2271  if (fabs(2.0 * PI * (tbw / 4.0)*x) > 1e-6) {
2272  rfenvelope = sin(2.0 * PI * (tbw / 4.0) * x) / (2.0 * PI * (tbw / 4.0) * x);
2273  } else {
2274  rfenvelope = 1.0;
2275  }
2276 
2277  window = 1.0; /* init to no filter */
2278 
2279  if (wintype == KS_RF_SINCWIN_HAMMING) {
2280  window = 0.54 + 0.46 * cos(PI * x);
2281  } else if (wintype == KS_RF_SINCWIN_HANNING) {
2282  window = (1.0 + cos(PI * x)) * 0.5;
2283  } else if (wintype == KS_RF_SINCWIN_BLACKMAN) {
2284  window = 0.42 + 0.5 * cos(PI * x) + 0.08 * cos(2 * PI * x);
2285  } else if (wintype == KS_RF_SINCWIN_BARTLETT) {
2286  if (x < 0.0)
2287  window = x + 1.0;
2288  else
2289  window = 1.0 - x;
2290  }
2291 
2292  rf->rfwave.waveform[i] = rfenvelope * window;
2293 
2294  } /* for */
2295 
2296  /* initialize amplitude to 1 */
2297  rf->amp = 1;
2298 
2299  /* set the usage of the RF PULSE to off until .base.ninst > 0 (see ks_pg_rf() on HOST) */
2300  rf->rfpulse.activity = PSD_PULSE_OFF;
2301 
2302  /* initialize number of occurrences of RF pulse to zero */
2303  rf->rfpulse.num = 0;
2304 
2305  /* setup rfpulse structure */
2306  return ks_eval_rfstat(rf);
2307 }
int start2iso
Definition: KSFoundation.h:944
Definition: KSFoundation.h:1954
RF_PULSE rfpulse
Definition: KSFoundation.h:948
STATUS ks_error(const char *format,...) __attribute__((format(printf
Common error message function for HOST and TGT
int32_t i
Definition: KSFoundation_tgt.c:1389
float flip
Definition: KSFoundation.h:940
#define KS_DESCRIPTION_LENGTH
Definition: KSFoundation.h:185
void ks_init_rf(KS_RF *rf)
Resets a KS_RF sequence object to its default value (KS_INIT_RF)
Definition: KSFoundation_host.c:70
KS_DESCRIPTION description
Definition: KSFoundation.h:666
STATUS ks_eval_rfstat(KS_RF *rf)
Sets up the RF_PULSE structure of a KS_RF object
Definition: KSFoundation_host.c:2615
KS_WAVE rfwave
Definition: KSFoundation.h:949
Definition: KSFoundation.h:1954
float amp
Definition: KSFoundation.h:943
Definition: KSFoundation.h:1954
int iso2end
Definition: KSFoundation.h:945
char KS_DESCRIPTION[KS_DESCRIPTION_LENGTH]
Definition: KSFoundation.h:261
int res
Definition: KSFoundation.h:667
Definition: KSFoundation.h:1954
KS_WAVEFORM waveform
Definition: KSFoundation.h:669
int duration
Definition: KSFoundation.h:668
#define KS_MAXWAVELEN
Definition: KSFoundation.h:186
float bw
Definition: KSFoundation.h:941

◆ ks_eval_rf_secant()

STATUS ks_eval_rf_secant ( KS_RF rf,
const char *const  desc,
float  A0,
float  tbw,
float  bw 
)

Sets up a KS_RF object with a Hyperbolic Secant shape

2311  {
2312  STATUS status;
2313  int debug = 1;
2314 
2315  /* reset RF object */
2316  ks_init_rf(rf);
2317 
2318  KS_DESCRIPTION tmpdesc;
2319  int i;
2320 
2321  /* don't allow empty description or a description with leading space */
2322  if (desc == NULL || desc[0] == ' ') {
2323  return ks_error("ks_eval_rf_secant: 'RF name' (2nd arg) cannot be NULL or leading space");
2324  }
2325  strncpy(tmpdesc, desc, KS_DESCRIPTION_LENGTH/2);
2326  tmpdesc[KS_DESCRIPTION_LENGTH/2] = 0;
2327 
2328  /* fill in wave object */
2329  sprintf(rf->rfwave.description, "%s.wave", tmpdesc);
2330 
2331  if (A0 < 0 || 0.25 < A0) {
2332  return ks_error("ks_eval_rf_secant(%s): 'A0' (%g, 3rd arg) must be in range [0,0.25] Gauss", rf->rfwave.description, A0);
2333  }
2334  if (tbw < 2 || 20 < tbw) {
2335  return ks_error("ks_eval_rf_secant(%s): 'tbw' (%g, 4th arg) must be in range [2,20]", rf->rfwave.description, tbw);
2336  }
2337  if (bw < 100 || 10000 < bw) {
2338  return ks_error("ks_eval_rf_secant(%s): 'bw' (%g, 5th arg) must be in range [0,10000] Hz", rf->rfwave.description, bw);
2339  }
2340 
2341  int duration = RUP_GRD((int)(1000000.0 * tbw / bw));
2342  int res = duration / GRAD_UPDATE_TIME; /* # sample points. RF_UPDATE_TIME = 2 => res always even */
2343 
2344  if (res < 4 || KS_MAXWAVELEN < res) {
2345  return ks_error("ks_eval_rf_secant(%s): tbw / bw resulted in a resolution(%d)/duration(%d) outside the valid range [4, %d]", rf->rfwave.description, res, duration, KS_MAXWAVELEN);
2346  }
2347 
2348  rf->rfwave.res = res;
2349  rf->rfwave.duration = duration;
2350  rf->iso2end = duration / 2;
2351  rf->start2iso = duration / 2;
2352 
2353  /* calculate pulse properties */
2354  float y = 5; /* 5 is a standard value from literature */
2355  float B = (bw * PI) / y;
2356  float Acheck = (PI * bw) / (2.0 * PI * 4258 * sqrt(y)); /* Gauss */
2357 
2358  if (Acheck > A0 * 0.5) {
2359  return ks_error("ks_eval_rf_secant(%s): The adiabatic condition might not be met since %g !>> %g (needs to be at least half)", rf->rfwave.description, A0, Acheck);
2360  }
2361 
2362  rf->bw = bw;
2363  rf->flip = 180.0;
2364 
2365  double rfenvelope, phaseenvelope, tau, sech;
2366  for (i = 0 ; i < res; i++) {
2367 
2368  /* range from -2pi to 2pi */
2369  tau = ((i / (((float)res - 1.0) * 0.5)) - 1.0) * 2.0 * PI;
2370 
2371  sech = 1.0 / cosh(tau * B * 0.001);
2372  rfenvelope = A0 * sech;
2373  phaseenvelope = y * log(sech) + y * log(A0);
2374 
2375  rf->rfwave.waveform[i] = rfenvelope;
2376  rf->thetawave.waveform[i] = phaseenvelope * (180.0/PI);
2377 
2378  }
2379 
2380  if (debug == 1) {
2381  ks_print_waveform(rf->rfwave.waveform, "secant_a.txt", rf->rfwave.res);
2382  ks_print_waveform(rf->thetawave.waveform, "secant_p.txt", rf->rfwave.res);
2383  ks_error("HS: A0=%f >> Ac=%f, bw=%f, tbw=%f", A0, Acheck, bw, tbw);
2384  ks_error("HS: y=%f, B=%f", y, B);
2385  }
2386 
2387  /* Normalize waveform to one & initialize amplitude to 1 */
2388  ks_wave_multiplyval(&rf->rfwave, 1.0 / ks_wave_absmax(&rf->rfwave));
2389  rf->amp = 1;
2390 
2391  /* set the usage of the RF PULSE to off until .base.ninst > 0 (see ks_pg_rf() on HOST) */
2392  rf->rfpulse.activity = PSD_PULSE_OFF;
2393 
2394  /* initialize number of occurrences of RF pulse to zero */
2395  rf->rfpulse.num = 0;
2396 
2397  /* setup rfpulse structure */
2398  status = ks_eval_rfstat(rf);
2399  if (status != SUCCESS) return status;
2400 
2401  rf->rfpulse.max_b1 = A0;
2402  rf->thetawave.res = res;
2403  rf->thetawave.duration = duration;
2404  rf->role = KS_RF_ROLE_INV;
2405  return status;
2406 }
int start2iso
Definition: KSFoundation.h:944
int role
Definition: KSFoundation.h:939
int debug
Definition: GERequired.e:2642
RF_PULSE rfpulse
Definition: KSFoundation.h:948
STATUS ks_error(const char *format,...) __attribute__((format(printf
Common error message function for HOST and TGT
Definition: KSFoundation.h:1953
int32_t i
Definition: KSFoundation_tgt.c:1389
float flip
Definition: KSFoundation.h:940
float ks_wave_absmax(const KS_WAVE *wave)
Returns the maximum absolute value in a KS_WAVE sequence object
Definition: KSFoundation_common.c:1224
#define KS_DESCRIPTION_LENGTH
Definition: KSFoundation.h:185
KS_WAVE thetawave
Definition: KSFoundation.h:951
void ks_init_rf(KS_RF *rf)
Resets a KS_RF sequence object to its default value (KS_INIT_RF)
Definition: KSFoundation_host.c:70
KS_DESCRIPTION description
Definition: KSFoundation.h:666
STATUS ks_eval_rfstat(KS_RF *rf)
Sets up the RF_PULSE structure of a KS_RF object
Definition: KSFoundation_host.c:2615
KS_WAVE rfwave
Definition: KSFoundation.h:949
float amp
Definition: KSFoundation.h:943
int iso2end
Definition: KSFoundation.h:945
void ks_print_waveform(const KS_WAVEFORM waveform, const char *filename, int res)
Writes a KS_WAVEFORM to disk with the specified filename
Definition: KSFoundation_host.c:5135
char KS_DESCRIPTION[KS_DESCRIPTION_LENGTH]
Definition: KSFoundation.h:261
int res
Definition: KSFoundation.h:667
void ks_wave_multiplyval(KS_WAVE *wave, float val)
In-place scalar multiplication of a KS_WAVE
Definition: KSFoundation_common.c:1398
KS_WAVEFORM waveform
Definition: KSFoundation.h:669
int duration
Definition: KSFoundation.h:668
#define KS_MAXWAVELEN
Definition: KSFoundation.h:186
float bw
Definition: KSFoundation.h:941

◆ ks_eval_rf_hard()

STATUS ks_eval_rf_hard ( KS_RF rf,
const char *const  desc,
int  duration,
float  flip 
)

Sets up a KS_RF object with a rectangular (hard) pulse of a given duration.

This function creates a hard RF pulse of the desired duration. Hard pulses are typically used as non-slice selective RF pulses, as their slice profile would be very poor (Sinc-like). The resolution of the RF pulse created is equal to the duration/2

Parameters
[out]rfKS_RF object to be created
[in]descA description (text string) of the KS_RF object. This description is used for pulse sequence generation (seen in the file generated by TimeHist in MGD Sim/WTools)
[in]durationDesired duration in [us] of the waveform (must be even)
[in]flipNominal flip angle of the RF pulse
Return values
STATUSSUCCESS or FAILURE
2409  {
2410  KS_DESCRIPTION tmpdesc;
2411  int i;
2412 
2413  /* don't allow empty description or a description with leading space */
2414  if (desc == NULL || desc[0] == ' ') {
2415  return ks_error("ks_eval_rf_hard: RF name (2nd arg) cannot be NULL or leading space");
2416  }
2417  strncpy(tmpdesc, desc, KS_DESCRIPTION_LENGTH/2);
2418  tmpdesc[KS_DESCRIPTION_LENGTH/2] = 0;
2419 
2420  /* reset RF object */
2421  ks_init_rf(rf);
2422 
2423  /* fill in wave object */
2424  sprintf(rf->rfwave.description, "%s.wave", tmpdesc);
2425 
2426  if (duration > KS_MAXWAVELEN * RF_UPDATE_TIME || duration < 4) {
2427  return ks_error("ks_eval_rf_hard(%s): 'duration' (3rd arg) must be in range [4, %d]", rf->rfwave.description, KS_MAXWAVELEN * RF_UPDATE_TIME);
2428  }
2429  if (duration % 2) {
2430  return ks_error("ks_eval_rf_hard(%s): 'duration' (3rd arg) must be divisible by 2", rf->rfwave.description);
2431  }
2432 
2433  rf->rfwave.res = duration / RF_UPDATE_TIME;
2434  rf->rfwave.duration = duration;
2435  rf->iso2end = duration / 2;
2436  rf->start2iso = duration / 2;
2437  rf->bw = 0;
2438  rf->flip = flip;
2439 
2440  /* generate waveform with max 1.0 */
2441  for (i = 0 ; i < rf->rfwave.res ; i++) {
2442  rf->rfwave.waveform[i] = 1.0;
2443  }
2444 
2445  /* initialize amplitude to 1 */
2446  rf->amp = 1;
2447 
2448  /* set the usage of the RF PULSE to off until .base.ninst > 0 (see ks_pg_rf() on HOST) */
2449  rf->rfpulse.activity = PSD_PULSE_OFF;
2450 
2451  /* initialize number of occurrences of RF pulse to zero */
2452  rf->rfpulse.num = 0;
2453 
2454  /* setup rfpulse structure */
2455  STATUS status = ks_eval_rfstat(rf);
2456  if (status != SUCCESS) return status;
2457 
2458  return SUCCESS;
2459 }
int start2iso
Definition: KSFoundation.h:944
RF_PULSE rfpulse
Definition: KSFoundation.h:948
STATUS ks_error(const char *format,...) __attribute__((format(printf
Common error message function for HOST and TGT
int32_t i
Definition: KSFoundation_tgt.c:1389
float flip
Definition: KSFoundation.h:940
#define KS_DESCRIPTION_LENGTH
Definition: KSFoundation.h:185
void ks_init_rf(KS_RF *rf)
Resets a KS_RF sequence object to its default value (KS_INIT_RF)
Definition: KSFoundation_host.c:70
KS_DESCRIPTION description
Definition: KSFoundation.h:666
STATUS ks_eval_rfstat(KS_RF *rf)
Sets up the RF_PULSE structure of a KS_RF object
Definition: KSFoundation_host.c:2615
KS_WAVE rfwave
Definition: KSFoundation.h:949
float amp
Definition: KSFoundation.h:943
int iso2end
Definition: KSFoundation.h:945
char KS_DESCRIPTION[KS_DESCRIPTION_LENGTH]
Definition: KSFoundation.h:261
int res
Definition: KSFoundation.h:667
KS_WAVEFORM waveform
Definition: KSFoundation.h:669
int duration
Definition: KSFoundation.h:668
#define KS_MAXWAVELEN
Definition: KSFoundation.h:186
float bw
Definition: KSFoundation.h:941

◆ ks_eval_rf_hard_optimal_duration()

STATUS ks_eval_rf_hard_optimal_duration ( KS_RF rf,
const char *const  desc,
int  order,
float  flip,
float  offsetFreq 
)

Sets up a KS_RF object with a rectangular (hard) pulse with optimal duration for fat selection/saturation

This function creates a hard RF pulse with optimal duration for fat selection/saturation. Hard pulses are typically used as non-slice selective RF pulses. Their slice profile is sinc-like and with this function you can create a hard pulse with a sinc-minima at the center of the spectral fat-peak. You can select wich minima using the input 'order'. A higher order generates a longer pulse duration with a more narrow frequency response. If the pulse is tuned to the fat peak, using input 'offsetFreq', the minimia will instead end up at the water-peak. The resolution of the RF pulse created is equal to the duration/2

Parameters
[out]rfKS_RF object to be created
[in]descA description (text string) of the KS_RF object. This description is used for pulse sequence generation (seen in the file generated by TimeHist in MGD Sim/WTools)
[in]orderSelect wich minima to use
[in]flipNominal flip angle of the RF pulse
[in]offsetFreqchoose center frequency offset
Return values
STATUSSUCCESS or FAILURE
2462  {
2463 
2464  if (order < 1) {
2465  return ks_error("ks_eval_rf_hard(%s): 'order' (%d, 3rd arg) must be positive", desc, order);
2466  }
2467 
2468  int duration = RUP_FACTOR((int)(1000000.0 * sqrt(pow(2.0 * PI * (float)order, 2) - pow((float)flip * (PI / 180.0), 2)) / fabs(2.0 * PI * offsetFreq)), GRAD_UPDATE_TIME);
2469 
2470  return ks_eval_rf_hard(rf, desc, duration, flip);
2471 
2472 }
STATUS ks_error(const char *format,...) __attribute__((format(printf
Common error message function for HOST and TGT
STATUS ks_eval_rf_hard(KS_RF *rf, const char *const desc, int duration, float flip)
Sets up a KS_RF object with a rectangular (hard) pulse of a given duration.
Definition: KSFoundation_host.c:2409

◆ ks_eval_rf_binomial()

STATUS ks_eval_rf_binomial ( KS_RF rf,
const char *const  desc,
int  offResExc,
int  nPulses,
float  flip,
float  offsetFreq 
)

Sets up a KS_RF object as a Binomial RF pulse

This function creates a binomial RF pulse, which consists of a series of hard pulses. Binomial pulses are used for spectral selection. They utilize the difference in resonance frequency of different tissue types, such as water and fat. Increasing the number of sub-pulses will improve the spectral selectivity but also increse the pulse duration.

Parameters
[out]rfKS_RF object to be created
[in]descA description (text string) of the KS_RF object. This description is used for pulse sequence generation (seen in the file generated by TimeHist in MGD Sim/WTools)
[in]offResExcIf 0 the pulse will excite the spinns at the center frequency, If 1 the pulse will excite the spinns at the offset frequency (given by input: offsetFreq)
[in]nPulsesNumber of sub-pulses
[in]flipNominal flip angle of the RF pulse
[in]offsetFreqchoose center frequency offset in Hz
Return values
STATUSSUCCESS or FAILURE
2475  {
2476  KS_DESCRIPTION tmpdesc;
2477  int i, j;
2478 
2479  /* Don't allow empty description or a description with leading space */
2480  if (desc == NULL || desc[0] == ' ') {
2481  return ks_error("ks_eval_rf_binomial: RF name (2nd arg) cannot be NULL or leading space");
2482  }
2483  strncpy(tmpdesc, desc, KS_DESCRIPTION_LENGTH/2);
2484  tmpdesc[KS_DESCRIPTION_LENGTH/2] = 0;
2485 
2486  /* Reset RF object */
2487  ks_init_rf(rf);
2488 
2489  /* Fill in wave object */
2490  sprintf(rf->rfwave.description, "%s.wave", tmpdesc);
2491 
2492  if (offResExc < 0 || offResExc > 1) {
2493  return ks_error("ks_eval_rf_hard(%s): 'offResExc' (3rd arg) must be either 0 or 1", rf->rfwave.description);
2494  }
2495  if (nPulses < 2 || nPulses > 10) {
2496  return ks_error("ks_eval_rf_hard(%s): 'nPulses' (4th arg) must be in range [2, 10]", rf->rfwave.description);
2497  }
2498 
2499  int pascalstriangle[9][10] = { {1,1},
2500  {1,2,1},
2501  {1,3,3,1},
2502  {1,4,6,4,1},
2503  {1,5,10,10,5,1},
2504  {1,6,15,20,15,6,1},
2505  {1,7,21,35,35,21,7,1},
2506  {1,8,28,56,70,56,28,8,1},
2507  {1,9,36,84,126,126,84,36,9,1} };
2508 
2509  int P[10];
2510  int pulseSum = 0;
2511  for (i = 0; i < nPulses; i++) {
2512  P[i] = pascalstriangle[nPulses - 2][i];
2513  pulseSum += P[i];
2514  }
2515 
2516  /* Calculate subpulse separation */
2517  float tau = fabs(1.0 / (2.0 * offsetFreq)); /* sec */
2518 
2519  /* Calculate number of samples so that each subpulse is as close as possible to maxB1, without exceeding it */
2520  float maxB1 = 0.20; /* Gauss */
2521  float pulseArea = (float) flip * (PI / 180.0); /* radians or Hz (depending on how you look at it) */
2522  float dt = RF_UPDATE_TIME / 1000000.0; /* sec */
2523  float subArea[10];
2524  int ntp[10];
2525  for (i = 0; i < nPulses; i++) {
2526  subArea[i] = (P[i] * pulseArea) / (pulseSum * 2.0 * PI * GAM); /* Gauss * sec */
2527  ntp[i] = ceil(subArea[i] / (maxB1 * dt)); /* number of time points in sub pulse */
2528  if (ntp[i] < 4 ) {
2529  ntp[i] = 4;
2530  }
2531  }
2532 
2533  int idx = 0;
2534  for (i = 0; i < nPulses; i++) {
2535 
2536  /* Calculate how much we have to attenuate this pulse
2537  to account for the fact that with this dwell time
2538  we couldn't hit the target flip exactly */
2539  float currentSubArea = ntp[i] * dt * maxB1; /* Gauss * sec */
2540  float amp = maxB1 * (subArea[i] / currentSubArea); /* Gauss */
2541 
2542  /* Flip every other sub pulse for off resonace excitaion */
2543  if (offResExc) {
2544  /* make sure the second center sub-pulse flips in the right direction */
2545  amp *= pow(-1.0, (float)(i + nPulses/2 % 2));
2546  }
2547 
2548  /* Stitch on the sub pulse */
2549  for (j = 0; j < ntp[i]; j++) {
2550  rf->rfwave.waveform[idx] = amp;
2551  idx++;
2552  }
2553 
2554  /* Stitch on the gap, skip for last sub pulse */
2555  if (i < nPulses - 1) {
2556  int ntp_gap = ceil(tau / dt - (float)ntp[i] / 2.0 - (float)ntp[i + 1] / 2.0);
2557  for (j = 0; j < ntp_gap; j++) {
2558  rf->rfwave.waveform[idx] = 0.0;
2559  idx++;
2560  }
2561  }
2562  }
2563 
2564  /* Make sure the resolution is divisible by 2 and the duration divisible by 4 */
2565  rf->rfwave.res = RUP_FACTOR(idx, 4);
2566  rf->rfwave.duration = rf->rfwave.res * RF_UPDATE_TIME;
2567  rf->iso2end = rf->rfwave.duration / 2;
2568  rf->start2iso = rf->rfwave.duration / 2;
2569  rf->bw = 0; /* ? */
2570  rf->flip = flip;
2571 
2572  /* If the resolution has been rounded up, place zeros at the end */
2573  for (j = idx; j < rf->rfwave.res; j++) {
2574  rf->rfwave.waveform[j] = 0.0;
2575  }
2576 
2577  /* Normalize waveform to one */
2578  ks_wave_multiplyval(&rf->rfwave, 1.0 / ks_wave_absmax(&rf->rfwave));
2579 
2580  /* Initialize amplitude to one */
2581  rf->amp = 1;
2582 
2583  /* Set the usage of the RF PULSE to off until .base.ninst > 0 (see ks_pg_rf() on HOST) */
2584  rf->rfpulse.activity = PSD_PULSE_OFF;
2585 
2586  /* Never allow binomial RF pulses to be stretched. We can avoid this by always setting extgradfile = 1 (even if this is not the case) */
2587  rf->rfpulse.extgradfile = 1;
2588 
2589  /* Initialize number of occurrences of RF pulse to zero */
2590  rf->rfpulse.num = 0;
2591 
2592  /* Save original waveform to fool rfstat, with abs(waveform) */
2593  float *saveWave = (float*)alloca(rf->rfwave.res * sizeof(float));
2594  if (offResExc) {
2595  memcpy(saveWave, rf->rfwave.waveform, rf->rfwave.res * sizeof(float));
2596  for (j = 0; j < rf->rfwave.res; j++) {
2597  rf->rfwave.waveform[j] = fabs(rf->rfwave.waveform[j]);
2598  }
2599  }
2600 
2601  /* Setup rfpulse structure */
2602  STATUS status = ks_eval_rfstat(rf);
2603  if (status != SUCCESS) return status;
2604 
2605  /* Put back the correct waveform */
2606  if (offResExc) {
2607  memcpy(rf->rfwave.waveform, saveWave, rf->rfwave.res * sizeof(float));
2608  }
2609 
2610  return SUCCESS;
2611 
2612 }
int start2iso
Definition: KSFoundation.h:944
RF_PULSE rfpulse
Definition: KSFoundation.h:948
STATUS ks_error(const char *format,...) __attribute__((format(printf
Common error message function for HOST and TGT
int32_t i
Definition: KSFoundation_tgt.c:1389
float GAM
float flip
Definition: KSFoundation.h:940
float ks_wave_absmax(const KS_WAVE *wave)
Returns the maximum absolute value in a KS_WAVE sequence object
Definition: KSFoundation_common.c:1224
#define KS_DESCRIPTION_LENGTH
Definition: KSFoundation.h:185
void ks_init_rf(KS_RF *rf)
Resets a KS_RF sequence object to its default value (KS_INIT_RF)
Definition: KSFoundation_host.c:70
KS_DESCRIPTION description
Definition: KSFoundation.h:666
STATUS ks_eval_rfstat(KS_RF *rf)
Sets up the RF_PULSE structure of a KS_RF object
Definition: KSFoundation_host.c:2615
KS_WAVE rfwave
Definition: KSFoundation.h:949
float amp
Definition: KSFoundation.h:943
int iso2end
Definition: KSFoundation.h:945
float maxB1[MAX_ENTRY_POINTS]
Definition: GERequired.e:269
char KS_DESCRIPTION[KS_DESCRIPTION_LENGTH]
Definition: KSFoundation.h:261
int res
Definition: KSFoundation.h:667
void ks_wave_multiplyval(KS_WAVE *wave, float val)
In-place scalar multiplication of a KS_WAVE
Definition: KSFoundation_common.c:1398
KS_WAVEFORM waveform
Definition: KSFoundation.h:669
int duration
Definition: KSFoundation.h:668
float bw
Definition: KSFoundation.h:941

◆ ks_eval_rfstat()

STATUS ks_eval_rfstat ( KS_RF rf)

Sets up the RF_PULSE structure of a KS_RF object

For RF scaling, SAR calculation, and RF heating calculations, GE uses a global array of RF_PULSE structs, where each RF pulse is associated with one RF_PULSE struct each. This struct array contains RF pulses from Prescan as well as the pulse sequence itself, and is normally declared in a grad_rf_<psd>.h file.

Here, each KS_RF object has its own RF_PULSE struct (the field .rfpulse), which this function sets up. The following fields in the KS_RF object must be set before calling this ks_eval_rfstat()

  1. .rfwave: Must contain the RF pulse waveform. An error is return if either .thetawave or .omegawave are non-empty (complex RF pulses not allowed)
  2. .bw: The bandwidth of the RF pulse [Hz]
  3. .iso2end: Time from the magnetic center of the RF pulse to the end, in [us]
  4. .flip: The flip angle of the RF pulse [degrees]. In this function, .flip will set .rfpulse.nom_fa and make sure .rfpulse.act_fa points to .flip

The RF waveform will be amplitude scaled so that its maximum absolute amplitude is 1.0 before making the calculations. Also the proper links between fields int the .rfpulse (RF_PULSE) struct and the parent fields in KS_RF are set up by calling ks_eval_rf_relink()

Credits: This function has been made largely after the code by Atsushi Takahashi, GE Healthcare.

Parameters
[in,out]rfKS_RF object to calculate the RF_PULSE parameters for (in field .rfpulse)
Return values
STATUSSUCCESS or FAILURE
2615  {
2616  double standard_pw = 1e-3;
2617 
2618  double nRFenvelope;
2619  int indx;
2620 
2621  double area = 0.0;
2622  double abswidth = 0.0;
2623  double effwidth = 0.0;
2624  double dtycyc = 0.0;
2625  double max_pw = 0.0;
2626  double max_b1 = 0.0;
2627  double max_int_b1_sq = 0.0;
2628  double max_rms_b1 = 0.0;
2629 
2630 
2631  if (rf->thetawave.res > 0 || rf->omegawave.res > 0) {
2632  return ks_error("ks_eval_rfstat(%s): function is not validated for phase/frequency modulated RF-pulses", rf->rfwave.description);
2633  }
2634  /* require fields 'duration', 'isodelay' and 'bw' to be set to realistic values */
2635  if (rf->rfwave.duration < 4 || rf->rfwave.duration > KS_MAXWAVELEN * RF_UPDATE_TIME || rf->rfwave.duration % 2) {
2636  return ks_error("ks_eval_rfstat: 'rf.rfwave.duration' (%d) must be an even number in the range [4,%d] us", rf->rfwave.duration, KS_MAXWAVELEN * RF_UPDATE_TIME);
2637  }
2638  if (rf->bw < 0 || rf->bw > 100000) {
2639  return ks_error("ks_eval_rfstat: 'rf.bw' (%f) must be in the range [0,100000] Hz", rf->bw);
2640  }
2641  if (rf->iso2end < 0 || rf->iso2end > KS_MAXWAVELEN * RF_UPDATE_TIME) {
2642  return ks_error("ks_eval_rfstat: 'rf.iso2end' (%d) must be in the range [0,%d] us", rf->iso2end, KS_MAXWAVELEN * RF_UPDATE_TIME);
2643  }
2644  if (rf->flip < 0.0 || rf->flip > 10000.0) {
2645  return ks_error("ks_eval_rfstat: 'rf.flip' (%.2f) must be in the range [0,3000] deg", rf->flip);
2646  }
2647  /* for reference, see: /ESE_xxxxx/psd/include/private/sar_pm.h */
2648 
2649  /* normalize RF just in case */
2650  ks_wave_multiplyval(&rf->rfwave, 1.0 / ks_wave_absmax(&rf->rfwave));
2651 
2652 
2653  for (indx = 0; indx < rf->rfwave.res; indx++) {
2654  nRFenvelope = rf->rfwave.waveform[indx];
2655  area += nRFenvelope;
2656  abswidth += fabs(nRFenvelope);
2657  effwidth += (nRFenvelope * nRFenvelope);
2658 
2659  /* dtycyc: % of Pulse Widths above 5% power */
2660  if (fabs(nRFenvelope) > 0.05)
2661  dtycyc++;
2662  }
2663 
2664  area /= rf->rfwave.res;
2665  abswidth /= rf->rfwave.res;
2666  effwidth /= rf->rfwave.res;
2667  dtycyc /= rf->rfwave.res;
2668 
2669  /* max_pw: % of the pulse width whose widest lobe is above 5% power
2670  GE uses max_pw = dtycyc for most pulses (sar_pm.h)
2671  Neither max_pw nor dtycyc seem to match well with the values used in GEs product sequences unlike
2672  the other fields, which seems to agree within a few percent */
2673  max_pw = dtycyc;
2674 
2675  max_b1 = rf->flip / 360.0 / (area * (rf->rfwave.duration) * 1e-6) / GAM;
2676  max_int_b1_sq = max_b1 * max_b1 * effwidth * (rf->rfwave.duration) * 1e-6 / standard_pw;
2677  max_rms_b1 = sqrt(max_int_b1_sq / ((rf->rfwave.duration) * 1e-6) * standard_pw);
2678 
2679 
2680  ks_eval_rf_relink(rf);
2681 
2682  rf->rfpulse.abswidth = abswidth;
2683  rf->rfpulse.effwidth = effwidth;
2684  rf->rfpulse.area = area;
2685  rf->rfpulse.dtycyc = dtycyc;
2686  rf->rfpulse.maxpw = max_pw;
2687  rf->rfpulse.num = 0;
2688  rf->rfpulse.max_b1 = max_b1;
2689  rf->rfpulse.max_int_b1_sq = max_int_b1_sq;
2690  rf->rfpulse.max_rms_b1 = max_rms_b1;
2691  rf->rfpulse.nom_fa = rf->flip; /* nom_fa and act_fa are equal */
2692  rf->rfpulse.nom_pw = rf->rfwave.duration;
2693  rf->rfpulse.nom_bw = rf->bw;
2694  rf->rfpulse.activity = PSD_PULSE_OFF; /* is set to PSD_SCAN_ON by ks_pg_rf() */
2695  rf->rfpulse.reference = 0;
2696  rf->rfpulse.isodelay = rf->iso2end;
2697  rf->rfpulse.scale = 1.0;
2698  if (rf->rfpulse.extgradfile != 1) {
2699  rf->rfpulse.extgradfile = 0; /* Only allow 0 or 1. Disallows RF pulse stretching if 1 */
2700  }
2701 
2702  return SUCCESS;
2703 }
RF_PULSE rfpulse
Definition: KSFoundation.h:948
STATUS ks_error(const char *format,...) __attribute__((format(printf
Common error message function for HOST and TGT
float GAM
float flip
Definition: KSFoundation.h:940
float ks_wave_absmax(const KS_WAVE *wave)
Returns the maximum absolute value in a KS_WAVE sequence object
Definition: KSFoundation_common.c:1224
KS_WAVE thetawave
Definition: KSFoundation.h:951
KS_DESCRIPTION description
Definition: KSFoundation.h:666
KS_WAVE rfwave
Definition: KSFoundation.h:949
int iso2end
Definition: KSFoundation.h:945
void ks_eval_rf_relink(KS_RF *rf)
(Internal use) Relinks pointers between fields in a KS_RF object
Definition: KSFoundation_host.c:2771
KS_WAVE omegawave
Definition: KSFoundation.h:950
int res
Definition: KSFoundation.h:667
void ks_wave_multiplyval(KS_WAVE *wave, float val)
In-place scalar multiplication of a KS_WAVE
Definition: KSFoundation_common.c:1398
KS_WAVEFORM waveform
Definition: KSFoundation.h:669
int duration
Definition: KSFoundation.h:668
#define KS_MAXWAVELEN
Definition: KSFoundation.h:186
float bw
Definition: KSFoundation.h:941

◆ ks_eval_rf()

STATUS ks_eval_rf ( KS_RF rf,
const char *const  desc 
)

Sets up a KS_RF object

See documentation for the KS_RF object for details on how to prepare the KS_RF object before calling ks_eval_rf()

This function does not set up a KS_RF object by itself, merely it peforms final validation, naming, makes sure fields and links in KS_RF are properly set up. ks_eval_rf() should be called for non-slice selective RF pulses. For slice-selective RF pulses, use KS_SELRF and call ks_eval_selrf() instead

Parameters
[in,out]rfPointer to the KS_RF object to be set up
[in]descA description (text string) of the KS_RF object. This description is used for pulse sequence generation (seen in the file generated by TimeHist in MGD Sim/WTools)
Return values
STATUSSUCCESS or FAILURE
2707  {
2708 
2709  if (desc == NULL || desc[0] == ' ') {
2710  return ks_error("ks_eval_rf: description (2nd arg) cannot be NULL or begin with a space");
2711  } else {
2712  strncpy(rf->rfwave.description, desc, KS_DESCRIPTION_LENGTH / 2); /* truncate after half max # chars to allow for suffixes */
2713  }
2714 
2715  ks_eval_rf_relink(rf);
2716 
2717  if (rf->role <= KS_RF_ROLE_NOTSET || rf->role > KS_RF_ROLE_INV) {
2718  return ks_error("ks_eval_rf(%s): 'rf.role' is not set or is invalid", desc);
2719  }
2720  if (rf->flip <= 0.0 || rf->flip > 10000.0) {
2721  return ks_error("ks_eval_rf(%s): 'rf.flip' (%.2f) must be in the range (0,3000] deg", desc, rf->flip);
2722  }
2723  if (rf->bw < 0 || rf->bw > 100000) {
2724  return ks_error("ks_eval_rf(%s): 'rf.bw' (%.2f) must be in the range [0,100000]", desc, rf->bw);
2725  }
2726  /* check that RF duration is even */
2727  if (rf->rfwave.duration % 2)
2728  return ks_error("ks_eval_rf (%s): RF duration (%d) [us] must be even", rf->rfwave.description, rf->rfwave.duration);
2729 
2730  /* check that RF duration and optional THETA / OMEGA waveforms have same duration */
2731  if (rf->omegawave.res) {
2732  sprintf(rf->omegawave.description, "%s", rf->rfwave.description); /* suffix '.x' is added automatically in ks_pg_wave(), where 'x' is the 1st letter of the board */
2733  if (rf->rfwave.duration != rf->omegawave.duration)
2734  return ks_error("ks_eval_rf (%s): OMEGA duration (%d) [us] must equal RF duration (%d) [us]", rf->rfwave.description, rf->omegawave.duration, rf->rfwave.duration);
2735  }
2736  if (rf->thetawave.res) {
2737  sprintf(rf->thetawave.description, "%s", rf->rfwave.description);
2738  if (rf->rfwave.duration != rf->thetawave.duration)
2739  return ks_error("ks_eval_rf (%s): THETA duration (%d) [us] must equal RF duration (%d) [us]", rf->rfwave.description, rf->thetawave.duration, rf->rfwave.duration);
2740  }
2741 
2742  if (rf->iso2end < 0 || rf->iso2end > rf->rfwave.duration) {
2743  return ks_error("ks_eval_rf (%s): 'rf.iso2end (%d) must in range [0,%d] [us]", rf->rfwave.description, rf->iso2end, rf->rfwave.duration);
2744  }
2745 
2746  /* check that .iso2end is equal to .rfpulse.isodelay */
2747  if (rf->rfpulse.isodelay != rf->iso2end) {
2748  return ks_error("ks_eval_rf (%s): 'rf.rfpulse.isodelay (%d) must be equal to 'rf.iso2end' (%d) [us]", rf->rfwave.description, rf->rfpulse.isodelay, rf->iso2end);
2749  }
2750 
2751  rf->rfpulse.isodelay = RUP_GRD(rf->rfpulse.isodelay); /* Round up using RUP_GRD() to fall on gradient raster (GRAD_UPDATE_TIME = 4us) */
2752  rf->iso2end = rf->rfpulse.isodelay;
2753  rf->start2iso = rf->rfwave.duration - rf->iso2end; /* time from start of RF pulse to its magnetic center */
2754 
2755  /* initialize amplitude to 1 */
2756  rf->amp = 1;
2757 
2758  /* set the usage of the RF PULSE to off until .base.ninst > 0 (see ks_pg_rf() on HOST) */
2759  rf->rfpulse.activity = PSD_PULSE_OFF;
2760 
2761  /* initialize number of occurrences of RF pulse to zero */
2762  rf->rfpulse.num = 0;
2763 
2764 
2765  return SUCCESS;
2766 
2767 } /* ks_eval_rf */
int start2iso
Definition: KSFoundation.h:944
int role
Definition: KSFoundation.h:939
RF_PULSE rfpulse
Definition: KSFoundation.h:948
STATUS ks_error(const char *format,...) __attribute__((format(printf
Common error message function for HOST and TGT
Definition: KSFoundation.h:1953
float flip
Definition: KSFoundation.h:940
#define KS_DESCRIPTION_LENGTH
Definition: KSFoundation.h:185
KS_WAVE thetawave
Definition: KSFoundation.h:951
KS_DESCRIPTION description
Definition: KSFoundation.h:666
KS_WAVE rfwave
Definition: KSFoundation.h:949
Definition: KSFoundation.h:1953
float amp
Definition: KSFoundation.h:943
int iso2end
Definition: KSFoundation.h:945
void ks_eval_rf_relink(KS_RF *rf)
(Internal use) Relinks pointers between fields in a KS_RF object
Definition: KSFoundation_host.c:2771
KS_WAVE omegawave
Definition: KSFoundation.h:950
int res
Definition: KSFoundation.h:667
int duration
Definition: KSFoundation.h:668
float bw
Definition: KSFoundation.h:941

◆ ks_eval_rf_relink()

void ks_eval_rf_relink ( KS_RF rf)

(Internal use) Relinks pointers between fields in a KS_RF object

This function is used by other RF functions to make sure that the links from pointers in .rfpulse are relinked to other fields inside the KS_RF object. This function does not need to be called manually as long as either ks_eval_rf(), ks_eval_selrf() are called.

If a KS_RF object is copied to another KS_RF object by simple assignment ('=' sign), then these pointers must be updated before the assigned KS_RF object can be used. Best practice is to call ks_eval_rf() or ks_eval_selrf() instead of this function, as more validation is performed and because description of the new KS_RF is also updated

Parameters
[in,out]rfPointer to the KS_RF object to be set up
2771  {
2772 
2773  /* link as required by RF_PULSE */
2774  rf->rfpulse.pw = &(rf->rfwave.duration);
2775  rf->rfpulse.amp = &(rf->amp);
2776  rf->rfpulse.act_fa = &(rf->flip);
2777  rf->rfpulse.res = &(rf->rfwave.res);
2778  rf->rfpulse.exciter = &ks_rhoboard;
2779 }
int ks_rhoboard
Definition: KSFoundation_common.c:42
RF_PULSE rfpulse
Definition: KSFoundation.h:948
float flip
Definition: KSFoundation.h:940
KS_WAVE rfwave
Definition: KSFoundation.h:949
float amp
Definition: KSFoundation.h:943
int res
Definition: KSFoundation.h:667
int duration
Definition: KSFoundation.h:668

◆ ks_eval_seltrap()

STATUS ks_eval_seltrap ( KS_TRAP trap,
const char *const  desc,
float  slewrate,
float  slthick,
float  bw,
int  rfduration 
)

(Internal use) Sets up a trapezoid for RF slice selection

This function is used by ks_eval_selrf_constrained() (and its wrapper functions, e.g. ks_eval_selrf()), and there is no need to use this function directly.

Based on the RF bandwidth and duration, this function will create a trapezoid gradient that with this type of RF pulse creates a slice with the desired thickness, by in turn calling ks_calc_selgradamp().

Parameters
[out]trapPointer to the KS_TRAP object to be set up
[in]descA description (text string) of the KS_PHASER object. This description is used for pulse sequence generation (seen in the file generated by TimeHist in MGD Sim/WTools)
[in]slewrateThe maximum allowed slewrate ([(G/cm) / us]). Value of 0.01 corresponds to 100 mT/m/s
[in]slthickThe desired slice thickness in [mm]. If = 0, the slice selection gradient will have zero amplitude (hard pulses)
[in]bwThe bandwidth of the RF pulse to use with the gradient. If = 0, the slice selection gradient will have zero amplitude (hard pulses)
[in]rfdurationThe duration of the RF pulse to use with the gradient
Return values
STATUSSUCCESS or FAILURE
2789  {
2790 
2791  /* Reset all fields and waveforms */
2792  ks_init_trap(trap);
2793 
2794  if ((rfduration < GRAD_UPDATE_TIME) || (rfduration % GRAD_UPDATE_TIME)) {
2795  return ks_error("%s: RF duration must be positive and divisible by 4", __FUNCTION__);
2796  }
2797 
2798  if (slthick < 0 || bw < 0) {
2799  /* N.B.: slthick may be exactly 0, in which case a zero amp gradient is created */
2800  return ks_error("%s: slice thickness & bandwidth cannot be negative", __FUNCTION__);
2801  }
2802 
2803  if (desc == NULL || desc[0] == ' ') {
2804  return ks_error("ks_eval_seltrap: trap name (2nd arg) cannot be NULL or begin with a space");
2805  }
2806 
2807  strncpy(trap->description, desc, KS_DESCRIPTION_LENGTH - 1);
2808 
2809  trap->amp = ks_calc_selgradamp(bw, slthick); /* amp [G/cm] */
2810 
2811  trap->ramptime = RUP_GRD((int) (trap->amp / slewrate)); /* ramp time [us] */
2812 
2813  if (trap->ramptime <= 0) {
2814  trap->ramptime = 4;
2815  }
2816 
2817  trap->plateautime = rfduration; /* plateau time [us] */
2818  trap->duration = trap->plateautime + 2 * trap->ramptime; /* total duration [us] */
2819  trap->area = (trap->plateautime + trap->ramptime) * trap->amp;
2820 
2821 
2822  return SUCCESS;
2823 
2824 }
int plateautime
Definition: KSFoundation.h:584
STATUS ks_error(const char *format,...) __attribute__((format(printf
Common error message function for HOST and TGT
void ks_init_trap(KS_TRAP *trap)
Resets a KS_TRAP sequence object to its default value (KS_INIT_TRAP)
Definition: KSFoundation_host.c:58
#define KS_DESCRIPTION_LENGTH
Definition: KSFoundation.h:185
float area
Definition: KSFoundation.h:582
float amp
Definition: KSFoundation.h:581
KS_DESCRIPTION description
Definition: KSFoundation.h:580
float ks_calc_selgradamp(float rfbw, float slthick)
Returns the gradient amplitude in [G/cm] necessary for slice selection (internal use)
Definition: KSFoundation_common.c:309
int duration
Definition: KSFoundation.h:585
int ramptime
Definition: KSFoundation.h:583

◆ ks_eval_selrf_constrained()

STATUS ks_eval_selrf_constrained ( KS_SELRF selrf,
const char *const  desc,
float  ampmax,
float  slewrate 
)

Sets up a KS_SELRF object for RF slice selection, subject to gradients constraints specified as input arguments

This function does not set up the KS_RF field in KS_SELRF itself, see KS_RF and ks_eval_rf() for more details on how to prepare the .rf field in KS_SELRF before calling ks_eval_selrf_constrained()

The following fields in the KS_SELRF object must be set up before calling this function:

  1. A ready KS_RF object, with a proper .rf.role (see below for details)
  2. .slthick: Slice thickness in [mm]. Note that the slice thickness often needs to be made larger to compensate from slice narrowing due to multiple RF pulses. GE uses usually gscale_rfX CVs with values often between 0.8-0.9. Dividing with this number gives a thicker slice in the calculations. NOTE: .slthick = 0 is accepted and results in zero gradient amplitude with minimal 4us ramps and a plateautime = .rf.rfwave.duration (cf. ks_eval_seltrap()->ks_calc_selgradamp())

ks_eval_selrf_constrained() checks .rf.role to decide on how to set up its gradient objects. The following values of .rf.role are allowed:

  • KS_RF_ROLE_EXC, indicating an RF excitation role. ks_eval_selrf_constrained() will in this case:
    • disable .pregrad
    • make .grad.plateautime equal to the RF duration (.rf.rfwave.duration) and the gradient amplitude (.grad.amp [G/cm]) will be set to the proper value depending on the BW (.rf.bw) and slice thickness (.slthick). The field .grad.description will have a ".trap" suffix added to it
    • make .postgrad to have a gradient area that rephases the slice. This is dependent on the isodelay of the RF pulse (.rf.iso2end) and the slice select gradient amplitude (.grad.amp). The field .postgrad.description will have a ".reph" suffix added to it
  • KS_RF_ROLE_REF, indicating an RF refocusing role. ks_eval_selrf_constrained() will in this case:
    • use .pregrad to make the left crusher (never bridged with slice selection). The size of the crusher can be controlled by .crusherscale. The field .pregrad.description will have a ".LC" suffix added to it
    • do the same with .grad as done for KS_RF_ROLE_EXC
    • use .postgrad to make the right crusher (never bridged with slice selection). The size of the crusher can be controlled by .crusherscale. The field .postgrad.description will have a ".RC" suffix added to it
  • KS_RF_ROLE_INV, indicating an RF inversion role. ks_eval_selrf_constrained() will in this case:
    • disable .pregrad
    • do the same with .grad as done for KS_RF_ROLE_EXC
    • disable .postgrad
  • KS_RF_ROLE_SPSAT, indicating a spatial RF saturation. ks_eval_selrf_constrained() will in this case:
    • disable .pregrad
    • do the same with .grad as done for KS_RF_ROLE_EXC
    • use .postgrad to make a gradient spoiler (never bridged with slice selection). The size of the spoiler can be controlled by .crusherscale. The field .postgrad.description will have a ".spoiler" suffix added to it

The role KS_RF_ROLE_CHEMSAT, indicates a non-slice selective, chemically selective, RF (usually fat-sat). ks_eval_selrf_constrained() will in this case throw an error since no gradients should be used

There are three wrapper functions to this function (ks_eval_selrf(), ks_eval_selrf1(), ks_eval_selrf2()) that have reduced number of input arguments, and calls ks_eval_selrf_constrained() with different preset gradient constraints. ks_eval_selrf() makes fewest assumptions, making it the most general choice.

Using a custom gradient wave for slice selection (.gradwave)

To support e.g. VERSEd RF excitations, a custom gradient wave can be used. If .gradwave.res > 0, ks_eval_selrf_constrained() will disable .grad and use .gradwave instead. When .rf.role = KS_RF_ROLE_EXC, the rephasing gradient area (in .postgrad) will be calculated by integrating the gradient waveform from the RF isocenter to the end. .gradwave must be created before calling ks_eval_selrf_constrained() using either ks_eval_wave() or ks_eval_wave_file(), and the units must be in [G/cm]. To further scale the amplitude of .gradwave before calling ks_eval_selrf_constrained(), consider e.g. ks_wave_multiplyval(), ks_wave_absmax(), and possibly ks_wave_addval()

Parameters
[in,out]selrfPointer to the KS_SELRF object to be set up
[in]descA description (text string) of the KS_SELRF object. This description is used for pulse sequence generation (seen in the file generated by TimeHist in MGD Sim/WTools)
[in]ampmaxThe maximum allowed gradient amplitude ([G/cm])
[in]slewrateThe maximum allowed slewrate ([(G/cm) / us]). Value of 0.01 corresponds to 100 mT/m/s
Return values
STATUSSUCCESS or FAILURE
2827  {
2828  char selname[KS_DESCRIPTION_LENGTH], preselname[KS_DESCRIPTION_LENGTH], postselname[KS_DESCRIPTION_LENGTH];
2829  float minthick;
2830  STATUS status;
2831 
2832  if (desc == NULL || desc[0] == ' ') {
2833  return ks_error("%s: description (2nd arg) cannot be NULL or begin with a space", __FUNCTION__);
2834  }
2835 
2836  /* setup the RF pulse */
2837  status = ks_eval_rf(&selrf->rf, desc);
2838  if (status != SUCCESS) return status;
2839 
2840  /* with slice selection (trap or gradwave), the RF duration must be divisible by GRAD_UPDATE_TIME (4) [us] */
2841  if ((selrf->rf.rfwave.duration < 4) || (selrf->rf.rfwave.duration % 4)) {
2842  return ks_error("%s (%s): RF duration (%d) must be divisible by GRAD_UPDATE_TIME (4)", __FUNCTION__, desc, selrf->rf.rfwave.duration);
2843  }
2844 
2845  /* init members of selrf to their default state */
2846  ks_init_trap(&selrf->pregrad);
2847  ks_init_trap(&selrf->grad);
2848  ks_init_trap(&selrf->postgrad);
2849  ks_init_sms_info(&selrf->sms_info);
2850 
2851  /* Slice thickness */
2852  /* protect against e.g. +/-Inf thicknesses. Note that we do *NOT* complain about slthick = 0. This is OK and means zero amp dummy gradient */
2853  if (selrf->slthick < 0 || selrf->slthick > 600) {
2854  return ks_error("%s (%s): Thickness not in range [0,600] mm", __FUNCTION__, desc);
2855  }
2856 
2857  minthick = selrf->rf.bw / (ampmax * (0.1) * GAM);
2858  if (selrf->slthick > 0 && selrf->slthick < minthick) {
2859  /* N.B.: checking for slthick > 0 above, allows slthick = 0 without leaading to this error (meaning no slice sel amp) */
2860  return ks_error("%s (%s): Minimum thickness (%.1f [mm]) violated", __FUNCTION__, desc, minthick);
2861  }
2862 
2863  /* if we have a gradwave generated, check that it has the same duration as the rfpulse */
2864  if (selrf->gradwave.res) {
2865  if (selrf->gradwave.duration != selrf->rf.rfwave.duration) {
2866  return ks_error("%s (%s): Custom gradient wave duration (%d) must equal the RF duration (%d)", __FUNCTION__, desc, selrf->gradwave.duration, selrf->rf.rfwave.duration);
2867  }
2868  if (selrf->gradwave.duration % selrf->gradwave.res) {
2869  return ks_error("%s (%s): Custom gradient wave duration (%d) must be divisible by res (%d)", __FUNCTION__, desc, selrf->gradwave.duration, selrf->gradwave.res);
2870  }
2871  sprintf(selrf->gradwave.description, "%s.gradwave", selrf->rf.rfwave.description); /* suffix '.x' is added automatically in ks_pg_wave(), where 'x' is the 1st letter of the board */
2872  }
2873 
2874  /* Setup the gradients (trapezoid case) */
2875  sprintf(selname, "%s.trap", selrf->rf.rfwave.description); /* suffix '.x' is added automatically in ks_pg_wave(), where 'x' is the 1st letter of the board */
2876 
2877  /* slice select */
2878  if (selrf->gradwave.res == 0) {
2879  /* slice select using trapezoid (.grad). If selrf->slthick = 0 or selrf->rf.bw = 0, a zero amp gradient will be created to make consistent KS_SELRF
2880  This is facilitated by ks_eval_seltrap()->ks_calc_selgradamp() */
2881  status = ks_eval_seltrap(&selrf->grad, selname, slewrate, selrf->slthick, selrf->rf.bw, selrf->rf.rfwave.duration);
2882  if (status != SUCCESS) return status;
2883  } else {
2885  /* slice select using existing custom waveform (.gradwave) - scaling gradwave amplitude correctly regardless of current amplitude
2886  assumes that the maximum absolute value in gradwave.waveform corresponds to the gradient amplitude intended for the current rf BW and slthick */
2887  float maxval = ks_wave_max(&selrf->gradwave); /* not absmax, but max, to allow for larger negative lobes for e.g. flyback SPSP */
2888  if (maxval <= 0) {
2889  /* a gradwave that is strictly below zero, normalize instead by absmax */
2890  maxval = fabs(ks_wave_min(&selrf->gradwave));
2891  }
2892  ks_wave_multiplyval(&selrf->gradwave, ks_calc_selgradamp(selrf->rf.bw, selrf->slthick) / maxval);
2893 
2894  } else if (selrf->gradwave.gradwave_units == KS_NOTSET) {
2895  return ks_error("%s (%s): Custom gradient wave scaling policy not set", __FUNCTION__, desc);
2896  }
2897  }
2898 
2899 
2900  switch (selrf->rf.role) {
2901 
2902  /********************************************************************************/
2903  /*********************** RF SEL EXCITATION **************************************/
2904  /********************************************************************************/
2905  case KS_RF_ROLE_EXC:
2906 
2907  sprintf(postselname, "%s.reph", selrf->rf.rfwave.description);
2908 
2909  if (selrf->gradwave.res == 0) { /* Slice selection using .grad (KS_TRAP) */
2910 
2911  /* area needed for rephaser */
2912  selrf->postgrad.area = -(selrf->rf.rfpulse.isodelay + selrf->grad.ramptime / 2) * selrf->grad.amp;
2913 
2914  } else { /* Slice selection using .gradwave (KS_WAVE) */
2915 
2916  int i, isostart;
2917  int grad_dwell = selrf->gradwave.duration / selrf->gradwave.res;
2918 
2919  /* rephaser area needed for gradient wave */
2920  selrf->postgrad.area = 0;
2921  if (selrf->rf.iso2end_subpulse != KS_NOTSET) {
2922  /* SPSP's subpulses may be linear, min or max phase pulses, not neccesarily
2923  centered on the plateau of each lobe. For these pulses, the iso2end_subpulse field
2924  is not KS_NOTSET (-1). One can also have self-rephased gradwaves, not needing
2925  a .postgrad. In this case rf.iso2end_subpulse = 0 would disable this, while keeping
2926  rf.iso2end for TE calculations */
2927  isostart = (selrf->gradwave.duration - selrf->rf.iso2end_subpulse) / grad_dwell;
2928  } else {
2929  isostart = (selrf->gradwave.duration - selrf->rf.iso2end) / grad_dwell;
2930  }
2931  for (i = isostart; i < selrf->gradwave.res; i++) {
2932  selrf->postgrad.area -= selrf->gradwave.waveform[i] * grad_dwell;
2933  }
2934 
2935  }
2936 
2937  /* setup rephaser trapezoid */
2938  status = ks_eval_trap_constrained(&selrf->postgrad, postselname, ampmax, slewrate, 0);
2939  if (status != SUCCESS) return status;
2940 
2941  break;
2942  /********************************************************************************/
2943  /*********************** RF SEL REFOCUSING **************************************/
2944  /********************************************************************************/
2945  case KS_RF_ROLE_REF:
2946 
2949 
2950  sprintf(preselname, "%s.LC", selrf->rf.rfwave.description);
2951  sprintf(postselname, "%s.RC", selrf->rf.rfwave.description);
2952 
2953  status = ks_eval_trap_constrained(&selrf->pregrad, preselname, ampmax, slewrate, 0);
2954  if (status != SUCCESS) return status;
2955  status = ks_eval_trap_constrained(&selrf->postgrad, postselname, ampmax, slewrate, 0);
2956  if (status != SUCCESS) return status;
2957 
2958  break;
2959  /********************************************************************************/
2960  /*********************** RF SEL INVERSION ***************************************/
2961  /********************************************************************************/
2962  case KS_RF_ROLE_INV:
2963 
2964  /* nothing to do for pregrad or postgrad */
2965 
2966  break;
2967  /********************************************************************************/
2968  /*********************** RF SPATIAL SATURATION **********************************/
2969  /********************************************************************************/
2970  case KS_RF_ROLE_SPSAT:
2971 
2973 
2974  sprintf(postselname, "%s.spoiler", selrf->rf.rfwave.description);
2975 
2976  status = ks_eval_trap_constrained(&selrf->postgrad, postselname, ampmax, slewrate, 0);
2977  if (status != SUCCESS) return status;
2978 
2979  break;
2980  /********************************************************************************/
2981  /********************************************************************************/
2982  /********************************************************************************/
2983  default:
2984  return ks_error("ks_eval_selrf(%s): RF role must be one of: KS_RF_ROLE_EXC, KS_RF_ROLE_REF, KS_RF_ROLE_INV, KS_RF_ROLE_SPSAT", desc);
2985  break;
2986 
2987  } /* role */
2988 
2989 
2990 
2991  /********************************************************************************/
2992  /*********************** Validation *********************************************/
2993  /********************************************************************************/
2994 
2995  if (selrf->gradwave.res) { /* custom gradient wave */
2996  float mostfavorable_gradmax = FMax(3, loggrd.tx, loggrd.ty, loggrd.tz);
2997 
2998 
2999 
3000  if (ks_wave_absmax(&selrf->gradwave) > mostfavorable_gradmax) {
3001  return ks_error("ks_eval_selrf(%s): too large gradient amplitude for 'gradwave'", desc);
3002  }
3003 
3004  /* we explicitly check for the hardware limits since loggrd is typically lower because obloptimze_epi
3005  (which allows access to the full slewrate limits) was not called for the rf pulse */
3006  float max_sys_slewrate = phygrd.zfs/phygrd.zrt;
3007  float max_gradwave_slewrate = ks_wave_maxslew(&selrf->gradwave);
3008  if (max_gradwave_slewrate > max_sys_slewrate) {
3009  return ks_error("ks_eval_selrf(%s): slewrate of gradwave (%.1f T/m/s) exceeds limits (%.1f T/m/s)", desc, max_gradwave_slewrate*1e4, max_sys_slewrate*1e4);
3010  }
3011 
3012  } else { /* standard trapezoid */
3013 
3014  /* timing check: grad vs. RF */
3015  if (selrf->grad.duration && selrf->grad.plateautime != selrf->rf.rfwave.duration) {
3016  return ks_error("ks_eval_selrf(%s): grad.plateautime (%d) != rf.rfwave.duration (%d)", desc, selrf->grad.plateautime, selrf->rf.rfwave.duration);
3017  }
3018 
3019  /* gradient duration divisibility check */
3020  if (selrf->grad.duration % GRAD_UPDATE_TIME) {
3021  return ks_error("ks_eval_selrf(%s): grad.duration (%d) must be divisible by 4 [us]", desc, selrf->grad.duration);
3022  }
3023 
3024  }
3025 
3026  return SUCCESS;
3027 
3028 } /* ks_eval_selrf_constrained */
#define KS_RF_STANDARD_CRUSHERAREA
Definition: KSFoundation.h:154
int plateautime
Definition: KSFoundation.h:584
KS_TRAP grad
Definition: KSFoundation.h:1491
int iso2end_subpulse
Definition: KSFoundation.h:946
#define KS_NOTSET
Definition: KSFoundation.h:103
int role
Definition: KSFoundation.h:939
KS_SMS_INFO sms_info
Definition: KSFoundation.h:1494
float ks_wave_maxslew(const KS_WAVE *wave)
Returns the maximum absolute value in a KS_WAVE
Definition: KSFoundation_common.c:1250
KS_WAVE gradwave
Definition: KSFoundation.h:1493
RF_PULSE rfpulse
Definition: KSFoundation.h:948
STATUS ks_error(const char *format,...) __attribute__((format(printf
Common error message function for HOST and TGT
void ks_init_trap(KS_TRAP *trap)
Resets a KS_TRAP sequence object to its default value (KS_INIT_TRAP)
Definition: KSFoundation_host.c:58
Definition: KSFoundation.h:1953
Definition: KSFoundation.h:1953
int gradwave_units
Definition: KSFoundation.h:671
int32_t i
Definition: KSFoundation_tgt.c:1389
STATUS ks_eval_trap_constrained(KS_TRAP *trap, const char *const desc, float ampmax, float slewrate, int minduration)
Sets up a trapezoid using a KS_TRAP sequence object with gradient constraints specified as input argu...
Definition: KSFoundation_host.c:319
Definition: KSFoundation.h:1975
PHYS_GRAD phygrd
Definition: KSFoundation.h:1953
float GAM
float ks_wave_absmax(const KS_WAVE *wave)
Returns the maximum absolute value in a KS_WAVE sequence object
Definition: KSFoundation_common.c:1224
#define KS_DESCRIPTION_LENGTH
Definition: KSFoundation.h:185
KS_TRAP pregrad
Definition: KSFoundation.h:1490
KS_RF rf
Definition: KSFoundation.h:1485
KS_DESCRIPTION description
Definition: KSFoundation.h:666
LOG_GRAD loggrd
KS_WAVE rfwave
Definition: KSFoundation.h:949
float area
Definition: KSFoundation.h:582
float amp
Definition: KSFoundation.h:581
void ks_init_sms_info(KS_SMS_INFO *sms_info)
Resets a KS_SMS_INFO sequence object to its default value (KS_INIT_SMS_INFO)
Definition: KSFoundation_host.c:74
KS_TRAP postgrad
Definition: KSFoundation.h:1492
int iso2end
Definition: KSFoundation.h:945
STATUS ks_eval_rf(KS_RF *rf, const char *const desc)
Sets up a KS_RF object
Definition: KSFoundation_host.c:2707
float ks_calc_selgradamp(float rfbw, float slthick)
Returns the gradient amplitude in [G/cm] necessary for slice selection (internal use)
Definition: KSFoundation_common.c:309
float ks_wave_max(const KS_WAVE *wave)
Returns the maximum value in a KS_WAVE sequence object
Definition: KSFoundation_common.c:1151
float crusherscale
Definition: KSFoundation.h:1488
STATUS ks_eval_seltrap(KS_TRAP *trap, const char *const desc, float slewrate, float slthick, float bw, int rfduration)
(Internal use) Sets up a trapezoid for RF slice selection
Definition: KSFoundation_host.c:2789
int duration
Definition: KSFoundation.h:585
float ks_wave_min(const KS_WAVE *wave)
Returns the minimum value in a KS_WAVE sequence object
Definition: KSFoundation_common.c:1177
int res
Definition: KSFoundation.h:667
void ks_wave_multiplyval(KS_WAVE *wave, float val)
In-place scalar multiplication of a KS_WAVE
Definition: KSFoundation_common.c:1398
Definition: KSFoundation.h:1953
KS_WAVEFORM waveform
Definition: KSFoundation.h:669
int ramptime
Definition: KSFoundation.h:583
int duration
Definition: KSFoundation.h:668
float slthick
Definition: KSFoundation.h:1487
float bw
Definition: KSFoundation.h:941

◆ ks_eval_selrf()

STATUS ks_eval_selrf ( KS_SELRF selrf,
const char *const  desc 
)

Sets up a KS_SELRF object for RF slice selection with preset gradient constraints

This is a wrapper function to ks_eval_selrf_constrained() with gradient constraints set to allow this KS_SELRF to be played on any axis while other gradients (set up with the same constraints) can be played out on the other two gradient boards, for any slice angulation. This is the generic and safest configuration, but for thin slices and high RF BWs, the gradient limits may be reached, especially for double oblique slices

See ks_eval_selrf_constrained() for details on fields that need to be set before calling this function

Parameters
[in,out]selrfPointer to the KS_SELRF object to be set up
[in]descA description (text string) of the KS_SELRF object. This description is used for pulse sequence generation (seen in the file generated by TimeHist in MGD Sim/WTools)
Return values
STATUSSUCCESS or FAILURE
3032  {
3033 
3035 
3036 }
float ks_syslimits_ampmax(LOG_GRAD loggrd)
Returns the maximum gradient amplitude that can be used on all gradient boards simultaneously
Definition: KSFoundation_common.c:201
STATUS ks_eval_selrf_constrained(KS_SELRF *selrf, const char *const desc, float ampmax, float slewrate)
Sets up a KS_SELRF object for RF slice selection, subject to gradients constraints specified as input...
Definition: KSFoundation_host.c:2827
LOG_GRAD loggrd
float ks_syslimits_slewrate(LOG_GRAD loggrd)
Returns the maximum slewrate that can be used on all gradient boards simultaneously
Definition: KSFoundation_common.c:248

◆ ks_eval_selrf2()

STATUS ks_eval_selrf2 ( KS_SELRF selrf,
const char *const  desc 
)

Sets up a KS_SELRF object for RF slice selection with preset gradient constraints

This is a wrapper function to ks_eval_selrf_constrained() with gradient constraints set to allow this KS_SELRF to be played on any axis while one more gradient (set up with the same constraints) can be played out on another gradient board, for any slice angulation. For double oblique slice angulations, this function will allow for thinner slices and higher RF BWs than ks_eval_selrf()

See ks_eval_selrf_constrained() for details on fields that need to be set before calling this function

Parameters
[in,out]selrfPointer to the KS_SELRF object to be set up
[in]descA description (text string) of the KS_SELRF object. This description is used for pulse sequence generation (seen in the file generated by TimeHist in MGD Sim/WTools)
Return values
STATUSSUCCESS or FAILURE
3038  {
3039 
3041 
3042 }
STATUS ks_eval_selrf_constrained(KS_SELRF *selrf, const char *const desc, float ampmax, float slewrate)
Sets up a KS_SELRF object for RF slice selection, subject to gradients constraints specified as input...
Definition: KSFoundation_host.c:2827
LOG_GRAD loggrd
float ks_syslimits_ampmax2(LOG_GRAD loggrd)
Returns the maximum gradient amplitude that can be used on two gradient boards simultaneously
Definition: KSFoundation_common.c:205
float ks_syslimits_slewrate2(LOG_GRAD loggrd)
Returns the maximum slewrate that can be used on two gradient boards simultaneously
Definition: KSFoundation_common.c:252

◆ ks_eval_selrf1()

STATUS ks_eval_selrf1 ( KS_SELRF selrf,
const char *const  desc 
)

Sets up a KS_SELRF object for RF slice selection with preset gradient constraints

This is a wrapper function to ks_eval_selrf_constrained() with gradient constraints set to allow this KS_SELRF to be played on any axis, as long as no other gradients are be played out simultaneously. For double oblique slice angulations, this function will allow for thinner slices and higher RF BWs than ks_eval_selrf() and ks_eval_selrf2()

See ks_eval_selrf_constrained() for details on fields that need to be set before calling this function

Parameters
[in,out]selrfPointer to the KS_SELRF object to be set up
[in]descA description (text string) of the KS_SELRF object. This description is used for pulse sequence generation (seen in the file generated by TimeHist in MGD Sim/WTools)
Return values
STATUSSUCCESS or FAILURE
3044  {
3045 
3047 
3048 }
STATUS ks_eval_selrf_constrained(KS_SELRF *selrf, const char *const desc, float ampmax, float slewrate)
Sets up a KS_SELRF object for RF slice selection, subject to gradients constraints specified as input...
Definition: KSFoundation_host.c:2827
float ks_syslimits_slewrate1(LOG_GRAD loggrd)
Returns the maximum slewrate that can be used on one gradient board at a time
Definition: KSFoundation_common.c:256
LOG_GRAD loggrd
float ks_syslimits_ampmax1(LOG_GRAD loggrd)
Returns the maximum gradient amplitude that can be used on one gradient board at a time...
Definition: KSFoundation_common.c:209

◆ ks_eval_selrf1p()

STATUS ks_eval_selrf1p ( KS_SELRF selrf,
const char *const  desc 
)

Sets up a KS_SELRF object for RF slice selection with physical gradient constraints (invariant to slice angulation)

Parameters
[in,out]selrfPointer to the KS_SELRF object to be set up
[in]descA description (text string) of the KS_SELRF object. This description is used for pulse sequence generation (seen in the file generated by TimeHist in MGD Sim/WTools)
Return values
STATUSSUCCESS or FAILURE
3050  {
3051 
3053 
3054 }
float ks_syslimits_ampmax1p(LOG_GRAD loggrd)
Returns the maximum gradient amplitude on the physical gradient axes
Definition: KSFoundation_common.c:214
STATUS ks_eval_selrf_constrained(KS_SELRF *selrf, const char *const desc, float ampmax, float slewrate)
Sets up a KS_SELRF object for RF slice selection, subject to gradients constraints specified as input...
Definition: KSFoundation_host.c:2827
LOG_GRAD loggrd
float ks_syslimits_slewrate1p(LOG_GRAD loggrd)
Returns the maximum gradient amplitude on the physical gradient axes
Definition: KSFoundation_common.c:261

◆ ks_eval_findb1()

float ks_eval_findb1 ( KS_SELRF selrf,
float  max_b1,
double  scaleFactor,
int  sms_multiband_factor,
int  sms_phase_modulation_mode,
float  sms_slice_gap 
)

Finds a stretch factor leading to the specified peak B1

Parameters
[in]selrfrf object KS_SELRF
[in]max_b1peak B1
[in]scaleFactorinitial stretch factor
[in]sms_multiband_factorSMS factor
[in]sms_phase_modulation_modeSMS phase modulation mode
[in]sms_slice_gapSMS slice gap
Returns
float stretch factor
3057  {
3058 
3059  STATUS status;
3060  float curr_b1 = 0.0;
3061  int debug = 0;
3062  double stepDown = 1.01;
3063  double stepUp = 1.05;
3064 
3065  /* Init tmp rf object */
3066  KS_SELRF selrftmp;
3067  ks_init_selrf(&selrftmp);
3068 
3069  /* Make the RF-pulse longer until its B1 peak is under its target */
3070  do {
3071  selrftmp = *selrf;
3072  ks_eval_stretch_rf(&selrftmp.rf, scaleFactor);
3073  status = ks_eval_selrf(&selrftmp, "tmp+");
3074  if (status == FAILURE) {continue;}
3075  if (sms_multiband_factor > 1) {
3076  status = ks_eval_sms_make_multiband(&selrftmp, &selrftmp, sms_multiband_factor, sms_phase_modulation_mode, sms_slice_gap, 0);
3077  } else {
3078  status = ks_eval_rfstat(&selrftmp.rf);
3079  }
3080  if (status == FAILURE) {continue;}
3081  curr_b1 = selrftmp.rf.rfpulse.max_b1;
3082  scaleFactor *= stepUp;
3083  if (debug) {ks_dbg("+ scaleFactor=%f, currB1=%f, maxB1=%f", scaleFactor, curr_b1, max_b1);}
3084  }
3085  while (fabs(curr_b1) > max_b1);
3086  scaleFactor /= stepUp;
3087 
3088  /* Make the RF-pulse shorter until its B1 peak hits its target */
3089  while ((fabs(curr_b1) < max_b1) && (scaleFactor > 0)) {
3090  selrftmp = *selrf;
3091  ks_eval_stretch_rf(&selrftmp.rf, scaleFactor);
3092  status = ks_eval_selrf(&selrftmp, "tmp-");
3093  if (status == FAILURE) {break;}
3094  if (sms_multiband_factor > 1) {
3095  status = ks_eval_sms_make_multiband(&selrftmp, &selrftmp, sms_multiband_factor, sms_phase_modulation_mode, sms_slice_gap, 0);
3096  } else {
3097  status = ks_eval_rfstat(&selrftmp.rf);
3098  }
3099  if (status == FAILURE) {break;}
3100  curr_b1 = selrftmp.rf.rfpulse.max_b1;
3101  scaleFactor /= stepDown;
3102  if (debug) {ks_dbg("- scaleFactor=%f, currB1=%f, maxB1=%f", scaleFactor, curr_b1, max_b1);}
3103  }
3104  scaleFactor *= stepDown;
3105 
3106  return scaleFactor;
3107 }
void ks_init_selrf(KS_SELRF *selrf)
Resets a KS_SELRF sequence object to its default value (KS_INIT_SELRF)
Definition: KSFoundation_host.c:78
Composite sequence object for slice-selective RF
Definition: KSFoundation.h:1484
int debug
Definition: GERequired.e:2642
RF_PULSE rfpulse
Definition: KSFoundation.h:948
KS_RF rf
Definition: KSFoundation.h:1485
STATUS ks_eval_rfstat(KS_RF *rf)
Sets up the RF_PULSE structure of a KS_RF object
Definition: KSFoundation_host.c:2615
STATUS ks_eval_sms_make_multiband(KS_SELRF *selrfMB, const KS_SELRF *selrf, const int sms_multiband_factor, const int sms_phase_modulation_mode, const float sms_slice_gap, int debug)
Creates a SMS (simultaneous-multi-slice) version of a KS_SELRF object
Definition: KSFoundation_host.c:3169
STATUS STATUS ks_dbg(const char *format,...) __attribute__((format(printf
Common debug message function for HOST and TGT
STATUS ks_eval_selrf(KS_SELRF *selrf, const char *const desc)
Sets up a KS_SELRF object for RF slice selection with preset gradient constraints
Definition: KSFoundation_host.c:3032
STATUS ks_eval_stretch_rf(KS_RF *rf, float stretch_factor)
In-place stretching of a KS_RF object
Definition: KSFoundation_host.c:3500

◆ ks_eval_transient_SPGR_FA_train_recursive()

void ks_eval_transient_SPGR_FA_train_recursive ( float *  FA_train,
float *  MZ_train,
int  N,
float  E1,
float  target_MT,
int  n 
)
3110  {
3111  float MT;
3112  n = (n < 0) ? (N-1) : n;
3113  if (n == 0) {
3114  MZ_train[n] = 1.0;
3115  FA_train[n] = asin(target_MT);
3116  } else {
3117  ks_eval_transient_SPGR_FA_train_recursive(FA_train, MZ_train, N, E1, target_MT, n-1);
3118  MT = MZ_train[n-1] * sin(FA_train[n-1]);
3119  MZ_train[n] = MZ_train[n-1] * cos(FA_train[n-1]) * E1 + 1.0 - E1;
3120  FA_train[n] = asin( MT / MZ_train[n] );
3121  }
3122 }
void ks_eval_transient_SPGR_FA_train_recursive(float *FA_train, float *MZ_train, int N, float E1, float target_MT, int n)
Definition: KSFoundation_host.c:3110

◆ ks_eval_transient_SPGR_FA_train_binary_search()

void ks_eval_transient_SPGR_FA_train_binary_search ( float *  FA_train,
float *  MZ_train,
int  N,
float  E1,
float  MTlo,
float  MThi,
float  total_FA 
)
3125  {
3126  float thresh = 1e-7;
3127  if ((MThi-MTlo) < thresh) {
3128  return ks_eval_transient_SPGR_FA_train_recursive(FA_train, MZ_train, N, E1, MTlo, -1);
3129  }
3130  float MT = (MTlo + MThi)/2.0;
3131  ks_eval_transient_SPGR_FA_train_recursive(FA_train, MZ_train, N, E1, MT, -1);
3132  if (ks_eval_check_FA_train(N, FA_train, MZ_train, total_FA) == FAILURE) {
3133  return ks_eval_transient_SPGR_FA_train_binary_search(FA_train, MZ_train, N, E1, MTlo, MT, total_FA); /* Less greed */
3134  } else {
3135  return ks_eval_transient_SPGR_FA_train_binary_search(FA_train, MZ_train, N, E1, MT, MThi, total_FA); /* More greed */
3136  }
3137 }
void ks_eval_transient_SPGR_FA_train_binary_search(float *FA_train, float *MZ_train, int N, float E1, float MTlo, float MThi, float total_FA)
Definition: KSFoundation_host.c:3125
STATUS ks_eval_check_FA_train(int N, float *FA_train, float *MZ_train, float total_FA)
Definition: KSFoundation_host.c:3140
void ks_eval_transient_SPGR_FA_train_recursive(float *FA_train, float *MZ_train, int N, float E1, float target_MT, int n)
Definition: KSFoundation_host.c:3110

◆ ks_eval_check_FA_train()

STATUS ks_eval_check_FA_train ( int  N,
float *  FA_train,
float *  MZ_train,
float  total_FA 
)
3140  {
3141  if (isnan(FA_train[N-1])) {return FAILURE;} /* infeasible solution */
3142  if (acos(MZ_train[N-1] * cos(FA_train[N-1])) > total_FA * PI/180.0) {return FAILURE;} /* total FA exceeded */
3143  int n;
3144  for (n=0; n<N; n++) {
3145  if ((n > 0) && (FA_train[n] < FA_train[n-1])) {return FAILURE;} /* decreasing FA */
3146  if ((n < N-1) && (FA_train[n] > 89.0 * PI/180.0)) {return FAILURE;} /* only last FA may be 90 degrees */
3147  }
3148  return SUCCESS;
3149 }

◆ ks_eval_transient_SPGR_FA_train()

STATUS ks_eval_transient_SPGR_FA_train ( float *  FA_train,
int  N,
float  TR,
float  T1,
float  total_FA 
)
3152  {
3153  if (N <= 0) {return ks_error("%s: N must be >0, but is %d", __FUNCTION__, N);}
3154  float E1 = exp(-TR/T1);
3155  float MTlo = 0.0;
3156  float MThi = 1.0;
3157  float MZ_train[N];
3158  ks_eval_transient_SPGR_FA_train_binary_search(FA_train, MZ_train, N, E1, MTlo, MThi, total_FA);
3159  if (ks_eval_check_FA_train(N, FA_train, MZ_train, total_FA) == FAILURE) {
3160  return FAILURE;
3161  } else {
3162  int n;
3163  for (n=0; n<N; n++) {FA_train[n]*=180.0/PI;} /* radians->degrees */
3164  return SUCCESS;
3165  }
3166 }
STATUS ks_error(const char *format,...) __attribute__((format(printf
Common error message function for HOST and TGT
void ks_eval_transient_SPGR_FA_train_binary_search(float *FA_train, float *MZ_train, int N, float E1, float MTlo, float MThi, float total_FA)
Definition: KSFoundation_host.c:3125
STATUS ks_eval_check_FA_train(int N, float *FA_train, float *MZ_train, float total_FA)
Definition: KSFoundation_host.c:3140

◆ ks_eval_sms_make_multiband()

STATUS ks_eval_sms_make_multiband ( KS_SELRF selrfMB,
const KS_SELRF selrf,
const int  sms_multiband_factor,
const int  sms_phase_modulation_mode,
const float  sms_slice_gap,
int  debug 
)

Creates a SMS (simultaneous-multi-slice) version of a KS_SELRF object

Parameters
[out]selrfMBPointer to multiband (SMS) KS_SELRF object
[in]selrfThe original (single-band) KS_SELRF object (set up using ks_eval_selrf())
[in]sms_multiband_factorThe multi-band acceleration factor
[in]sms_phase_modulation_modeSee ks_enum_smstype for options
[in]sms_slice_gapSlice gap in [mm] for the selrfMB pulse
[in]debugDebug flag for writing out text files (SIM: in current dir HW:/usr/g/mrraw/kstmp)
Return values
STATUSSUCCESS or FAILURE
3170  {
3171 /*
3172 %
3173 */
3174  int jdx, idx, kdx;
3175  float sms_phase_modulation[16] = KS_INITZEROS(16);
3176  STATUS status;
3177 
3178  /* Debug stuff */
3179  int single_band_mode = -1;
3180  char fname[KS_DESCRIPTION_LENGTH + 14];
3181  char outputdir_uid[250];
3182  FILE *asciiWaveReal;
3183  FILE *asciiWaveImag;
3184  FILE *asciiWaveRealMB;
3185  FILE *asciiWaveImagMB;
3186  FILE *asciiWaveRealMod;
3187  FILE *asciiWaveImagMod;
3188  FILE *asciiWaveParams;
3189 
3190  if (debug) {
3191 
3192 #ifdef SIM
3193  sprintf(outputdir_uid, "./");
3194 #else
3195  char cmd[250];
3196  sprintf(outputdir_uid, "/usr/g/mrraw/kstmp/%010d/", rhkacq_uid);
3197  sprintf(cmd, "mkdir -p %s > /dev/null", outputdir_uid);
3198  system(cmd);
3199 #endif
3200 
3201  sprintf(fname, "%s%s_mb_rf_Real.txt", outputdir_uid, selrfMB->rf.rfwave.description);
3202  asciiWaveReal = fopen(fname,"w");
3203  sprintf(fname, "%s%s_mb_rf_Imag.txt", outputdir_uid, selrfMB->rf.rfwave.description);
3204  asciiWaveImag = fopen(fname,"w");
3205  sprintf(fname, "%s%s_mb_rfMB_Real.txt", outputdir_uid, selrfMB->rf.rfwave.description);
3206  asciiWaveRealMB = fopen(fname,"w");
3207  sprintf(fname, "%s%s_mb_rfMB_Imag.txt", outputdir_uid, selrfMB->rf.rfwave.description);
3208  asciiWaveImagMB = fopen(fname,"w");
3209  sprintf(fname, "%s%s_mod_rfMB_Real.txt", outputdir_uid, selrfMB->rf.rfwave.description);
3210  asciiWaveRealMod = fopen(fname,"w");
3211  sprintf(fname, "%s%s_mod_rfMB_Imag.txt", outputdir_uid, selrfMB->rf.rfwave.description);
3212  asciiWaveImagMod = fopen(fname,"w");
3213  }
3214 
3215  /* Check if gradient is present */
3216  if (areSame(selrf->grad.amp, 0)){
3217  return ks_error("ks_eval_sms_make_multiband: Need a pre calculated gradient amp (2nd arg. selrf->grad.amp = %g)", selrf->grad.amp);
3218  }
3219 
3220  /* Duplicate the selRF structure */
3221  if (selrfMB != selrf ) {
3222  *selrfMB = *selrf;
3223  }
3224 
3225  /* Set info struct */
3226  selrfMB->sms_info.mb_factor = sms_multiband_factor;
3227  selrfMB->sms_info.slice_gap = sms_slice_gap;
3228  selrfMB->sms_info.pulse_type = KS_SELRF_SMS_MB;
3229 
3230  /* Save max b1 to make sure it does not get overwritten */
3231  double base_rf_max_amp = ks_waveform_absmax(selrf->rf.rfwave.waveform, ks_wave_res(&selrf->rf.rfwave));
3232 
3233  /* Get phase modulation to lower peak B1 */
3234  if (sms_phase_modulation_mode != KS_SELRF_SMS_PHAS_MOD_OFF) {
3235  status = ks_eval_sms_get_phase_modulation(sms_phase_modulation, sms_multiband_factor, sms_phase_modulation_mode);
3236  if (status != SUCCESS) return status;
3237  }
3238 
3239  /* initialize thetawave */
3240  if (selrfMB->rf.thetawave.res == 0) {
3241  ks_init_wave(&selrfMB->rf.thetawave);
3242  strncpy(selrfMB->rf.thetawave.description, selrfMB->rf.rfwave.description, KS_DESCRIPTION_LENGTH);
3243  selrfMB->rf.thetawave.res = selrfMB->rf.rfwave.res;
3244  selrfMB->rf.thetawave.duration = selrfMB->rf.rfwave.duration;
3245  }
3246 
3247  if (debug) {sprintf(fname, "%s%s_mb_rf_base.txt", outputdir_uid, selrfMB->rf.rfwave.description); ks_print_waveform(selrf->rf.rfwave.waveform, fname, selrf->rf.rfwave.res); }
3248 
3249  /* Figure out a new resolution, since many pulses are of low resolution */
3250  int newRes = selrf->rf.rfwave.duration/RF_UPDATE_TIME;
3251  int nPoints = newRes/selrf->rf.rfwave.res;
3252 
3253  /* Calc and apply sms modulation to base pulse */
3254  float *sms_wave_real = (float*)alloca(newRes*sizeof(float));
3255  float *sms_wave_imag = (float*)alloca(newRes*sizeof(float));
3256 
3257  kdx = 0;
3258  for (idx=0; idx < newRes; idx++) {
3259 
3260  double wave_real, wave_imag, sms_modulation_real = 0, sms_modulation_imag = 0, p, slice_offset;
3261 
3262  /* Convert base pulse from polar to cartesian */
3263  wave_real = selrf->rf.rfwave.waveform[kdx] * cos(selrf->rf.thetawave.waveform[kdx]*(PI/180.0));
3264  wave_imag = selrf->rf.rfwave.waveform[kdx] * sin(selrf->rf.thetawave.waveform[kdx]*(PI/180.0));
3265  if (debug) {
3266  fprintf(asciiWaveReal, "%d %f\n", idx, wave_real); fflush(asciiWaveReal);
3267  fprintf(asciiWaveImag, "%d %f\n", idx, wave_imag); fflush(asciiWaveImag);
3268  }
3269 
3270  if (idx % nPoints == 0 && idx > 0) {
3271  kdx++;
3272  }
3273 
3274  /* Calc sms modulation */
3275  double centeredIndex = idx - (newRes-1) * 0.5;
3276  for (jdx = 0; jdx < sms_multiband_factor; jdx++) {
3277 
3278  slice_offset = (sms_slice_gap / 10) * (1.0 + jdx - (sms_multiband_factor / 2.0) - 0.5);
3279  p = 2.0 * PI * GAM * slice_offset * selrf->grad.amp * centeredIndex * RF_UPDATE_TIME * 1e-6 + sms_phase_modulation[jdx];
3280 
3281  if (single_band_mode >= 0) {
3282  if (jdx == single_band_mode) {
3283  sms_modulation_real += cos(p);
3284  sms_modulation_imag += sin(p);
3285  }
3286  } else {
3287  sms_modulation_real += cos(p);
3288  sms_modulation_imag += sin(p);
3289  }
3290  }
3291 
3292  /* Apply sms modulation via complex multiplication */
3293  sms_wave_real[idx] = wave_real * sms_modulation_real - wave_imag * sms_modulation_imag;
3294  sms_wave_imag[idx] = wave_real * sms_modulation_imag + wave_imag * sms_modulation_real;
3295 
3296  if (debug) {
3297  fprintf(asciiWaveRealMB, "%d %f\n", idx, sms_wave_real[idx]); fflush(asciiWaveRealMB);
3298  fprintf(asciiWaveImagMB, "%d %f\n", idx, sms_wave_imag[idx]); fflush(asciiWaveImagMB);
3299  fprintf(asciiWaveRealMod, "%d %f\n", idx, sms_modulation_real); fflush(asciiWaveRealMod);
3300  fprintf(asciiWaveImagMod, "%d %f\n", idx, sms_modulation_imag); fflush(asciiWaveImagMod);
3301  }
3302 
3303  }
3304 
3305  if (debug) {
3306  fclose(asciiWaveReal);
3307  fclose(asciiWaveImag);
3308  fclose(asciiWaveRealMB);
3309  fclose(asciiWaveImagMB);
3310  /*ks_error("GAM=%g, PI=%g, grad.amp=%g, slice_gap=%g, dt=%d", GAM, PI, selrf->grad.amp, sms_slice_gap, RF_UPDATE_TIME);*/
3311  }
3312 
3313  kdx = 0;
3314  for (idx=0; idx < newRes; idx++) {
3315 
3316  if (sms_phase_modulation_mode == KS_SELRF_SMS_PHAS_MOD_OFF || sms_phase_modulation_mode == KS_SELRF_SMS_PHAS_MOD_AMPL) {
3317 
3318  /* Use real part and place waves in selrfMB */
3319  selrfMB->rf.rfwave.waveform[idx] = sms_wave_real[idx];
3320 
3321  } else {
3322 
3323  /* Convert from polar to cartesian and place waves in selrfMB */
3324  selrfMB->rf.rfwave.waveform[idx] = sqrt(pow(sms_wave_real[idx], 2.0) + pow(sms_wave_imag[idx], 2.0));
3325  selrfMB->rf.thetawave.waveform[idx] = atan2(sms_wave_imag[idx], sms_wave_real[idx]) * (180.0/PI);
3326 
3327  }
3328 
3329  if (idx % nPoints == 0 && idx > 0) {
3330  kdx++;
3331  }
3332  }
3333 
3334  /* Update resolution fields */
3335  selrfMB->rf.rfwave.res = newRes;
3336  if (sms_phase_modulation_mode == KS_SELRF_SMS_PHAS_MOD_OFF || sms_phase_modulation_mode == KS_SELRF_SMS_PHAS_MOD_AMPL) {
3337  selrfMB->rf.thetawave.res = 0;
3338  selrfMB->rf.thetawave.duration = 0;
3339  } else {
3340  selrfMB->rf.thetawave.res = newRes;
3341  }
3342 
3343  if (debug) {sprintf(fname, "%s%s_mb_rf.txt", outputdir_uid, selrfMB->rf.rfwave.description); ks_print_waveform(selrfMB->rf.rfwave.waveform, fname, selrfMB->rf.rfwave.res); }
3344  if (debug) {sprintf(fname, "%s%s_mb_theta.txt", outputdir_uid, selrfMB->rf.rfwave.description); ks_print_waveform(selrfMB->rf.thetawave.waveform, fname, selrfMB->rf.thetawave.res); }
3345 
3346  /* Modify rfpulse structure */
3347  double standard_pw = 1e-3;
3348  double b1_scale_factor = ks_waveform_absmax(selrfMB->rf.rfwave.waveform, newRes) / base_rf_max_amp;
3349  selrfMB->rf.rfpulse.max_b1 *= b1_scale_factor;
3350  selrfMB->rf.rfpulse.area /= b1_scale_factor;
3351  selrfMB->rf.rfpulse.effwidth /= b1_scale_factor;
3352  selrfMB->rf.rfpulse.max_int_b1_sq = selrfMB->rf.rfpulse.max_b1 * selrfMB->rf.rfpulse.max_b1 * selrfMB->rf.rfpulse.effwidth * (selrfMB->rf.rfwave.duration) * 1e-6 / standard_pw;
3353  selrfMB->rf.rfpulse.max_rms_b1 = sqrt(selrfMB->rf.rfpulse.max_int_b1_sq / ((selrfMB->rf.rfwave.duration) * 1e-6) * standard_pw);
3354 
3355 
3356  /* Normalize to 1 */
3357  ks_wave_multiplyval(&selrfMB->rf.rfwave, 1.0 / ks_wave_absmax(&selrfMB->rf.rfwave));
3358 
3359  /* register the RF for later heat calcs and RF scaling */
3360  char description[KS_DESCRIPTION_LENGTH + 4];
3361  sprintf(description, "%s_sms", selrfMB->rf.rfwave.description);
3362  status = ks_eval_rf(&selrfMB->rf, description);
3363  if (status != SUCCESS) return status;
3364 
3365  if (debug) {
3366  sprintf(fname, "%s%s_mb_params.txt", outputdir_uid, selrfMB->rf.rfwave.description);
3367  asciiWaveParams = fopen(fname,"w");
3368  fprintf(asciiWaveParams, "%f\n", (float)selrfMB->rf.flip);
3369  fprintf(asciiWaveParams, "%f\n", (float)selrfMB->grad.amp);
3370  fprintf(asciiWaveParams, "%f\n", (float)selrfMB->rf.rfpulse.max_b1);
3371  fprintf(asciiWaveParams, "%f\n", (float)selrfMB->slthick);
3372  fprintf(asciiWaveParams, "%f\n", (float)sms_slice_gap);
3373  fprintf(asciiWaveParams, "%f\n", (float)sms_multiband_factor);
3374  fprintf(asciiWaveParams, "%f\n", (float)b1_scale_factor);
3375  fflush(asciiWaveParams); fclose(asciiWaveParams);
3376  }
3377 
3378  return SUCCESS;
3379 
3380 } /* EOF */
KS_TRAP grad
Definition: KSFoundation.h:1491
#define areSame(a, b)
Definition: KSFoundation.h:119
int debug
Definition: GERequired.e:2642
KS_SMS_INFO sms_info
Definition: KSFoundation.h:1494
RF_PULSE rfpulse
Definition: KSFoundation.h:948
STATUS ks_error(const char *format,...) __attribute__((format(printf
Common error message function for HOST and TGT
#define KS_INITZEROS(n)
Definition: KSFoundation.h:199
float slice_gap
Definition: KSFoundation.h:1369
STATUS ks_eval_sms_get_phase_modulation(float *sms_phase_modulation, const int sms_multiband_factor, const int sms_phase_modulation_mode)
Sets up an array with values for phase modulation between slices in a multi-band (MB) RF...
Definition: KSFoundation_host.c:3383
float GAM
float flip
Definition: KSFoundation.h:940
float ks_wave_absmax(const KS_WAVE *wave)
Returns the maximum absolute value in a KS_WAVE sequence object
Definition: KSFoundation_common.c:1224
#define KS_DESCRIPTION_LENGTH
Definition: KSFoundation.h:185
KS_WAVE thetawave
Definition: KSFoundation.h:951
int rhkacq_uid
KS_RF rf
Definition: KSFoundation.h:1485
KS_DESCRIPTION description
Definition: KSFoundation.h:666
KS_WAVE rfwave
Definition: KSFoundation.h:949
Definition: KSFoundation.h:1970
float amp
Definition: KSFoundation.h:581
int mb_factor
Definition: KSFoundation.h:1368
void ks_init_wave(KS_WAVE *wave)
Resets a KS_WAVE sequence object to its default value (KS_INIT_WAVE)
Definition: KSFoundation_host.c:66
void ks_print_waveform(const KS_WAVEFORM waveform, const char *filename, int res)
Writes a KS_WAVEFORM to disk with the specified filename
Definition: KSFoundation_host.c:5135
STATUS ks_eval_rf(KS_RF *rf, const char *const desc)
Sets up a KS_RF object
Definition: KSFoundation_host.c:2707
int ks_wave_res(const KS_WAVE *wave)
Returns the number of samples in a KS_WAVE sequence object
Definition: KSFoundation_common.c:1107
Definition: KSFoundation.h:1968
int pulse_type
Definition: KSFoundation.h:1370
float ks_waveform_absmax(const KS_WAVEFORM waveform, int res)
Returns the maximum absolute value in a KS_WAVEFORM
Definition: KSFoundation_common.c:1203
Definition: KSFoundation.h:1961
int res
Definition: KSFoundation.h:667
void ks_wave_multiplyval(KS_WAVE *wave, float val)
In-place scalar multiplication of a KS_WAVE
Definition: KSFoundation_common.c:1398
KS_WAVEFORM waveform
Definition: KSFoundation.h:669
int duration
Definition: KSFoundation.h:668
float slthick
Definition: KSFoundation.h:1487

◆ ks_eval_sms_get_phase_modulation()

STATUS ks_eval_sms_get_phase_modulation ( float *  sms_phase_modulation,
const int  sms_multiband_factor,
const int  sms_phase_modulation_mode 
)

Sets up an array with values for phase modulation between slices in a multi-band (MB) RF

Parameters
[out]sms_phase_modulationArray with phase modulation between slices in a MB RF
[in]sms_multiband_factorThe multi-band acceleration factor
[in]sms_phase_modulation_modeSee ks_enum_smstype for options
Return values
STATUSSUCCESS or FAILURE
3383  {
3384  /*
3385  % Return optimal phases for phase-modulated or amplitude-modulated low-peak power multiband pulses.
3386  %
3387  % sms_phase_modulation: Optimal phases
3388  % sms_multiband_factor: Number of bands
3389  % sms_phase_modulation_mode: phasmod, amplmod, or quadmod
3390  %
3391  % Adopted for EPIC from Will Grissom's (Vanderbilt University, 2015) MATLAB code by Ola Norbeck (Karolinska Unviversity Hospital, 2015).
3392  */
3393 
3394  int jdx;
3395 
3396  if (sms_phase_modulation_mode == KS_SELRF_SMS_PHAS_MOD_PHAS) {
3397 
3398  /* Wong's sms_phase_modulation: From E C Wong, ISMRM 2012, p. 2209 */
3399 
3400  if ((sms_multiband_factor < 2) || (sms_multiband_factor > 16)) {
3401  return ks_error("ks_eval_sms_get_phase_modulation: multiband factor (%d, 2nd arg) must be in range [2,16] for Wong's sms_phase_modulation", sms_multiband_factor);
3402  }
3403 
3404  float P[15][16] = {
3405  /* mbf = 2 */ {0.0, 0.0}, /* alt.{0.0, 3.142} */
3406  /* mbf = 3 */ {0.0, 0.730, 4.602},
3407  /* mbf = 4 */ {0.0, 3.875, 5.940, 6.197},
3408  /* mbf = 5 */ {0.0, 3.778, 5.335, 0.872, 0.471},
3409  /* mbf = 6 */ {0.0, 2.005, 1.674, 5.012, 5.736, 4.123},
3410  /* mbf = 7 */ {0.0, 3.002, 5.998, 5.909, 2.624, 2.528, 2.440},
3411  /* mbf = 8 */ {0.0, 1.036, 3.414, 3.778, 3.215, 1.756, 4.555, 2.467},
3412  /* mbf = 9 */ {0.0, 1.250, 1.783, 3.558, 0.739, 3.319, 1.296, 0.521, 5.332},
3413  /* mbf = 10 */ {0.0, 4.418, 2.360, 0.677, 2.253, 3.472, 3.040, 3.974, 1.192, 2.510},
3414  /* mbf = 11 */ {0.0, 5.041, 4.285, 3.001, 5.765, 4.295, 0.056, 4.213, 6.040, 1.078, 2.759},
3415  /* mbf = 12 */ {0.0, 2.755, 5.491, 4.447, 0.231, 2.499, 3.539, 2.931, 2.759, 5.376, 4.554, 3.479},
3416  /* mbf = 13 */ {0.0, 0.603, 0.009, 4.179, 4.361, 4.837, 0.816, 5.995, 4.150, 0.417, 1.520, 4.517, 1.729},
3417  /* mbf = 14 */ {0.0, 3.997, 0.830, 5.712, 3.838, 0.084, 1.685, 5.328, 0.237, 0.506, 1.356, 4.025, 4.483, 4.084},
3418  /* mbf = 15 */ {0.0, 4.126, 2.266, 0.957, 4.603, 0.815, 3.475, 0.977, 1.449, 1.192, 0.148, 0.939, 2.531, 3.612, 4.801},
3419  /* mbf = 16 */ {0.0, 4.359, 3.510, 4.410, 1.750, 3.357, 2.061, 5.948, 3.000, 2.822, 0.627, 2.768, 3.875, 4.173, 4.224, 5.941}
3420  };
3421 
3422  for (jdx = 0; jdx < sms_multiband_factor; jdx++ ) {
3423  sms_phase_modulation[jdx] = P[sms_multiband_factor - 2][jdx];
3424  }
3425 
3426  } else if (sms_phase_modulation_mode == KS_SELRF_SMS_PHAS_MOD_AMPL) {
3427 
3428  /* Seada's Hermitian sms_phase_modulation: From Seada et. al. Optimized Amplitude Modulated Multiband RF Pulse Design. MRM 2017 */
3429 
3430  if ((sms_multiband_factor < 3) || (sms_multiband_factor > 12)) {
3431  return ks_error("ks_eval_sms_get_phase_modulation: multiband factor (%d, 2nd arg) must be in range [3,12] for Seada's Hermitian sms_phase_modulation", sms_multiband_factor);
3432  }
3433 
3434  float P[10][12] = {
3435  /* mbf = 3 */ {1.2846, 0.0000, -1.2846},
3436  /* mbf = 4 */ {0.9739, 1.3718, -1.3718, -0.9739},
3437  /* mbf = 5 */ {1.1572, -0.9931, 0.0000, 0.9931, -1.1572},
3438  /* mbf = 6 */ {1.6912, 2.8117, 1.1572, -1.1572, -2.8117, -1.6912},
3439  /* mbf = 7 */ {2.5813, -0.5620, 0.1030, 0.0000, -0.1030, 0.5620, -2.5813},
3440  /* mbf = 8 */ {2.1118, 0.2199, 1.4643, 1.9914, -1.9914, -1.4643, -0.2199, -2.1118},
3441  /* mbf = 9 */ {0.4800, -2.6669, -0.6458, -0.4189, 0.0000, 0.4189, 0.6458, 2.6669, -0.4800},
3442  /* mbf = 10 */ {1.6825, -2.3946, 2.9130, 0.3037, 0.7365, -0.7365, -0.3037, -2.9130, 2.3946, -1.6825},
3443  /* mbf = 11 */ {1.4050, 0.8866, -1.8535, 0.0698, -1.4940, 0.0000, 1.4940, -0.0698, 1.8535, -0.8866, -1.4050},
3444  /* mbf = 12 */ {1.7296, 0.4433, 0.7208, 2.1904, -2.1956, 0.9844, -0.9844, 2.1956, -2.1904, -0.7208, -0.4433, -1.7296}
3445  };
3446 
3447  for (jdx = 0; jdx < sms_multiband_factor; jdx++ ) {
3448  sms_phase_modulation[jdx] = P[sms_multiband_factor - 3][jdx];
3449  }
3450 
3451 
3452  } else if (sms_phase_modulation_mode == KS_SELRF_SMS_PHAS_MOD_QUAD) {
3453 
3454  /* Grissom's quadratic sms_phase_modulation (unpublished) */
3455 
3456  for (jdx = 0; jdx < sms_multiband_factor; jdx++) {
3457  sms_phase_modulation[jdx] = pow( (3.4 / sms_multiband_factor) * (float)(1.0 + (float)jdx - ((float)sms_multiband_factor / 2.0) - 0.5), 2.0); /* sms_phase_modulation for each band */
3458  }
3459 
3460  } else {
3461 
3462  return ks_error("ks_eval_sms_get_phase_modulation: sms_phase_modulation_mode (%d, 3rd arg) is of unrecognized type. Choose phasmod = 1, amplmod = 2, or quadmod = 3", sms_phase_modulation_mode);
3463 
3464  }; /* end of if */
3465 
3466  return SUCCESS;
3467 
3468 } /* EOF */
Definition: KSFoundation.h:1969
STATUS ks_error(const char *format,...) __attribute__((format(printf
Common error message function for HOST and TGT
Definition: KSFoundation.h:1970
Definition: KSFoundation.h:1971

◆ ks_eval_sms_calc_slice_gap()

float ks_eval_sms_calc_slice_gap ( int  sms_multiband_factor,
const SCAN_INFO *  org_slice_positions,
int  nslices,
float  slthick,
float  slspace 
)

Calculates the resulting slice gap in [mm] for SMS RF

Parameters
[in]sms_multiband_factorThe multi-band acceleration factor
[in]org_slice_positionsSCAN_INFO struct holding the original slice information
[in]nslicesPrescribed number of slices
[in]slthickPrescribed slice thickness
[in]slspacePrescribed slice spacing
Return values
slicegapSMS slice gap in [mm]
3470  {
3471 
3472  float gap;
3473 
3474  if (nslices % sms_multiband_factor) {
3475  ks_error("ks_eval_sms_calc_slice_gap: Number of slices (%d, 3rd arg) must be divisible with the MB factor (%d, 1st arg)", nslices, sms_multiband_factor);
3476  }
3477 
3478  /* gap between MB slices mm */
3479  /*gap = org_slice_positions[nslices/sms_multiband_factor].optloc - org_slice_positions[0].optloc;*/
3480  gap = (float)((nslices + sms_multiband_factor - 1) / sms_multiband_factor) * (float)(slthick + slspace);
3481 
3482  return gap;
3483 
3484 } /* EOF */
STATUS ks_error(const char *format,...) __attribute__((format(printf
Common error message function for HOST and TGT

◆ ks_eval_sms_calc_caipi_area()

float ks_eval_sms_calc_caipi_area ( int  caipi_fov_shift,
float  sms_slice_gap 
)

Calculates the CAIPI blip area

Parameters
[in]caipi_fov_shiftThe FOV shift in units of faction of FOV (e.g. 2 means shift of FOV/2)
[in]sms_slice_gapSlice gap in [mm] for the selrfMB pulse
Return values
caipi_blip_areaThe area of the CAIPIRINHA blip in [(G/cm) * us]
3487  {
3488 
3489  if (sms_slice_gap <= 0) {
3490  return 0.0;
3491  }
3492 
3493  /* Calculate CAIPI-blip area in us*G/cm */
3494  return ((caipi_fov_shift-1)*(float)PI/(float)caipi_fov_shift) / (2.0*PI*GAM*1e-6 * (sms_slice_gap/10.0));
3495 
3496 } /* EOF */
float GAM

◆ ks_eval_stretch_rf()

STATUS ks_eval_stretch_rf ( KS_RF rf,
float  stretch_factor 
)

In-place stretching of a KS_RF object

This function takes a KS_RF object already set up, having some resolution (rfwave.res), bandwidth (.bw), duration (rfwave.duration) and waveform (.rfwave.waveform) and performs a linear interpolation of the waveform contained in .rfwave.waveform so its new duration becomes

newDuration = oldDuration * stretch_factor

  • Note that regardless of the input resolution of the waveform, the output resolution will always be newDuration/2.
  • The description of the stretched KS_RF object (.rfwave.description) is updated with a suffix stretched
  • The fields .start2iso, .iso2end and .bw are automatically updated to reflect the new duration
Parameters
[in,out]rfPointer to the KS_RF object to be stretched
[in]stretch_factorStretch factor (float) that widens/shortens the RF pulse if larger/smaller than 1.0. If negative, the RF waveform will be mirrored (in time)
Return values
STATUSSUCCESS or FAILURE
3500  {
3501 
3502  STATUS status;
3503  int idx;
3504  int debug = 0;
3505  int mirror_rfpulse = 0;
3506 
3507  if (areSame(stretch_factor, 1.0)) {
3508  return SUCCESS;
3509  }
3510 
3511  mirror_rfpulse = (stretch_factor < 0); /* we are going to mirror the RF pulse after stretching */
3512  stretch_factor = fabsf(stretch_factor);
3513 
3514  if (stretch_factor < FLT_EPSILON) {
3515  return ks_error("%s(%s): can not stretch by %f", __FUNCTION__, rf->rfwave.description, stretch_factor);
3516  }
3517 
3518  /* get original duration/resolution and calculate new duration/resolution */
3519  int orgDur = rf->rfwave.duration;
3520  int newDur = RUP_GRD((int)(orgDur * stretch_factor));
3521  int orgRes = rf->rfwave.res;
3522  int newRes = newDur/RF_UPDATE_TIME;
3523 
3524  if (newRes > KS_MAXWAVELEN) {
3525  return ks_error("%s(%s): stretch => too high res (%d)", __FUNCTION__, rf->rfwave.description, newRes);
3526  }
3527 
3528  /* create index for interpolation */
3529  float *orgGrid = (float*)alloca(orgRes * sizeof(float));
3530  float *newGrid = (float*)alloca(newRes * sizeof(float));
3531  for (idx = 0; idx < orgRes; idx++) {
3532  orgGrid[idx] = (float) idx / (orgRes-1);
3533  }
3534  for (idx = 0; idx < newRes; idx++) {
3535  newGrid[idx] = (float) idx / (newRes-1);
3536  }
3537 
3538  if (debug == 1) {ks_print_waveform(rf->rfwave.waveform, "stretch_orgrf.txt", orgRes); }
3539 
3540  /* perform linear interpolation */
3541  ks_eval_linear_interp1(orgGrid, orgRes, rf->rfwave.waveform, newGrid, newRes, rf->rfwave.waveform);
3542 
3543  if (debug == 1) {ks_print_waveform(rf->rfwave.waveform, "stretch_newrf.txt", newRes); }
3544 
3545  /* set new duration, resolution and bandwidth */
3546  rf->rfwave.duration = newDur;
3547  rf->rfwave.res = newRes;
3548  rf->bw *= ((float)orgDur/newDur);
3549  rf->rfpulse.isodelay = RUP_FACTOR((int)(rf->rfpulse.isodelay * stretch_factor),RF_UPDATE_TIME);
3550  rf->iso2end = rf->rfpulse.isodelay;
3551  rf->start2iso = rf->rfwave.duration - rf->iso2end;
3552 
3553  if (mirror_rfpulse) {
3554  rf->iso2end = rf->start2iso; /* N.B.: start2iso will be updated again in ks_eval_rf() */
3555  rf->rfpulse.isodelay = rf->start2iso;
3556  ks_eval_mirrorwave(&rf->rfwave);
3559  }
3560 
3561  char description[KS_DESCRIPTION_LENGTH];
3562  sprintf(description, "%s_stretched", rf->rfwave.description);
3563  status = ks_eval_rf(rf, description);
3564  if (status != SUCCESS) return status;
3565 
3566  status = ks_eval_rfstat(rf);
3567  if (status != SUCCESS) return status;
3568 
3569  return SUCCESS;
3570 }
int start2iso
Definition: KSFoundation.h:944
#define areSame(a, b)
Definition: KSFoundation.h:119
int debug
Definition: GERequired.e:2642
RF_PULSE rfpulse
Definition: KSFoundation.h:948
STATUS ks_error(const char *format,...) __attribute__((format(printf
Common error message function for HOST and TGT
void ks_eval_linear_interp1(const float *x, int x_length, const float *y, const float *xx, int xx_length, float *yy)
Linear interpolation
Definition: KSFoundation_host.c:3844
#define KS_DESCRIPTION_LENGTH
Definition: KSFoundation.h:185
KS_WAVE thetawave
Definition: KSFoundation.h:951
KS_DESCRIPTION description
Definition: KSFoundation.h:666
STATUS ks_eval_rfstat(KS_RF *rf)
Sets up the RF_PULSE structure of a KS_RF object
Definition: KSFoundation_host.c:2615
KS_WAVE rfwave
Definition: KSFoundation.h:949
STATUS ks_eval_mirrorwave(KS_WAVE *wave)
Flips the contents of the KS_WAVEFORM in a KS_WAVE object
Definition: KSFoundation_host.c:2206
int iso2end
Definition: KSFoundation.h:945
void ks_print_waveform(const KS_WAVEFORM waveform, const char *filename, int res)
Writes a KS_WAVEFORM to disk with the specified filename
Definition: KSFoundation_host.c:5135
STATUS ks_eval_rf(KS_RF *rf, const char *const desc)
Sets up a KS_RF object
Definition: KSFoundation_host.c:2707
KS_WAVE omegawave
Definition: KSFoundation.h:950
int res
Definition: KSFoundation.h:667
KS_WAVEFORM waveform
Definition: KSFoundation.h:669
int duration
Definition: KSFoundation.h:668
#define KS_MAXWAVELEN
Definition: KSFoundation.h:186
float bw
Definition: KSFoundation.h:941

◆ ks_eval_sms_make_pins()

STATUS ks_eval_sms_make_pins ( KS_SELRF selrfPINS,
const KS_SELRF selrf,
float  sms_slice_gap 
)

Creates a PINS RF pulse (KS_SELRF) based on a KS_SELRF object

Parameters
[out]selrfPINSPINS RF pulse (KS_SELRF object)
[in]selrfThe original (single-band) KS_SELRF object (set up using ks_eval_selrf())
[in]sms_slice_gapSlice gap in [mm] for the selrfMB pulse
Return values
STATUSSUCCESS or FAILURE
3576  {
3577 
3578  STATUS status;
3579  int jdx, idx, kdx = 0, gdx = 0, debug = 0;
3580  int orgDur = selrfPINS->rf.rfwave.duration;
3581  KS_TRAP gzblip = KS_INIT_TRAP;
3582 
3583  /* Duplicate the selRF structure */
3584  if (selrfPINS != selrf ) {
3585  *selrfPINS = *selrf;
3586  }
3587 
3588  /* Set info struct */
3589  selrfPINS->sms_info.slice_gap = sms_slice_gap;
3590  selrfPINS->sms_info.pulse_type = KS_SELRF_SMS_PINS;
3591 
3592  ks_init_trap(&selrfPINS->grad);
3593 
3594  if (debug == 1) {ks_print_waveform(selrf->rf.rfwave.waveform, "pins_rfMB_base.txt", selrf->rf.rfwave.res); }
3595 
3596  double kzw = (selrf->rf.bw * selrf->rf.rfwave.duration * 1e-6) / (selrf->slthick / 10); /* 1/cm, width in kz-space we must go */
3597  int NPulses = (floor(ceil(kzw / (1 / (sms_slice_gap / 10))) / 2) * 2) + 1; /* number of subpulses (odd) */
3598 
3599  /* Init waves */
3600  KS_WAVEFORM rfLowRes = KS_INIT_WAVEFORM;
3601  KS_WAVE gzBlipWave = KS_INIT_WAVE;
3602  float *lowResGrid = (float*)alloca(NPulses * sizeof(float));
3603  float *baseGrid = (float*)alloca(selrf->rf.rfwave.res * sizeof(float));
3604 
3605  /* create index for interpolation */
3606  for (idx = 0; idx < selrf->rf.rfwave.res; idx++) {
3607  baseGrid[idx] = (float) idx / (selrf->rf.rfwave.res - 1);
3608  }
3609  for (idx = 0; idx < NPulses; idx++) {
3610  lowResGrid[idx] = (float) idx / (NPulses - 1);
3611  }
3612 
3613  /* Interpolate the base rf to get PINS sub pulse amplitudes and high res for MB pulse */
3614  ks_eval_linear_interp1(baseGrid, selrf->rf.rfwave.res, selrf->rf.rfwave.waveform, lowResGrid, NPulses, rfLowRes);
3615 
3616  if (debug == 1) {ks_print_waveform(rfLowRes, "pins_rfLowRes.txt", NPulses); }
3617 
3618  /* Create blip */
3619  double gArea = 1 / (sms_slice_gap / 10) / (GAM); /* (g sec)/cm, k-area of each blip */
3620  gzblip.area = gArea * 1e6; /* (G/cm)*us */
3621  status = ks_eval_trap1(&gzblip, "PINSblip"); if (status != SUCCESS) return status;
3622  status = ks_eval_trap2wave(&gzBlipWave, &gzblip); if (status != SUCCESS) return status;
3623  ks_init_trap(&gzblip);
3624 
3625  /* Matched-duration RF subpulses */
3626  float maxB1 = 0.24; /* temp. */
3627  float dt = GRAD_UPDATE_TIME / 1000000.0; /* sec */
3628  float pulseArea = (float) selrf->rf.flip * (PI / 180.0); /* radians or Hz (depending on how you look at it) */
3629  float subArea = (ks_waveform_absmax(rfLowRes, NPulses) * pulseArea) / (ks_waveform_sum(rfLowRes, NPulses) * 2.0 * PI * GAM); /* Gauss * sec */
3630  int hpw = ceil(subArea / (maxB1 * dt)); /* number of time points in sub pulse */
3631  float scaleFactor = 1.0;
3632 
3633  /* kludge :( */
3634  for (idx = 0; idx < NPulses; idx++) {
3635  if (areSame(rfLowRes[idx], 0)) {
3636  rfLowRes[idx] = rfLowRes[idx] + 0.01;
3637  }
3638  }
3639 
3640  /* Loop to concatenate the sub pulses and gradient blips */
3641  for (idx = 0; idx < NPulses; idx++) {
3642  for (jdx = 0; jdx < hpw; jdx++) {
3643  selrfPINS->rf.rfwave.waveform[kdx] = rfLowRes[idx] / scaleFactor;
3644  selrfPINS->gradwave.waveform[kdx] = 0.0;
3645  kdx++;
3646  }
3647  if (idx != NPulses - 1) {
3648  for (jdx = 0; jdx < gzBlipWave.res; jdx++) {
3649  selrfPINS->rf.rfwave.waveform[kdx] = 0.0;
3650  selrfPINS->gradwave.waveform[kdx] = gzBlipWave.waveform[jdx];
3651  kdx++; gdx++;
3652  }
3653  }
3654  }
3655 
3656  /* Set duration and resolution */
3658  selrfPINS->gradwave.res = RUP_FACTOR(kdx, GRAD_UPDATE_TIME);
3659  selrfPINS->gradwave.duration = selrfPINS->gradwave.res * GRAD_UPDATE_TIME;
3660  selrfPINS->rf.rfwave.res = selrfPINS->gradwave.res;
3661  selrfPINS->rf.rfwave.duration = selrfPINS->gradwave.duration;
3662  selrfPINS->rf.rfpulse.isodelay = RUP_FACTOR(ceil((float)selrfPINS->rf.rfpulse.isodelay * ((float)selrfPINS->rf.rfwave.duration/(float)orgDur)), GRAD_UPDATE_TIME);
3663  selrfPINS->rf.iso2end = selrfPINS->rf.rfpulse.isodelay;
3664  selrfPINS->rf.bw = 1e3 * (NPulses+1) * selrf->slthick / sms_slice_gap;
3665 
3666  /* If the resolution has been rounded up, place zeros at the end */
3667  for (idx = kdx; idx < selrfPINS->rf.rfwave.res; idx++) {
3668  selrfPINS->rf.rfwave.waveform[idx] = 0.0;
3669  selrfPINS->gradwave.waveform[idx] = 0.0;
3670  }
3671 
3672  if (debug == 1) {ks_print_waveform(selrfPINS->rf.rfwave.waveform, "pins_RF.txt", selrfPINS->rf.rfwave.res); }
3673  if (debug == 1) {ks_print_waveform(selrfPINS->gradwave.waveform, "pins_GRAD.txt", selrfPINS->gradwave.res); }
3674 
3675  /* Normalize to 1 */
3676  ks_waveform_multiplyval(selrfPINS->rf.rfwave.waveform, 1.0 / ks_waveform_absmax(selrfPINS->rf.rfwave.waveform, selrfPINS->rf.rfwave.res), selrfPINS->rf.rfwave.res);
3677 
3678  /* rfStat */
3679  status = ks_eval_rfstat(&selrfPINS->rf); if (status != SUCCESS) return status;
3680 
3681  /* init theta wave */
3682  ks_init_wave(&selrfPINS->rf.thetawave);
3683  selrfPINS->rf.thetawave.duration = selrfPINS->gradwave.duration;
3684  selrfPINS->rf.thetawave.res = selrfPINS->gradwave.res;
3685  if (debug == 1) {ks_print_waveform(selrfPINS->rf.thetawave.waveform, "pins_TH.txt", selrfPINS->rf.thetawave.res); }
3686 
3687  /* Register the RF */
3688  char description[KS_DESCRIPTION_LENGTH + 4];
3689  sprintf(description, "%s_PINS", selrfPINS->rf.rfwave.description);
3690  sprintf(selrfPINS->gradwave.description, "%s_PINS", selrfPINS->rf.rfwave.description);
3691  if (debug == 1) {fprintf(stderr, "---------- PINS %s rfstat ---------\n", selrfPINS->rf.rfwave.description);}
3692  if (debug == 1) {ks_print_rfpulse(selrfPINS->rf.rfpulse, stderr);}
3693  return ks_eval_rf(&selrfPINS->rf, description);
3694 
3695 }
Definition: KSFoundation.h:1975
KS_TRAP grad
Definition: KSFoundation.h:1491
STATUS ks_eval_trap1(KS_TRAP *trap, const char *const desc)
Sets up a trapezoid using a KS_TRAP sequence object with preset gradient constraints
Definition: KSFoundation_host.c:446
Core sequence object for making trapezoids on X,Y,Z, and OMEGA boards
Definition: KSFoundation.h:578
#define areSame(a, b)
Definition: KSFoundation.h:119
Core sequence object making arbitrary waveforms on any board (using float data format)
Definition: KSFoundation.h:664
float ks_waveform_sum(const KS_WAVEFORM waveform, int res)
Returns the sum of a KS_WAVEFORM
Definition: KSFoundation_common.c:1293
int debug
Definition: GERequired.e:2642
KS_SMS_INFO sms_info
Definition: KSFoundation.h:1494
#define KS_INIT_WAVE
Definition: KSFoundation.h:227
KS_WAVE gradwave
Definition: KSFoundation.h:1493
RF_PULSE rfpulse
Definition: KSFoundation.h:948
void ks_init_trap(KS_TRAP *trap)
Resets a KS_TRAP sequence object to its default value (KS_INIT_TRAP)
Definition: KSFoundation_host.c:58
int gradwave_units
Definition: KSFoundation.h:671
float slice_gap
Definition: KSFoundation.h:1369
void ks_eval_linear_interp1(const float *x, int x_length, const float *y, const float *xx, int xx_length, float *yy)
Linear interpolation
Definition: KSFoundation_host.c:3844
STATUS ks_eval_trap2wave(KS_WAVE *wave, const KS_TRAP *trap)
Converts a trapezoid object (KS_TRAP) to a wave object (KS_WAVE)
Definition: KSFoundation_host.c:3880
float GAM
float flip
Definition: KSFoundation.h:940
#define KS_INIT_WAVEFORM
Definition: KSFoundation.h:226
#define KS_DESCRIPTION_LENGTH
Definition: KSFoundation.h:185
#define KS_INIT_TRAP
Definition: KSFoundation.h:212
KS_WAVE thetawave
Definition: KSFoundation.h:951
KS_RF rf
Definition: KSFoundation.h:1485
KS_DESCRIPTION description
Definition: KSFoundation.h:666
STATUS ks_eval_rfstat(KS_RF *rf)
Sets up the RF_PULSE structure of a KS_RF object
Definition: KSFoundation_host.c:2615
KS_WAVE rfwave
Definition: KSFoundation.h:949
float KS_WAVEFORM[KS_MAXWAVELEN]
Definition: KSFoundation.h:262
float area
Definition: KSFoundation.h:582
void ks_init_wave(KS_WAVE *wave)
Resets a KS_WAVE sequence object to its default value (KS_INIT_WAVE)
Definition: KSFoundation_host.c:66
int iso2end
Definition: KSFoundation.h:945
void ks_print_waveform(const KS_WAVEFORM waveform, const char *filename, int res)
Writes a KS_WAVEFORM to disk with the specified filename
Definition: KSFoundation_host.c:5135
STATUS ks_eval_rf(KS_RF *rf, const char *const desc)
Sets up a KS_RF object
Definition: KSFoundation_host.c:2707
void ks_waveform_multiplyval(KS_WAVEFORM waveform, float val, int res)
In-place scalar multiplication of a KS_WAVEFORM
Definition: KSFoundation_common.c:1390
float maxB1[MAX_ENTRY_POINTS]
Definition: GERequired.e:269
int pulse_type
Definition: KSFoundation.h:1370
void ks_print_rfpulse(RF_PULSE rfpulse, FILE *fp)
Writes out the contents of an RF_PULSE struct for debugging
Definition: KSFoundation_host.c:5246
float ks_waveform_absmax(const KS_WAVEFORM waveform, int res)
Returns the maximum absolute value in a KS_WAVEFORM
Definition: KSFoundation_common.c:1203
int res
Definition: KSFoundation.h:667
KS_WAVEFORM waveform
Definition: KSFoundation.h:669
int duration
Definition: KSFoundation.h:668
float slthick
Definition: KSFoundation.h:1487
float bw
Definition: KSFoundation.h:941
Definition: KSFoundation.h:1962

◆ ks_eval_sms_make_pins_dante()

STATUS ks_eval_sms_make_pins_dante ( KS_SELRF selrfPINS,
const KS_SELRF selrf,
float  sms_slice_gap 
)
3698  {
3699  STATUS status;
3700  int jdx, idx, kdx = 0, gdx = 0, debug = 1;
3701  int orgDur = selrfPINS->rf.rfwave.duration;
3702  KS_TRAP gzblip = KS_INIT_TRAP;
3703 
3704  /* Duplicate the selRF structure */
3705  if (selrfPINS != selrf ) {
3706  *selrfPINS = *selrf;
3707  }
3708 
3709  /* Set info struct */
3710  selrfPINS->sms_info.slice_gap = sms_slice_gap;
3712 
3713  if (debug == 1) {ks_print_waveform(selrf->rf.rfwave.waveform, "dpins_rfMB_base.txt", selrf->rf.rfwave.res); }
3714  if (debug == 1) {ks_print_waveform(selrf->rf.thetawave.waveform, "dpins_phMB_base.txt", selrf->rf.thetawave.res); }
3715 
3716  /* Create blip */
3717  KS_WAVE gzBlipWave = KS_INIT_WAVE;
3718  double gArea = 1 / (sms_slice_gap / 10) / (GAM); /* (g sec)/cm, k-area of each blip */
3719  gzblip.area = gArea * 1e6; /* (G/cm)*us */
3720  status = ks_eval_trap1(&gzblip, "PINSblip"); if (status != SUCCESS) return status;
3721  status = ks_eval_trap2wave(&gzBlipWave, &gzblip); if (status != SUCCESS) return status;
3722  ks_init_trap(&gzblip);
3723 
3724  /* Calcualte PINS parameters */
3725  double kzw = (selrf->rf.bw * selrf->rf.rfwave.duration * 1e-6) / (selrf->slthick / 10); /* 1/cm, width in kz-space we must go */
3726  int NPulses = (floor(ceil(kzw / (1 / (sms_slice_gap / 10))) / 2) * 2) + 1; /* number of subpulses (odd) */
3727  int hpw = ceil((float)selrf->rf.rfwave.res / (float)NPulses); /* number of time points in sub pulse */
3728  int interpolRes = hpw * NPulses;
3729 
3730  /* Create index for interpolation */
3731  float *newGrid = (float*)alloca(interpolRes * sizeof(float));
3732  float *baseGrid = (float*)alloca(selrf->rf.rfwave.res * sizeof(float));
3733  for (idx = 0; idx < selrf->rf.rfwave.res; idx++) {
3734  baseGrid[idx] = (float) idx / (selrf->rf.rfwave.res - 1);
3735  }
3736  for (idx = 0; idx < interpolRes; idx++) {
3737  newGrid[idx] = (float) idx / (interpolRes - 1);
3738  }
3739 
3740  /* Interpolate the base rf & phase to make their resolution divideble by NPulses */
3741  KS_WAVEFORM rfNewRes = KS_INIT_WAVEFORM;
3742  KS_WAVEFORM phNewRes = KS_INIT_WAVEFORM;
3743  ks_eval_linear_interp1(baseGrid, selrf->rf.rfwave.res, selrf->rf.rfwave.waveform, newGrid, interpolRes, rfNewRes);
3744  ks_eval_linear_interp1(baseGrid, selrf->rf.thetawave.res, selrf->rf.thetawave.waveform, newGrid, interpolRes, phNewRes);
3745 
3746  if (debug == 1) {ks_print_waveform(rfNewRes, "rfNewRes.txt", interpolRes); }
3747  if (debug == 1) {ks_print_waveform(phNewRes, "phNewRes.txt", interpolRes); }
3748 
3749  /* Loop to concatenate the sub pulses and gradient blips */
3750  for (idx = 0; idx < NPulses; idx++) {
3751  for (jdx = 0; jdx < hpw; jdx++) {
3752  selrfPINS->rf.rfwave.waveform[kdx] = rfNewRes[(idx * hpw) + jdx];
3753  selrfPINS->rf.thetawave.waveform[kdx] = phNewRes[(idx * hpw) + jdx];
3754  selrfPINS->gradwave.waveform[kdx] = 0.0;
3755  kdx++;
3756  }
3757  if (idx != NPulses - 1) {
3758  for (jdx = 0; jdx < gzBlipWave.res; jdx++) {
3759  selrfPINS->rf.rfwave.waveform[kdx] = 0.0;
3760  selrfPINS->rf.thetawave.waveform[kdx] = 0.0;
3761  selrfPINS->gradwave.waveform[kdx] = gzBlipWave.waveform[jdx];
3762  kdx++; gdx++;
3763  }
3764  }
3765  }
3766 
3767  /* Set duration and resolution */
3768  selrfPINS->grad.duration = 0;
3770  selrfPINS->gradwave.res = RUP_FACTOR(kdx, GRAD_UPDATE_TIME);
3771  selrfPINS->gradwave.duration = selrfPINS->gradwave.res * GRAD_UPDATE_TIME;
3772  selrfPINS->rf.rfwave.res = selrfPINS->gradwave.res;
3773  selrfPINS->rf.rfwave.duration = selrfPINS->gradwave.duration;
3774  selrfPINS->rf.rfpulse.isodelay = RUP_FACTOR((int)(selrfPINS->rf.rfpulse.isodelay * ((float)selrfPINS->rf.rfwave.duration/(float)orgDur)),RF_UPDATE_TIME);
3775  selrfPINS->rf.iso2end = selrfPINS->rf.rfpulse.isodelay;
3776  selrfPINS->rf.bw = 1e3 * (NPulses+1) * selrf->slthick / sms_slice_gap;
3777 
3778  /* If the resolution has been rounded up, place zeros at the end */
3779  for (idx = kdx; idx < selrfPINS->rf.rfwave.res; idx++) {
3780  selrfPINS->rf.rfwave.waveform[idx] = 0.0;
3781  selrfPINS->rf.thetawave.waveform[idx] = selrfPINS->rf.thetawave.waveform[kdx];
3782  selrfPINS->gradwave.waveform[idx] = 0.0;
3783  }
3784 
3785  if (debug == 1) {ks_print_waveform(selrfPINS->rf.rfwave.waveform, "dpins_RF.txt", selrfPINS->rf.rfwave.res); }
3786  if (debug == 1) {ks_print_waveform(selrfPINS->gradwave.waveform, "dpins_GRAD.txt", selrfPINS->gradwave.res); }
3787 
3788  /* Normalize to 1 */
3789  ks_waveform_multiplyval(selrfPINS->rf.rfwave.waveform, 1.0 / ks_waveform_absmax(selrfPINS->rf.rfwave.waveform, selrfPINS->rf.rfwave.res), selrfPINS->rf.rfwave.res);
3790 
3791  /* rfStat */
3792  float orgMaxB1 = selrfPINS->rf.rfpulse.max_b1;
3793  selrfPINS->rf.thetawave.res = 0;
3794  status = ks_eval_rfstat(&selrfPINS->rf); if (status != SUCCESS) return status;
3795  selrfPINS->rf.rfpulse.max_b1 = orgMaxB1;
3796 
3797  /* Theta wave */
3798  selrfPINS->rf.thetawave.duration = selrfPINS->gradwave.duration;
3799  selrfPINS->rf.thetawave.res = selrfPINS->gradwave.res;
3800 
3801  for (idx = 0; idx < selrfPINS->rf.thetawave.res; idx++) {
3802  if (fabs(selrfPINS->rf.thetawave.waveform[idx]) > 180.0) {
3803  double angle = selrfPINS->rf.thetawave.waveform[idx];
3804  double revolutions = floor((angle + 180.0) / 360.0);
3805  selrfPINS->rf.thetawave.waveform[idx] = (float) (angle - revolutions * 360.0);
3806  }
3807  }
3808 
3809  if (debug == 1) {ks_print_waveform(selrfPINS->rf.thetawave.waveform, "dpins_TH.txt", selrfPINS->rf.thetawave.res); }
3810 
3811  /* Register the RF */
3812  char description[KS_DESCRIPTION_LENGTH + 4];
3813  sprintf(description, "%s_PINSDANTE", selrfPINS->rf.rfwave.description);
3814  sprintf(selrfPINS->gradwave.description, "%s_PINSDANTE", selrfPINS->rf.rfwave.description);
3815  if (debug == 1) {fprintf(stderr, "---------- PINS DANTE %s rfstat ---------\n", selrfPINS->rf.rfwave.description);}
3816  if (debug == 1) {ks_print_rfpulse(selrfPINS->rf.rfpulse, stderr);}
3817  return ks_eval_rf(&selrfPINS->rf, description);
3818 
3819 }
Definition: KSFoundation.h:1975
KS_TRAP grad
Definition: KSFoundation.h:1491
STATUS ks_eval_trap1(KS_TRAP *trap, const char *const desc)
Sets up a trapezoid using a KS_TRAP sequence object with preset gradient constraints
Definition: KSFoundation_host.c:446
Core sequence object for making trapezoids on X,Y,Z, and OMEGA boards
Definition: KSFoundation.h:578
Core sequence object making arbitrary waveforms on any board (using float data format)
Definition: KSFoundation.h:664
int debug
Definition: GERequired.e:2642
KS_SMS_INFO sms_info
Definition: KSFoundation.h:1494
#define KS_INIT_WAVE
Definition: KSFoundation.h:227
KS_WAVE gradwave
Definition: KSFoundation.h:1493
RF_PULSE rfpulse
Definition: KSFoundation.h:948
void ks_init_trap(KS_TRAP *trap)
Resets a KS_TRAP sequence object to its default value (KS_INIT_TRAP)
Definition: KSFoundation_host.c:58
int gradwave_units
Definition: KSFoundation.h:671
float slice_gap
Definition: KSFoundation.h:1369
Definition: KSFoundation.h:1963
void ks_eval_linear_interp1(const float *x, int x_length, const float *y, const float *xx, int xx_length, float *yy)
Linear interpolation
Definition: KSFoundation_host.c:3844
STATUS ks_eval_trap2wave(KS_WAVE *wave, const KS_TRAP *trap)
Converts a trapezoid object (KS_TRAP) to a wave object (KS_WAVE)
Definition: KSFoundation_host.c:3880
float GAM
#define KS_INIT_WAVEFORM
Definition: KSFoundation.h:226
#define KS_DESCRIPTION_LENGTH
Definition: KSFoundation.h:185
#define KS_INIT_TRAP
Definition: KSFoundation.h:212
KS_WAVE thetawave
Definition: KSFoundation.h:951
KS_RF rf
Definition: KSFoundation.h:1485
KS_DESCRIPTION description
Definition: KSFoundation.h:666
STATUS ks_eval_rfstat(KS_RF *rf)
Sets up the RF_PULSE structure of a KS_RF object
Definition: KSFoundation_host.c:2615
KS_WAVE rfwave
Definition: KSFoundation.h:949
float KS_WAVEFORM[KS_MAXWAVELEN]
Definition: KSFoundation.h:262
float area
Definition: KSFoundation.h:582
int iso2end
Definition: KSFoundation.h:945
void ks_print_waveform(const KS_WAVEFORM waveform, const char *filename, int res)
Writes a KS_WAVEFORM to disk with the specified filename
Definition: KSFoundation_host.c:5135
STATUS ks_eval_rf(KS_RF *rf, const char *const desc)
Sets up a KS_RF object
Definition: KSFoundation_host.c:2707
void ks_waveform_multiplyval(KS_WAVEFORM waveform, float val, int res)
In-place scalar multiplication of a KS_WAVEFORM
Definition: KSFoundation_common.c:1390
int pulse_type
Definition: KSFoundation.h:1370
void ks_print_rfpulse(RF_PULSE rfpulse, FILE *fp)
Writes out the contents of an RF_PULSE struct for debugging
Definition: KSFoundation_host.c:5246
float ks_waveform_absmax(const KS_WAVEFORM waveform, int res)
Returns the maximum absolute value in a KS_WAVEFORM
Definition: KSFoundation_common.c:1203
int duration
Definition: KSFoundation.h:585
int res
Definition: KSFoundation.h:667
KS_WAVEFORM waveform
Definition: KSFoundation.h:669
int duration
Definition: KSFoundation.h:668
float slthick
Definition: KSFoundation.h:1487
float bw
Definition: KSFoundation.h:941

◆ ks_eval_findNearestNeighbourIndex()

int ks_eval_findNearestNeighbourIndex ( float  value,
const float *  x,
int  length 
)

Find nearest neighbor index (NEEDS BETTER DOCUMENTATION)

Parameters
[in]value
[in]xInput array
[in]lengthLength of array
Return values
indexIndex into array
3822  {
3823 
3824  float dist = fabs(value - x[0]);
3825  float newDist;
3826  int idx = 0, i;
3827 
3828  for (i = 1; i < length; i++) {
3829 
3830  newDist = value - x[i];
3831 
3832  if (0 < newDist && newDist < dist) {
3833 
3834  dist = newDist;
3835  idx = i;
3836  }
3837  }
3838 
3839  return idx;
3840 }
int32_t i
Definition: KSFoundation_tgt.c:1389

◆ ks_eval_linear_interp1()

void ks_eval_linear_interp1 ( const float *  x,
int  x_length,
const float *  y,
const float *  xx,
int  xx_length,
float *  yy 
)

Linear interpolation

Parameters
[in]xInput array of x coordinates for data (y)
[in]x_lengthLength of array
[in]yInput array of data (same length as x)
[in]xxArray of new sample points of the data in y
[in]xx_lengthLength of array with new sample points
[out]yyOutput array with interpolated data
Return values
STATUSSUCCESS or FAILURE
3844  {
3845 
3846  int i, index;
3847  float dx, dy;
3848  float *slope = (float*)alloca(x_length * sizeof(float));
3849  float *intercept = (float*)alloca(x_length * sizeof(float));
3850  float *Y = (float*)alloca(x_length * sizeof(float));
3851  memcpy(Y, y, x_length * sizeof(float));
3852 
3853  for (i = 0; i < x_length; i++) {
3854 
3855  if (i < x_length - 1) {
3856 
3857  dx = x[i + 1] - x[i];
3858  dy = Y[i + 1] - Y[i];
3859  slope[i] = dy / dx;
3860  intercept[i] = Y[i] - x[i] * slope[i];
3861 
3862  } else {
3863 
3864  slope[i] = slope[i - 1];
3865  intercept[i] = intercept[i - 1];
3866 
3867  }
3868  }
3869 
3870  for (i = 0; i < xx_length; i++) {
3871 
3872  index = ks_eval_findNearestNeighbourIndex(xx[i], x, x_length);
3873  yy[i] = slope[index] * xx[i] + intercept[index];
3874  }
3875 } /* EOF */
int ks_eval_findNearestNeighbourIndex(float value, const float *x, int length)
Find nearest neighbor index (NEEDS BETTER DOCUMENTATION)
Definition: KSFoundation_host.c:3822
int32_t i
Definition: KSFoundation_tgt.c:1389

◆ ks_eval_trap2wave()

STATUS ks_eval_trap2wave ( KS_WAVE wave,
const KS_TRAP trap 
)

Converts a trapezoid object (KS_TRAP) to a wave object (KS_WAVE)

The input KS_TRAP object will be converted to a KS_WAVE object, which will have its .waveform (float array) field filled with the shape of the trapezoid with the maintained GRAD_UPDATE_TIME (4 [us]) sample duration. The .waveform amplitude on the plateau will be .amp [G/cm].

Parameters
[out]wavePointer to a KS_WAVE object to be set up with same shape as the trapezoid (KS_TRAP) in arg 2
[in]trapThe trapezoid to convert into a waveform
Return values
STATUSSUCCESS or FAILURE
3880  {
3881 
3882  int i;
3883  int idx = 0;
3884 
3885  ks_init_wave(wave);
3886 
3887  int pointsInRamp = (trap->ramptime / GRAD_UPDATE_TIME) + 1;
3888  int pointsInPlateau = (trap->plateautime / GRAD_UPDATE_TIME) - 2;
3889  int pointsInWave = 2 * pointsInRamp + pointsInPlateau;
3890  double slope = trap->amp / pointsInRamp;
3891 
3892  if (trap->duration != (pointsInWave * GRAD_UPDATE_TIME)) {
3893  return ks_error("ks_eval_trap2wave(%s): field .ramptime or .plateautime not divisible by GRAD_UPDATE_TIME (4)", trap->description);
3894  }
3895 
3896  /* ramp up */
3897  for (i = 1; i <= pointsInRamp; i++) {
3898  wave->waveform[idx++] = (float)(i * slope);
3899  }
3900 
3901  /* plateau */
3902  for (i = 0; i < pointsInPlateau; i++) {
3903  wave->waveform[idx++] = trap->amp;
3904  }
3905 
3906  /* ramp down */
3907  for (i = pointsInRamp; i > 0; i--) {
3908  wave->waveform[idx++] = (float)(i * slope);
3909  }
3910 
3911  /* set fields */
3912  wave->res = idx;
3913  if (pointsInWave != wave->res) {
3914  return ks_error("ks_eval_trap2wave(%s): Implementation error - The resulting KS_WAVE has wrong res [%d!=%d]", trap->description, pointsInWave, wave->res);
3915  }
3916 
3917  wave->duration = idx * GRAD_UPDATE_TIME;
3918  if (trap->duration != wave->duration) {
3919  return ks_error("ks_eval_trap2wave(%s): Implementation error - The resulting KS_WAVE has wrong duration [%d!=%d]", trap->description,trap->duration,wave->duration);
3920  }
3921 
3922  sprintf(wave->description, "%s_wave", trap->description);
3923 
3924  return SUCCESS;
3925 
3926 } /* EOF */
int plateautime
Definition: KSFoundation.h:584
STATUS ks_error(const char *format,...) __attribute__((format(printf
Common error message function for HOST and TGT
int32_t i
Definition: KSFoundation_tgt.c:1389
KS_DESCRIPTION description
Definition: KSFoundation.h:666
float amp
Definition: KSFoundation.h:581
void ks_init_wave(KS_WAVE *wave)
Resets a KS_WAVE sequence object to its default value (KS_INIT_WAVE)
Definition: KSFoundation_host.c:66
KS_DESCRIPTION description
Definition: KSFoundation.h:580
int duration
Definition: KSFoundation.h:585
int res
Definition: KSFoundation.h:667
KS_WAVEFORM waveform
Definition: KSFoundation.h:669
int ramptime
Definition: KSFoundation.h:583
int duration
Definition: KSFoundation.h:668

◆ ks_eval_append_two_waves()

STATUS ks_eval_append_two_waves ( KS_WAVE first_wave,
KS_WAVE second_wave 
)
3929  {
3930  memcpy( &(first_wave->waveform[first_wave->res]), second_wave->waveform, sizeof(float) *second_wave->res);
3931  first_wave->res += second_wave->res;
3932  first_wave->duration += second_wave->duration;
3933  if (first_wave->res % 2) {
3934  first_wave->waveform[++first_wave->res] = 0.0f;
3935  first_wave->duration += GRAD_UPDATE_TIME;
3936  }
3937  return SUCCESS;
3938 }
int res
Definition: KSFoundation.h:667
KS_WAVEFORM waveform
Definition: KSFoundation.h:669
int duration
Definition: KSFoundation.h:668

◆ ks_eval_concatenate_waves()

STATUS ks_eval_concatenate_waves ( int  num_waves,
KS_WAVE target_wave,
KS_WAVE **  waves_to_append 
)
3940  {
3941  int idx;
3942  for(idx = 0; idx < num_waves; idx++) {
3943  if (ks_eval_append_two_waves(target_wave, waves_to_append[idx]) != SUCCESS) {
3944  ks_error("%s failed", __FUNCTION__);
3945  return FAILURE;
3946  };
3947  }
3948 
3949  return SUCCESS;
3950 }
STATUS ks_eval_append_two_waves(KS_WAVE *first_wave, KS_WAVE *second_wave)
Definition: KSFoundation_host.c:3929
STATUS ks_error(const char *format,...) __attribute__((format(printf
Common error message function for HOST and TGT

◆ ks_eval_epi_constrained()

STATUS ks_eval_epi_constrained ( KS_EPI epi,
const char *const  desc,
float  ampmax,
float  slewrate 
)

Sets up a KS_EPI composite sequence object, subject to gradients constraints specified as input arguments

The KS_EPI composite sequence object controls an entire EPI train (including de/rephasers on freq & phase axes). Four fields in KS_EPI are other sequence objects:

  • KS_READTRAP .read: The EPI readout lobes including data acquisition
  • KS_TRAP .readphaser: The dephaser & rephaser gradients before & after the train of readout lobes, on the same board as .read
  • KS_TRAP .blip: The blip gradients in the phase encoding direction between the readout lobes
  • KS_PHASER .blipphaser: The dephaser & rephaser gradients on the same board as the .blip gradients. For multi-shot EPI these gradients will change in scan() when calling ks_scan_epi_shotcontrol()

Before calling this function, portions of the .read field (KS_READTRAP) and .blipphaser field (KS_PHASER) must be set up. Here follows a list of fields that must be set:

  1. .read.fov: The Field-of-View (FOV) in the frequency encoding (readout) direction
  2. .read.res: The number of pixels within the FOV along the frequency encoding direction
  3. .blipphaser.fov: The Field-of-View (FOV) in the phase encoding direction
  4. .blipphaser.res: The number of pixels within the FOV along the phase encoding direction
  5. .blipphaser.R: Either the number of EPI shots or the parallel imaging factor
  6. .read.rampsampling: Whether rampsampling should be used (see KS_READTRAP). Value of 1 is recommended for EPI
  7. .read.acq.rbw: The receiver bandwidth in [kHz/FOV]. Maximum: 250 (recommended)

If the KS_EPI object is initialized with KS_INIT_EPI on declaration, .read.rampsampling will already be set to 1 and .read.acq.rbw will be set to 250.0 (hence steps 6-7 can be skipped).

Single-shot EPI acquisitions are geometrically distorted to some degree depending on chosen .read.res and .blipphaser.fov. To reduce the EPI distortions, either multi-shot EPI or parallel imaging acceleration can be used. For both alternatives, every Rth phase ecoding lines are acquired in k-space as the EPI train is played out. R corresponds to the number of shots (opnshots menu in the UI) or the parallel imaging factor. Therefore, e.g. a 4-shot EPI readout is identical to an R = 4 accelerated EPI readout with the same FOV and resolution, why one same field .R may be used to control the EPI train setup, be it multi-shot or parallel imaging acceleration (but not both at the same time). What differs between multi-shot and parallel imaging acceleration in the acquisition is only whether the .blipphaser should change from TR to TR. Hence, to set up a KS_EPI object, use the field .R for both multi-shot EPI and parallel imaging scenarios.

The remaining fields of interest in the KS_EPI sequence objects are:

  • .etl: This is the echo-train-length (ETL) of the EPI readout train, set by ks_eval_epi() based on .blipphaser.res and .blipphaser.R
  • .read_spacing: Additional spacing in [us] between readout lobes. Default value is zero, but can be set > 0 if additional gradients is to be inserted between the readout lobes. One scenario is adding CAIPIRINHA blips on ZGRAD during the time where the EPI readout is played out in the pulse sequence
  • .duration: Convenience information for timing calculations, set by ks_eval_epi(). This is the total time in [us] for the EPI train, including the de/rephasers on the frequency/phase axes. Note that there are also .duration fields for each of the four KS_*** sequence objects in KS_EPI, stating the duration of each of these components.
  • .time2center: Convenience information for timing calculations, set by ks_eval_epi(). The time in [us] from the start of the first dephaser until the center of k-space is reached. This deviates from <epi>.duration/2 for partial Fourier EPI trains where .blipphaser.nover > 0 (a.k.a. fractional NEX, but typically shown as MiniumTE in the UI for EPI)

If peripheral nerve stimulation would be observed, consider calling ks_eval_epi_constrained() with reduced slewrate. This will however increase the geometric distortions in the images

Parameters
[in,out]epiPointer to the KS_EPI object to be set up
[in]descA description (text string) of the KS_SELRF object. This description is used for pulse sequence generation (seen in the file generated by TimeHist in MGD Sim/WTools)
[in]ampmaxThe maximum allowed gradient amplitude ([G/cm])
[in]slewrateThe maximum allowed slewrate ([(G/cm) / us]). Value of 0.01 corresponds to 100 mT/m/s
Return values
STATUSSUCCESS or FAILURE
3957  {
3958  char tmpstr[1000];
3959  int kspacelines_noacc;
3960  /* same code as ks_eval_dixon_dualreadtrap */
3961  const float ampmax_phys = FMin(3, phygrd.xfs, phygrd.yfs, phygrd.zfs);
3962  const int ramptimemax_phys = IMax(3, phygrd.xrt, phygrd.yrt, phygrd.zrt);
3963  const float slewrate_phys = ampmax_phys / ramptimemax_phys;
3964  float slewrate_phasers; /* lower slew rate for phasers before/after the train */
3965  float ampmax_phasers; /* lower ampmax for phasers before/after the train */
3966 
3967  /* for now spherical model such that out of spec slewrate or amplitude cannot happen (for the phasers), even with prospective moco */
3968  if (epi->zphaser.res > 1) {
3969  slewrate_phasers = FMin(2, slewrate, slewrate_phys / sqrt(3));
3970  ampmax_phasers = FMin(2, ampmax, ampmax_phys / sqrt(3));
3971  } else {
3972  slewrate_phasers = FMin(2, slewrate, slewrate_phys / sqrt(2));
3973  ampmax_phasers = FMin(2, ampmax, ampmax_phys / sqrt(2));
3974  }
3975 
3976  if (desc == NULL || desc[0] == ' ') {
3977  return ks_error("%s: description (2nd arg) cannot be NULL or begin with a space", __FUNCTION__);
3978  }
3979 
3980  /************************** Step 1: resolution checking ************************/
3981 
3982  /* For EPI, we don't allow negative 'nover'. For lower k-space pFourier control, we use ks_phaseencoding_generate_epi() */
3983  if (epi->blipphaser.nover < 0) {
3984  return ks_error("%s(%s): negative 'blipphaser.nover' not allowed for EPI. See ks_phaseencoding_generate_epi()", __FUNCTION__, desc);
3985  }
3986  if (abs(epi->read.nover) > 0) {
3987  return ks_error("%s(%s): partial Fourier in read is not supported", __FUNCTION__, desc);
3988  }
3989  if (epi->minbliparea < 0 || epi->minbliparea > 1000) {
3990  return ks_error("%s(%s): field 'minbliparea' must be in range [0,1000]", __FUNCTION__, desc);
3991  }
3992  if (epi->read.res % 2) {
3993  return ks_error("%s(%s): Read res must be even", __FUNCTION__, desc);
3994  }
3995 
3996  /************************** Step 2: blip phaser (DE/REphasers) ***************************/
3997  epi->blipphaser.nacslines = 0; /* ACS lines are forbidden for EPI */
3998 
3999  /* blip DE/REphaser (KS_PHASER). Here the '.res' and '.nover' fields may be rounded due to '.R'
4000  as ks_eval_phaser_constrained() is also calling ks_eval_phaser_adjustres() */
4001  sprintf(tmpstr, "%s.blipphaser", desc);
4002  if (ks_eval_phaser_constrained(&epi->blipphaser, tmpstr, ampmax_phasers, slewrate_phasers, 0) == FAILURE)
4003  return FAILURE;
4004 
4005  /************************** Step 3: z phaser ***************************/
4006 
4007  /* blip DE/REphaser (KS_PHASER). Here the '.res' and '.nover' fields may be rounded due to '.R'
4008  as ks_eval_phaser_constrained() is also calling ks_eval_phaser_adjustres() */
4009  sprintf(tmpstr, "%s.zphaser", desc);
4010  if ((epi->blipphaser.nover > 0) && (epi->zphaser.nover > 0)) {
4011  return ks_error("%s: Cannot have partial Fourier in both ky (nover=%d) and kz (nover=%d)", __FUNCTION__, epi->blipphaser.nover, epi->zphaser.nover);
4012  }
4013  if (epi->zphaser.res > 1) {
4014  if (ks_eval_phaser_constrained(&epi->zphaser, tmpstr, ampmax_phasers, slewrate_phasers, 0) == FAILURE)
4015  return FAILURE;
4016  } else {
4017  ks_init_phaser(&epi->zphaser);
4018  }
4019 
4020  /************************** Step 4: ETL ***************************/
4021 
4022  if (epi->blipphaser.nover > 0) {
4023  kspacelines_noacc = epi->blipphaser.res / 2 + epi->blipphaser.nover;
4024  } else {
4025  kspacelines_noacc = epi->blipphaser.res;
4026  }
4027 
4028  epi->etl = kspacelines_noacc / epi->blipphaser.R;
4029 
4030  if (epi->etl < 1) {
4031  return ks_error("%s(%s): R (or shots) must be in range [1,%d]", __FUNCTION__, desc, kspacelines_noacc);
4032  }
4033 
4034  /********************* Step 5: EPI blips ********************/
4035 
4036  /* blip area [(G/cm)*usec] */
4037  if (epi->etl > 1) {
4038 
4040 
4041  /* The blip area should not be too small to reduce discretization errors. This is based on observations doing moment calcs in WTools. */
4042  if (epi->blip.area < epi->minbliparea)
4043  epi->blipoversize = (epi->minbliparea / epi->blip.area); /* design a bigger blip, and use .blipoversize in ks_scan_epi_shotcontrol() to reduce the amp correspondingly */
4044  else
4045  epi->blipoversize = 1.0;
4046 
4048  } else {
4049  epi->blip.area = 0.0;
4050  }
4051  /* Get ramp & plateau time as well as amplitude of the blip */
4052  sprintf(tmpstr, "%s.blip", desc);
4053 
4054  if (ks_eval_trap_constrained(&epi->blip, tmpstr, ampmax, slewrate, 0) == FAILURE)
4055  return FAILURE;
4056 
4057 
4058  /************************** Step 6: Read width and amp **************************/
4059 
4060  /* rampsampling (normal case): We don't want to acquire data during half the blip duration (straddling two readouts) */
4061  if (epi->read.rampsampling)
4062  epi->read.acqdelay = epi->blip.duration / 2;
4063 
4064  sprintf(tmpstr, "%s.read", desc);
4065  if (ks_eval_readtrap_constrained(&epi->read, tmpstr, ampmax, slewrate) == FAILURE)
4066  return FAILURE;
4067 
4068  if (!epi->read.rampsampling) {
4069  /* non-rampsampling (unusual case) */
4070  if (epi->read.grad.ramptime < (epi->blip.duration / 2)) {
4071  /* blip-duration limited: Prolong the read gradient ramps to fit the blip, reduce PNS and acoustic noise */
4072  epi->read.grad.ramptime = epi->blip.duration / 2;
4073  epi->read.acqdelay = epi->read.grad.ramptime;
4074  epi->read.grad.duration = epi->read.grad.plateautime + 2 * epi->read.grad.ramptime;
4075  epi->read.grad.area = (epi->read.grad.plateautime + epi->read.grad.ramptime) * epi->read.grad.amp;
4076  epi->read.area2center = epi->read.grad.area / 2;
4077  }
4078  }
4079 
4080 
4081  /************************** Step 7: read phaser (DE/REphasers) **************************/
4082 
4083  /* readphaser (KS_TRAP) */
4084  epi->readphaser.area = -epi->read.area2center;
4085 
4086  if (!areSame(epi->readphaser.area, 0.0)) {
4087  sprintf(tmpstr, "%s.readphaser", desc);
4088  if (ks_eval_trap_constrained(&epi->readphaser, tmpstr, ampmax_phasers, slewrate_phasers, 0) == FAILURE)
4089  return FAILURE;
4090  }
4091 
4092 
4093  /**************************** Step 8: Convenience info ****************************/
4094 
4095  ks_eval_epi_setinfo(epi);
4096 
4097 
4098  return SUCCESS;
4099 
4100 } /* ks_eval_epi_constrained */
float blipoversize
Definition: KSFoundation.h:1847
KS_TRAP blip
Definition: KSFoundation.h:1839
int plateautime
Definition: KSFoundation.h:584
int R
Definition: KSFoundation.h:1680
int res
Definition: KSFoundation.h:1678
#define areSame(a, b)
Definition: KSFoundation.h:119
float ks_calc_fov2gradareapixel(float fov)
Calculates the gradient area needed to move one pixel in k-space
Definition: KSFoundation_common.c:346
int nacslines
Definition: KSFoundation.h:1681
STATUS ks_eval_epi_setinfo(KS_EPI *epi)
Set convenience info for KS_EPI based on objects&#39; timing in KS_EPI
Definition: KSFoundation_host.c:4104
STATUS ks_error(const char *format,...) __attribute__((format(printf
Common error message function for HOST and TGT
KS_PHASER blipphaser
Definition: KSFoundation.h:1840
STATUS ks_eval_phaser_constrained(KS_PHASER *phaser, const char *const phasername, float ampmax, float slewrate, int minduration)
Sets up a trapezoid for phase encoding, subject to gradient constraints specified as input arguments...
Definition: KSFoundation_host.c:2035
STATUS ks_eval_trap_constrained(KS_TRAP *trap, const char *const desc, float ampmax, float slewrate, int minduration)
Sets up a trapezoid using a KS_TRAP sequence object with gradient constraints specified as input argu...
Definition: KSFoundation_host.c:319
KS_PHASER zphaser
Definition: KSFoundation.h:1841
PHYS_GRAD phygrd
KS_TRAP grad
Definition: KSFoundation.h:1586
void ks_init_phaser(KS_PHASER *phaser)
Resets a KS_PHASER sequence object to its default value (KS_INIT_PHASER)
Definition: KSFoundation_host.c:86
STATUS ks_eval_readtrap_constrained(KS_READTRAP *readtrap, const char *const desc, float ampmax, float slewrate)
Sets up an acquisition window with a trapezoid, subject to gradient constraints specified as input ar...
Definition: KSFoundation_host.c:545
int etl
Definition: KSFoundation.h:1843
int res
Definition: KSFoundation.h:1577
KS_READTRAP read
Definition: KSFoundation.h:1837
float minbliparea
Definition: KSFoundation.h:1848
float area
Definition: KSFoundation.h:582
float amp
Definition: KSFoundation.h:581
int nover
Definition: KSFoundation.h:1679
KS_TRAP readphaser
Definition: KSFoundation.h:1838
int nover
Definition: KSFoundation.h:1579
int rampsampling
Definition: KSFoundation.h:1578
int duration
Definition: KSFoundation.h:585
float area2center
Definition: KSFoundation.h:1584
float fov
Definition: KSFoundation.h:1677
int acqdelay
Definition: KSFoundation.h:1580
int ramptime
Definition: KSFoundation.h:583

◆ ks_eval_epi_setinfo()

STATUS ks_eval_epi_setinfo ( KS_EPI epi)

Set convenience info for KS_EPI based on objects' timing in KS_EPI

4104  {
4105 
4106  epi->duration = epi->etl * epi->read.grad.duration + \
4107  (epi->etl - 1) * epi->read_spacing + \
4108  IMax(3, epi->readphaser.duration, epi->blipphaser.grad.duration, epi->zphaser.grad.duration) + \
4109  IMax(3, epi->readphaser.duration, epi->blipphaser.grad.duration, epi->zphaser.grad.duration);
4110 
4111  if (abs(epi->blipphaser.nover)) {
4112  /* partial Fourier */
4113  epi->time2center = ((epi->read.grad.duration + epi->read_spacing) * (epi->blipphaser.nover / epi->blipphaser.R)) - epi->read_spacing / 2;
4114  } else {
4115  epi->time2center = ((epi->read.grad.duration + epi->read_spacing) * epi->etl / 2) - epi->read_spacing / 2;
4116  }
4117  epi->time2center += IMax(3, epi->readphaser.duration, epi->blipphaser.grad.duration, epi->zphaser.grad.duration);
4118 
4119  return SUCCESS;
4120 }
int R
Definition: KSFoundation.h:1680
KS_TRAP grad
Definition: KSFoundation.h:1676
KS_PHASER blipphaser
Definition: KSFoundation.h:1840
KS_PHASER zphaser
Definition: KSFoundation.h:1841
KS_TRAP grad
Definition: KSFoundation.h:1586
int duration
Definition: KSFoundation.h:1845
int etl
Definition: KSFoundation.h:1843
KS_READTRAP read
Definition: KSFoundation.h:1837
int nover
Definition: KSFoundation.h:1679
int time2center
Definition: KSFoundation.h:1846
KS_TRAP readphaser
Definition: KSFoundation.h:1838
int read_spacing
Definition: KSFoundation.h:1844
int duration
Definition: KSFoundation.h:585

◆ ks_eval_epi_maxamp_slewrate()

STATUS ks_eval_epi_maxamp_slewrate ( float *  ampmax,
float *  slewrate,
int  xres,
float  quietnessfactor 
)

Get maximum amplitude and slewrate for the EPI readout constrained by hardware and PNS

Get a reasonably optimized max gradient amplitude and slewrate for the EPI train. This function results in similar values as GE's epigradopts(), where gradient system limits and peripheral-nerve-stimulation (PNS) are not to be exceeded. ks_eval_epi_maxamp_slewrate() has no dB/dt model, but is dependent on xres (assuming fixed FOV, rampsampling and maxmimum rBW). This has also been tested for axial and oblique scans on volunteers at Karolinska without PNS experience using FOV = 24 cm on both DV750 and DV750w with rampsampling and rBW = 250. If PNS should still occur, the slewrate value passed to ks_eval_epi_constrained() should be decreased. The units of the slewrate is [(G / cm) / us], i.e. gradient amp in G / cm divided by the ramptime in us. This value times 1e4 gives slewrate in SI units: T / m / s (up to e.g. 200).

Parameters
[out]ampmaxThe maximum allowed gradient amplitude ([G/cm])
[out]slewrateThe maximum allowed slewrate ([(G/cm) / us]). Value of 0.01 corresponds to 100 mT/m/s
[in]xresThe frequency encoding resolution
[in]quietnessfactorValue >= 1 used for optional gradient derating
Return values
STATUSSUCCESS or FAILURE
4124  {
4125  PHYS_GRAD epiphygrd = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
4126  LOG_GRAD epiloggrd = {0, 0, 0, 0, 0, 0, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
4127  int epi_initnewgeo = 1;
4128  extern SCAN_INFO scan_info[SLTAB_MAX];
4129  extern int opcoax;
4130  extern int opplane;
4131  extern int opslquant;
4132  extern int obl_debug;
4133  int stronggradient_flag = 0;
4134  int premiersystem_flag = 0;
4135  const float premierslewrate_max = 0.0150; /* 0.0150 (G/cm)/us = SR150 (T/m)/s */
4136  const float stronggradient_ampmax = 3.5; /* 35 mT/m. Maximum gradient amplitude to avoid too high rBW at k-space center */
4137 
4138  /* relationships below have been tested in the range [16,256] */
4139  if (xres < 16)
4140  xres = 16;
4141  if (xres > 256)
4142  xres = 256;
4143 
4144 
4145  if (quietnessfactor < 1.0) {
4146  return ks_error("%s: The quietness factor must be >= 1.0", __FUNCTION__);
4147  }
4148 
4149 
4150  inittargets(&epiloggrd, &epiphygrd);
4151 
4152  /* we need to know the system limits, but we don't want to be dependent on GERequired:GEReq_init_gradspecs() and ks_srfact
4153  for the EPI readout. I.e. ks_srfact (GERequired.e) can be used to set a proper slewrate tweak factor for all gradients except for
4154  the EPI readout (and EPI blips), while the ampmax and slewrate for the EPI train is controlled only by this function */
4155  if (obloptimize_epi(&epiloggrd, &epiphygrd, scan_info, (opslquant),
4156  (opplane), (opcoax), PSD_OBL_OPTIMAL, (obl_debug), &epi_initnewgeo, cfsrmode) == FAILURE) {
4157  return ks_error("%s: obloptimize_epi() failed", __FUNCTION__);
4158  }
4159 
4160  /* ampmax */
4161 #if EPIC_RELEASE >= 27
4162  stronggradient_flag = (cfgcoiltype == PSD_XRMB_COIL || cfgcoiltype == PSD_HRMW_COIL); /* 750 or Premier. 50+ mT/m */
4163  premiersystem_flag = (cfgcoiltype == PSD_HRMW_COIL);
4164 #else
4165  stronggradient_flag = cfgcoiltype == PSD_XRMB_COIL; /* 750 */
4166 #endif
4167 
4168  if (stronggradient_flag) {
4169  *ampmax = FMin(2, stronggradient_ampmax, 0.01 * xres + 2); /* very unscientific linear relationship, but GE's EPI gradients increase semi-linearly with xres (they use epigradopt.c) */
4170  } else {
4171  /* 750w and others */
4172  if (xres > 128)
4173  *ampmax = 0.005 * xres + 1.36; /* allow the amp to increase slowly for higher res than 128 */
4174  else
4175  *ampmax = 2.0;
4176  }
4177  *ampmax = FMin(2, *ampmax, epiphygrd.xfs);
4178 
4179 
4180  /* slewrate */
4181  if (stronggradient_flag) {
4182  //* 750 or Premier. 50+ mT/m */
4183  if (xres <= 48){
4184  *slewrate = 250e-4; /* SR250 */
4185  } else {
4186  *slewrate = 1.0e-4 / (2.4e-5 * xres + 0.003); /* SR250 (xres 48) -> SR110 (xres 256). 1/x function */
4187  }
4188  if (premiersystem_flag && *slewrate > premierslewrate_max) {
4189  *slewrate = premierslewrate_max;
4190  }
4191  } else {
4192  /* 750w. 33 mT/m, SR120 (and all others) */
4193  if (xres <= 48)
4194  *slewrate = 170e-4; /* SR170 */
4195  else
4196  *slewrate = 120e-4; /* SR120 */
4197 
4198  /* use the relative ampmax reduction dependent on slice angulation to instead reduce the slewrate on 750w.
4199  ks_syslimits_ampmax2(&epiloggrd) returns 1.0 for axial scans,
4200  but for oblique scans the value is reduced. Although this should cap the amplitude, we use
4201  is here to instead reduce the slewrate, since we need some way to make the slewrate lower for (double)
4202  oblique scans, or maybe when the XGRAD is not parallel to the physical X-axis (R/L) */
4203 
4204  *slewrate *= epiloggrd.tx_xy / epiloggrd.xfs;
4205  }
4206 
4207  /* but if user wants a quieter scan, reduce the slewate */
4208  *slewrate /= quietnessfactor;
4209 
4210 
4211  return SUCCESS;
4212 
4213 } /* ks_eval_epi_maxamp_slewrate() */
STATUS inittargets(LOG_GRAD *lgrad, PHYS_GRAD *pgrad)
STATUS ks_error(const char *format,...) __attribute__((format(printf
Common error message function for HOST and TGT
STATUS obloptimize_epi(LOG_GRAD *lgrad, PHYS_GRAD *pgrad, SCAN_INFO *scaninfotab, INT slquant, INT plane_type, INT coaxial, INT method, INT debug, INT *newgeo, INT srmode)
#define PSD_HRMW_COIL
Definition: KSFoundation.h:165
int obl_debug
Definition: GERequired.e:233
int cfgcoiltype
int cfsrmode

◆ ks_eval_epi()

STATUS ks_eval_epi ( KS_EPI epi,
const char *const  desc,
float  quietnessfactor 
)

Sets up a KS_EPI composite sequence object with preset gradient constraints

This is a wrapper function to ks_eval_epi_constrained() with a reduced set of input arguments using preset gradient constraints. These constraints should allow for EPI readouts for double oblique slice angulations, but the values for ampmax or slewrate may not be optimal. If peripheral nerve stimulation (PNS) would be observed, consider calling ks_eval_epi_constrained() directly with reduced slewrate. This will however increase the geometric distortions in the images

See ks_eval_epi_constrained() for more details on fields in KS_EPI that must be set before calling this function

Parameters
[in,out]epiPointer to the KS_EPI object to be set up
[in]descA description (text string) of the KS_SELRF object. This description is used for pulse sequence generation (seen in the file generated by TimeHist in MGD Sim/WTools)
[in]quietnessfactorValue >= 1 used for optional gradient derating
Return values
STATUSSUCCESS or FAILURE
4219  {
4220  float ampmax, slewrate;
4221 
4222  if (ks_eval_epi_maxamp_slewrate(&ampmax, &slewrate, epi->read.res, quietnessfactor) == FAILURE)
4223  return FAILURE;
4224 
4225  return ks_eval_epi_constrained(epi, desc, ampmax, slewrate);
4226 
4227 } /* ks_eval_epi */
STATUS ks_eval_epi_maxamp_slewrate(float *ampmax, float *slewrate, int xres, float quietnessfactor)
Get maximum amplitude and slewrate for the EPI readout constrained by hardware and PNS...
Definition: KSFoundation_host.c:4124
STATUS ks_eval_epi_constrained(KS_EPI *epi, const char *const desc, float ampmax, float slewrate)
Sets up a KS_EPI composite sequence object, subject to gradients constraints specified as input argum...
Definition: KSFoundation_host.c:3957
int res
Definition: KSFoundation.h:1577
KS_READTRAP read
Definition: KSFoundation.h:1837

◆ ks_eval_makegradpulse()

GRAD_PULSE ks_eval_makegradpulse ( KS_TRAP trp,
int  gradchoice 
)

(Internal use) Creates a GRAD_PULSE struct from a KS_TRAP object

To use GE's minseq() function that determines gradient heating limits from the gradient usage, a GRAD_PULSE array needs to be generated for each gradient axis. This info is for standard EPIC psds stored on disk in a file grad_rf_<psdname>.h. KSFoundation EPIC psds with one or more sequence modules (KS_SEQ_CONTROL) being part of a KS_SEQ_COLLECTION struct, have all gradient trapezoid (KS_TRAP) information stored in seqcollection->seqctrlptr[i].gradrf.trapptr[]. Hence, from the seqcollection struct it is possible to reach all KS_TRAP for all sequence modules (see ks_eval_gradrflimits()), which one by one can be converted to GRAD_PULSE structs using this function.

Parameters
[in]trpPointer to a KS_TRAP object
[in]gradchoiceXGRAD, YGRAD, or ZGRAD, specifying which gradient this is used on
Return values
GRAD_PULSEA gradient pulse structure in GE's standard form
4233  {
4234  GRAD_PULSE g;
4235 
4236  /* we have to compute it here because g.amp is a pointer */
4237  trp->heat_scaled_amp[gradchoice] = trp->amp*trp->heat_scaling_factor[gradchoice];
4238 
4239 
4240  g.ptype = G_TRAP;
4241  g.attack = &trp->ramptime;
4242  g.decay = &trp->ramptime;
4243  g.pw = &trp->plateautime;
4244  g.amps = NULL;
4245  g.amp = &trp->heat_scaled_amp[gradchoice];
4246  g.ampd = NULL;
4247  g.ampe = NULL;
4248  g.gradfile = NULL;
4249  g.num = trp->gradnum[gradchoice];
4250  g.scale = 1.0; /* scale. < 1 for phase enc. */
4251  g.time = NULL;
4252  g.tdelta = 0; /* Time delta in microseconds in between multiple occurances of the pulse */
4253  g.powscale = 1.0; /* might want to do loggrd.xfs/loggrd.tx_xyz instead? */
4254 
4255  /* the following are set by minseq***() later */
4256  g.power = 0.0;
4257  g.powpos = 0.0;
4258  g.powneg = 0.0;
4259  g.powabs = 0.0;
4260  g.amptran = 0.0;
4261  g.pwm = 1;
4262  g.bridge = 0;
4263  g.intabspwmcurr = 0;
4264 
4265  return g;
4266 }
int plateautime
Definition: KSFoundation.h:584
float heat_scaling_factor[3]
Definition: KSFoundation.h:588
float heat_scaled_amp[3]
Definition: KSFoundation.h:587
float amp
Definition: KSFoundation.h:581
int gradnum[3]
Definition: KSFoundation.h:586
int ramptime
Definition: KSFoundation.h:583

◆ ks_eval_seqctrl_setminduration()

STATUS ks_eval_seqctrl_setminduration ( KS_SEQ_CONTROL seqctrl,
int  mindur 
)

Sets the minimum duration and duration fields of a KS_SEQ_CONTROL struct based on some minimum time (arg 2)

It is recommended that this function is put at the end of the sequence generating function for the current sequence module (c.f. KS_SEQ_CONTROL), e.g. mypsd_pg(), which is a point where one knows how long the pulse sequence is, as the last gradient or RF waveform has been placed out. After passing the known minimum allowed sequence module duration as arg2 to ks_eval_seqctrl_setminduration(), both seqctrl.duration and `seqctrl.min_duration are set to this minimum value + the SSI time for the sequence module (which must be > 0 before calling this function).

This function is only available on HOST for the reason that the .duration field should not be reset to the minimum duration on TGT. The recommended order of events is the following:

  1. In cveval() on HOST: Call mypsd_pg(). Having the ks_eval_seqctrl_setminduration() call at the end of mypsd_pg(), with the second argument (minimum duration) set to the absolute time corresponding to the last waveform, it is assured that both seqctrl.duration (which can grow bigger later) and seqctrl.min_duration (which should not be modified later) is equal to the same minimum possible value at this point.
    #ifndef IPG // make it explicit that this function is to be called only on HOST (not IPG)
    // ...end of mypsd_pg()...
    // N.B.: .seqctrl.ssi_time must be > 0 before calling ks_eval_seqctrl_setminduration()
    ks_eval_seqctrl_setminduration(&ksfse.seqctrl, tmploc.pos); // tmploc.pos now corresponds to the end of last gradient in the sequence
    #endif
  2. In cveval() on HOST, after the mypsd_pg() call: Do some TR timing calculations and increase the seqctrl.duration field(s) for one or more sequence modules to fill up to the intended TR. The reasons for why seqctrl.duration needs to be larger than seqctrl.min_duration could be either that the TR is larger than the minimum possible by the waveforms, and/or that SAR or hardware constraints (reported back by ks_eval_mintr()) mandates that additional time must be added to the sequence to honor these limits.
  3. In pulsegen() on TGT (IPG): The mypsd_pg() function is called to place out the actual hardware waveforms, followed by the "sequence ending here"-call to KS_SEQLENGTH(), which is using seqctrl.duration to determine the length of the sequence module. As efforts have been made on HOST to set seqctrl.duration, we cannot have ks_eval_seqctrl_setminduration() to reset it again on TGT when mypsd_pg() is called. Hence this is why ks_eval_seqctrl_setminduration() is only accessible on HOST
Parameters
[in,out]seqctrlPointer to KS_SEQ_CONTROL struct for the current sequence module
[in]mindurmindur + .ssi_time => .min_duration = .duration @retval STATUSSUCCESSorFAILURE`
4281  {
4282 #ifndef IPG
4283  /* only do this on HOST */
4284 
4285  if (seqctrl == NULL) {
4286  return ks_error("%s: arg 1 is NULL", __FUNCTION__);
4287  } else if (mindur > 0 && seqctrl->ssi_time <= 0) {
4288  return ks_error("%s: %s - seqctrl.ssi_time must be > 0 before setting minimum duration", __FUNCTION__, seqctrl->description);
4289  } else if (mindur > 0 && seqctrl->ssi_time % 4) {
4290  return ks_error("%s: %s - seqctrl.ssi_time must be divisible by GRAD_UPDATE_TIME", __FUNCTION__, seqctrl->description);
4291  } else if (mindur < 0) {
4292  return ks_error("%s: %s - min duration (arg 2) must be >= 0", __FUNCTION__, seqctrl->description);
4293  } else if (mindur % 4) {
4294  return ks_error("%s: %s - min duration (arg 2) must be divisible by GRAD_UPDATE_TIME", __FUNCTION__, seqctrl->description);
4295  } else {
4296  if (mindur == 0) {
4297  seqctrl->min_duration = 0;
4298  } else {
4299  /* we add 4us (GRAD_UPDATE_TIME) to avoid waveform-after-seqcore errors */
4300  seqctrl->min_duration = RUP_GRD(mindur + seqctrl->ssi_time + GRAD_UPDATE_TIME);
4301  }
4302  seqctrl->duration = seqctrl->min_duration;
4303  }
4304 #endif
4305 
4306  return SUCCESS;
4307 }
STATUS ks_error(const char *format,...) __attribute__((format(printf
Common error message function for HOST and TGT
int duration
Definition: KSFoundation.h:1135
int min_duration
Definition: KSFoundation.h:1132
int ssi_time
Definition: KSFoundation.h:1134
KS_DESCRIPTION description
Definition: KSFoundation.h:1141

◆ ks_eval_seqctrl_setduration()

STATUS ks_eval_seqctrl_setduration ( KS_SEQ_CONTROL seqctrl,
int  dur 
)

Sets the .duration field of the sequence module (KS_SEQ_CONTROL), while checking that the input value is not less than .min_duration

Parameters
[in,out]seqctrlPointer to KS_SEQ_CONTROL struct for the current sequence module
[in]durDesired duration of the current sequence module (including its SSI time, .ssi_time)
Return values
STATUSSUCCESS or FAILURE
4311  {
4312 #ifndef IPG
4313  /* only do this on HOST */
4314 
4315  if (seqctrl == NULL) {
4316  return ks_error("%s: arg 1 is NULL", __FUNCTION__);
4317  } else if (dur < seqctrl->min_duration) {
4318  return ks_error("%s: duration (arg 2) must be >= min duration (%d", __FUNCTION__, seqctrl->min_duration);
4319  } else {
4320  seqctrl->duration = RUP_GRD(dur);
4321  }
4322 #endif
4323 
4324  return SUCCESS;
4325 }
STATUS ks_error(const char *format,...) __attribute__((format(printf
Common error message function for HOST and TGT
int duration
Definition: KSFoundation.h:1135
int min_duration
Definition: KSFoundation.h:1132

◆ ks_eval_seqcollection_durations_setminimum()

STATUS ks_eval_seqcollection_durations_setminimum ( KS_SEQ_COLLECTION seqcollection)

Sets the .duration fields of all sequence modules being part of the sequence collection (KS_SEQ_COLLECTION) to their .min_duration value

Parameters
[in,out]seqcollectionPointer to KS_SEQ_COLLECTION struct holding pointers to all sequence modules
Return values
STATUSSUCCESS or FAILURE
4329  {
4330 #ifndef IPG
4331  /* only do this on HOST */
4332 
4333  int i;
4334  if (seqcollection != NULL) {
4335  for (i = 0; i < seqcollection->numseq; i++) {
4336  if (seqcollection->seqctrlptr[i]->min_duration % 4) {
4337  return ks_error("%s: Field min_duration for sequence module #%d is not divisible by GRAD_UPDATE_TIME", __FUNCTION__, i);
4338  }
4340  }
4341  }
4342 #endif
4343 
4344  return SUCCESS;
4345 }
STATUS ks_error(const char *format,...) __attribute__((format(printf
Common error message function for HOST and TGT
int32_t i
Definition: KSFoundation_tgt.c:1389
int duration
Definition: KSFoundation.h:1135
KS_SEQ_CONTROL * seqctrlptr[KS_MAXUNIQUE_SEQUENCES]
Definition: KSFoundation.h:1306
int min_duration
Definition: KSFoundation.h:1132
KS_SEQ_COLLECTION seqcollection
Definition: ksepi.e:80
int numseq
Definition: KSFoundation.h:1300

◆ ks_eval_seqcollection_durations_atleastminimum()

STATUS ks_eval_seqcollection_durations_atleastminimum ( KS_SEQ_COLLECTION seqcollection)

Assures the .duration fields of all sequence modules being part of the sequence collection (KS_SEQ_COLLECTION) are at least their .min_duration value

Parameters
[in,out]seqcollectionPointer to KS_SEQ_COLLECTION struct holding pointers to all sequence modules
Return values
STATUSSUCCESS or FAILURE
4348  {
4349 #ifndef IPG
4350  /* only do this on HOST */
4351 
4352  int i;
4353  if (seqcollection != NULL) {
4354  for (i = 0; i < seqcollection->numseq; i++) {
4355  if (seqcollection->seqctrlptr[i]->min_duration % 4) {
4356  return ks_error("%s: Field min_duration for sequence module #%d is not divisible by GRAD_UPDATE_TIME", __FUNCTION__, i);
4357  }
4360  }
4361  }
4362  }
4363 #endif
4364 
4365  return SUCCESS;
4366 }
STATUS ks_error(const char *format,...) __attribute__((format(printf
Common error message function for HOST and TGT
int32_t i
Definition: KSFoundation_tgt.c:1389
int duration
Definition: KSFoundation.h:1135
KS_SEQ_CONTROL * seqctrlptr[KS_MAXUNIQUE_SEQUENCES]
Definition: KSFoundation.h:1306
int min_duration
Definition: KSFoundation.h:1132
KS_SEQ_COLLECTION seqcollection
Definition: ksepi.e:80
int numseq
Definition: KSFoundation.h:1300

◆ ks_eval_gradrflimits()

int ks_eval_gradrflimits ( KS_SAR sar,
KS_SEQ_COLLECTION seqcollection,
float  gheatfact 
)

Returns the time required to play out a certain number of sequence modules over some interval, honoring SAR, RF power and gradient heating constraints

This function uses one KS_SEQ_COLLECTION struct holding the RF and gradient information for the entire psd (involving one or more sequence modules) to check if an interval can be played out as is or if the overall duration needs to be increased to honor SAR and hardware limits. The interval of measurement is given by the function pointer passed to ks_eval_mintr(), which typically is a sliceloop function playing out the slices for one TR (using one or more sequence modules).

The net time for this period is given by the sum of instances and durations of the sequence modules involved. This information is taken from ks_eval_seqcollection_gettotalduration(), which sums the product of the duration of each sequence module by the number of instances of each sequence module.

ks_eval_gradrflimits() will call GE's regular SAR, RF power and gradient heating functions after creating temporary RF_PULSE and GRAD_PULSE structs using the sequence objects (RF and gradients) in the sequence modules.

The gradient information for all sequence modules is accessed via seqcollection->seqctrlptr[i].gradrf.trapptr[...], while the RF information for all sequence modules is accessed via seqcollection->seqctrlptr[i].gradrf.rfptr[...], with i being the internal sequence module index.

GE's hardware and SAR check functions will return new minimum durations, which may be equal or larger than the net time, depending on the hardware and safety limits. The largest new minimum duration is the return value for this function. Also, if the 1st argument (KS_SAR *) is not pointing to NULL, the SAR information will be stored in this struct, so it can be used by the calling function.

Currently, KSFoundation does not use GE's new gradheat method where cornerpoints of the sequence waveforms are used for the gradient heating calculations. When using the old method, the minseq() function will likely be too conservative and may add unnecessary time penalty. The level of exaggeration is not clear, why there is a temporary solution with a 'gheatfact' as an input argument, where it is up to the EPIC programmer to decide on this. This variable should be 1.0 to fully obey the old gradient heating calculations (understanding the minimum TR can bump up significantly). To fully ignore gradient heating calculations a value of 0.0 is used. The proper value of this variable, that would correspond to the actual hardware gradient limits, is not known at the moment, but a value closer to 0.0 than 1.0 better matches the new gradient heating model used by GE's product psds. Having said that, absolutely no guarantees are given in terms of gradient damage or similar.

See also: ks_eval_mintr(), ks_eval_maxslicespertr(), ks_eval_seqcollection_gettotalduration()

Parameters
[out]sarPointer to KS_SAR struct with SAR parameters. If NULL, no attempt to store SAR parameters will be done. If not null, KS_SAR will be set up. In addition, the description of GE's CV 'optr' will be changed so that one can see the limit factor and how much time that had to be added to be within specs. This information can be viewed on the MR system while scanning by DisplayCV and type in 'optr'.
[in]seqcollectionA KS_SEQ_COLLECTION struct, which have undergone a reset of seqctlptr[].nseqinstances followed by a single run of the sequence's slice loop or equivalent period
[in]gheatfactExperimental fudge factor in range [0.0, 1.0] to control how much to obey the gradient heating calculations using the old gradient heating model. It is up to the EPIC programmer to set this to a proper value for now. This input argument can hopefully be removed in the future
Return values
intMinimum time in [us] that is needed to safetly play out the .nseqinstances sequence modules over the intended interval (usually one TR for 2D sequences)
4370  {
4371  int i, j;
4372  int rfindx = 0;
4373  int numuniquetrap = 0;
4374  int numxtrap = 0;
4375  int numytrap = 0;
4376  int numztrap = 0;
4377  int numuniquerf = 0;
4378  int nettime = 0;
4379  int newtime_gradheat = 0;
4380  int newtime_rfamp = 0;
4381  int newtime_sar = 0;
4382  int newtime_allconstraints = 0;
4383  int dummy;
4384  double ave_sar = 0;
4385  double peak_sar = 0;
4386  double cave_sar = 0; /* Coil SAR */
4387  double b1rms = 0;
4388  GRAD_PULSE tempgrad;
4389 
4390  STATUS status;
4391  if (seqcollection == NULL) {
4392  ks_error("%s: 2nd arg is NULL (KS_SEQ_COLLECTION)", __FUNCTION__);
4393  return KS_NOTSET;
4394  } else if (seqcollection->numseq < 1) {
4395  ks_error("%s: No sequence modules in 2nd arg (KS_SEQ_COLLECTION)", __FUNCTION__);
4396  return KS_NOTSET;
4397  }
4398 
4399  /********** copy to common struct arrays for grad and RF from the sequence collection **********/
4400  for (i = 0; i < seqcollection->numseq; i++) {
4401  numuniquetrap += seqcollection->seqctrlptr[i]->gradrf.numtrap;
4402  numuniquerf += seqcollection->seqctrlptr[i]->gradrf.numrf;
4403  }
4404 
4406  if (nettime <= 0) {
4407  return nettime;
4408  }
4409 
4410  GRAD_PULSE *mygradx = (GRAD_PULSE*)alloca(numuniquetrap * sizeof(GRAD_PULSE));
4411  GRAD_PULSE *mygrady = (GRAD_PULSE*)alloca(numuniquetrap * sizeof(GRAD_PULSE));
4412  GRAD_PULSE *mygradz = (GRAD_PULSE*)alloca(numuniquetrap * sizeof(GRAD_PULSE));
4413  RF_PULSE *myrfpulse = (RF_PULSE*)alloca(numuniquerf * sizeof(RF_PULSE));
4414 
4415 
4416  for (i = 0; i < seqcollection->numseq; i++) {
4417 
4418  for (j = 0; j < seqcollection->seqctrlptr[i]->gradrf.numtrap; j++) {
4419 
4420  tempgrad = ks_eval_makegradpulse(seqcollection->seqctrlptr[i]->gradrf.trapptr[j], XGRAD);
4421  if (tempgrad.num > 0) {
4422  /* change from "number of instances per sequence module" to "number of instances per TR"
4423  by multiplying by the number of times this sequence module is played out each TR */
4424  tempgrad.num *= seqcollection->seqctrlptr[i]->nseqinstances;
4425  mygradx[numxtrap++] = tempgrad;
4426  }
4427 
4428  tempgrad = ks_eval_makegradpulse(seqcollection->seqctrlptr[i]->gradrf.trapptr[j], YGRAD);
4429  if (tempgrad.num > 0) {
4430  tempgrad.num *= seqcollection->seqctrlptr[i]->nseqinstances;
4431  mygrady[numytrap++] = tempgrad;
4432  }
4433 
4434  tempgrad = ks_eval_makegradpulse(seqcollection->seqctrlptr[i]->gradrf.trapptr[j], ZGRAD);
4435  if (tempgrad.num > 0) {
4436  tempgrad.num *= seqcollection->seqctrlptr[i]->nseqinstances;
4437  mygradz[numztrap++] = tempgrad;
4438  }
4439 
4440  }
4441 
4442  for (j = 0; j < seqcollection->seqctrlptr[i]->gradrf.numrf; j++) {
4444  myrfpulse[rfindx] = seqcollection->seqctrlptr[i]->gradrf.rfptr[j]->rfpulse;
4445  /* change from "number of instances per sequence module" to "number of instances per TR"
4446  by multiplying by the number of times this sequence module is played out each TR */
4447  myrfpulse[rfindx].num *= seqcollection->seqctrlptr[i]->nseqinstances;
4448  rfindx++;
4449  }
4450 
4451  }
4452 
4453  /********** Gradient heating ***********/
4454 
4455  /* turn gradHeatMethod off until we understand how/if getCornerPoints() work (PGenOnHost()) in minseq.c */
4456  const int saveGradHeatMethod = gradHeatMethod;
4457 
4458  /* gradHeatMethod:
4459  - Off: old traditional gradient heating calcs where minseq() is calling minseqgrad() and minseqcoil()
4460  Warning: minseqcoil() is also calling Safe_DC_Model() if the CV gradDCsafeMethod is 1, which will cause problems for
4461  non-product PSDs. This because of the call to Safe_DC_Model()->predict_gcontirms_fse(), which contains hardcoded indices to gradient
4462  entries for product FSE. To avoid this, we now force gradDCsafeMethod = 0 until KSFoundation PSDs are using PGenOnHost for
4463  gradient heating calcs (gradHeatMethod = 1).
4464  - On: New gradient heating calcs where minseq() is calling minseqseg(). This is not supported yet in KSFoundation, but is indeed the preferred
4465  choice.
4466  */
4467  gradHeatMethod = PSD_OFF;
4468  gradDCsafeMethod = PSD_OFF; /* must be off until PGenOnHost is used (gradHeatMethod = 1) */
4469 
4470  status = minseq(&newtime_gradheat,
4471  mygradx, numxtrap, mygrady, numytrap, mygradz, numztrap,
4472  &loggrd, seqcollection->seqctrlptr[0]->handle.index /* assume 0 is main seq */, /*tsamp*/ GRAD_UPDATE_TIME, nettime,
4473  /*use_ermes*/0, /*debug*/0);
4474 
4475  if (status != SUCCESS) {
4476  ks_error("%s: minseq() failed (gradient heating)", __FUNCTION__);
4477  return KS_NOTSET;
4478  }
4479  gradHeatMethod = saveGradHeatMethod;
4480 
4481  /* Protection against not understood values of minseqcable_t or minseqbusbar_t
4482  which can be -2147483648 (neg INT max). This is nonsense and out of valid CV range (by 1 value)
4483  and leads to download failure. Need to find where minseqcable_t and minseqbusbar_t are set.
4484  */
4485  if (minseqcable_t < 0 || minseqcable_t > 100 * nettime) {
4486  minseqcable_t = 0;
4487  }
4488  if (minseqbusbar_t < 0 || minseqbusbar_t > 100 * nettime) {
4489  minseqbusbar_t = 0;
4490  }
4491 
4492 
4493 
4494  /********** RF amplifier **********/
4495  status = minseqrfamp(&newtime_rfamp, numuniquerf, myrfpulse, L_SCAN);
4496 
4497  if (status != SUCCESS) {
4498  ks_error("%s: minseqrfamp() failed - Please reduce FA", __FUNCTION__);
4499  return KS_NOTSET;
4500  }
4501 
4502  /********** RF SAR **********/
4503 #if EPIC_RELEASE >= 24
4504  status = maxsar(&newtime_sar,
4505  &dummy, &ave_sar, &cave_sar, &peak_sar, &b1rms,
4506  numuniquerf, myrfpulse, L_SCAN, nettime);
4507 #else
4508  status = maxsar(&newtime_sar,
4509  &dummy, &ave_sar, &cave_sar, &peak_sar,
4510  numuniquerf, myrfpulse, L_SCAN, nettime);
4511 #endif
4512 
4513  if (status != SUCCESS) {
4514  ks_error("%s: maxsar() failed", __FUNCTION__);
4515  return KS_NOTSET;
4516  }
4517 
4518  if (sar != NULL) {
4519  sar->average = ave_sar;
4520  sar->coil = cave_sar;
4521  sar->peak = peak_sar;
4522  sar->b1rms = b1rms;
4523  }
4524 
4525  newtime_allconstraints = IMax(4, nettime, (int) (newtime_gradheat * gheatfact), newtime_rfamp, newtime_sar);
4526 
4527  if (sar == NULL) {
4528  /* Change description of optr only if `sar` (1st arg) == NULL (c.f. GEReq_eval_TR()->ks_eval_mintr() )
4529  This is because when we use ks_eval_mintr() we have .duration = .min_duration and hence nettime is equal
4530  to the bare sum of sequence module durations */
4531  char tmpstr[100];
4532 
4533  if (newtime_allconstraints == nettime) {
4534  sprintf(tmpstr, "TR [Not SAR/heat limited (%d)]", nettime);
4535  } else if (newtime_allconstraints == newtime_gradheat) {
4536  sprintf(tmpstr, "TR [Grad. heat limited (%d/%d)]", nettime, newtime_allconstraints);
4537  } else if (newtime_allconstraints == newtime_rfamp) {
4538  sprintf(tmpstr, "TR [RF amp limited (%d/%d)]", nettime, newtime_allconstraints);
4539  } else if (newtime_allconstraints == newtime_sar) {
4540  sprintf(tmpstr, "TR [SAR limited (%d/%d)]", nettime, newtime_allconstraints);
4541  }
4542  cvdesc(optr, tmpstr); /* optr declared as extern int in epic.ext.h included by KSFoundation_private.h */
4543  }
4544 
4545  /* return the maximum value (rounded up to nearest divisible by 8) */
4546  return RUP_GRD(newtime_allconstraints);
4547 
4548 } /* ks_eval_gradrflimits */
int ks_eval_seqcollection_gettotalduration(KS_SEQ_COLLECTION *seqcollection)
Returns the total duration of the sequence collection
Definition: KSFoundation_host.c:4603
STATUS minseqrfamp(INT *Minseqrfamp, const INT numPulses, const RF_PULSE *rfpulse, const INT entry)
int gradHeatMethod
STATUS maxsar(INT *Maxseqsar, INT *Maxslicesar, DOUBLE *Avesar, DOUBLE *Cavesar, DOUBLE *Pksar, DOUBLE *B1rms, const INT numPulses, const RF_PULSE *rfpulse, const INT entry, const INT tr_val)
int gradDCsafeMethod
int minseqbusbar_t
double b1rms
Definition: KSFoundation.h:1319
KS_RF * rfptr[KS_MAXUNIQUE_RF]
Definition: KSFoundation.h:966
#define KS_NOTSET
Definition: KSFoundation.h:103
double average
Definition: KSFoundation.h:1316
RF_PULSE rfpulse
Definition: KSFoundation.h:948
STATUS ks_error(const char *format,...) __attribute__((format(printf
Common error message function for HOST and TGT
int numtrap
Definition: KSFoundation.h:969
double coil
Definition: KSFoundation.h:1317
int32_t i
Definition: KSFoundation_tgt.c:1389
int index
Definition: KSFoundation.h:987
int nseqinstances
Definition: KSFoundation.h:1133
KS_SEQ_CONTROL * seqctrlptr[KS_MAXUNIQUE_SEQUENCES]
Definition: KSFoundation.h:1306
int numrf
Definition: KSFoundation.h:967
GRAD_PULSE ks_eval_makegradpulse(KS_TRAP *trp, int gradchoice)
(Internal use) Creates a GRAD_PULSE struct from a KS_TRAP object
Definition: KSFoundation_host.c:4233
KS_GRADRFCTRL gradrf
Definition: KSFoundation.h:1143
LOG_GRAD loggrd
KS_TRAP * trapptr[KS_MAXUNIQUE_TRAP]
Definition: KSFoundation.h:968
void ks_eval_rf_relink(KS_RF *rf)
(Internal use) Relinks pointers between fields in a KS_RF object
Definition: KSFoundation_host.c:2771
STATUS minseq(INT *p_minseqgrad, GRAD_PULSE *gradx, const INT gx_free, GRAD_PULSE *grady, const INT gy_free, GRAD_PULSE *gradz, const INT gz_free, const LOG_GRAD *log_grad, const INT seq_entry_index, const INT samp_rate, const INT min_tr, const INT e_flag, const INT debug_flag)
KS_SEQ_COLLECTION seqcollection
Definition: ksepi.e:80
KS_SEQ_HANDLE handle
Definition: KSFoundation.h:1142
int minseqcable_t
double peak
Definition: KSFoundation.h:1318
int numseq
Definition: KSFoundation.h:1300

◆ ks_eval_mintr()

int ks_eval_mintr ( int  nslices,
KS_SEQ_COLLECTION seqcollection,
float  gheatfact,
int(*)(int, int, void **)  play_loop,
int  nargs,
void **  args 
)

Returns the minimum TR based on the content of a slice loop, adding additional time due to SAR and hardware constraints if necessary

This function relies on the existence of a slice loop (wrapper) function that can be run on both HOST and TGT. In addition, it needs a KS_SEQ_COLLECTION struct containing references to the sequence modules (KS_SEQ_CONTROL) used in the slice loop. ks_eval_mintr() will call the slice loop, passed in as a function pointer, with nslices as the first argument.

Different psds have different arguments to their slice loop function depending on what is needed to know for the particular sequence, but a standardized argument list of the sliceloop function is required to allow it to be passed in to ks_eval_mintr(). Therefore, a small wrapper function is likely needed for the psd's sliceloop. Moreover, the sliceloop function must return the time in [us] (int) corresponding to the duration to play out the slice loop for nslices number of slices. E.g. if there is a psd specific function

int mysliceloop(int nslices, int ky, int exc, float somecustominfo) {
int time = 0;
for (i = 0; i < nslices; i++) {
time += ks_scan_playsequence(&fatsatseqctrl); // play the fatsat sequence module, and count up fatsatseqctrl.nseqinstances by 1
time += ks_scan_playsequence(&mymainseqctrl); // play the main sequence, and count up mymainseqctrl.nseqinstances by 1
}
return time; // return the duration of the sliceloop given 'nslices' slices.
// This time is used to set the scan clock, while the .nseqinstances and .duration fields of each seqctrl struct
// is used by ks_eval_gradrflimits() and ks_eval_mintr() to determine the total duration. .nseqinstances must be
// known to calculate SAR in order to get the correct number of RF pulses for the slice loop via seqctrl.gradrf.rfptr[]
}

there should be a wrapper function with the following fixed input args:

  1. nslices
  2. nargs (number of extra arguments)
  3. args (pointer to array of (void *) for the nargs extra arguments)

The most important argument is nslices, which must be passed to mysliceloop() so that different times are returned for different nslices. Hence, mysliceloop() must also have nslices as an input argument.

Scenario 1:

If the remaining arguments to mysliceloop() (like ky, exc, somecustominfo) do not affect the sequence timing, the sliceloop wrapper function can be written as (using mysliceloop() above):

int mysliceloop_nargs(int nslices, int nargs, void **args) {
mysliceloop(nslices, 0, 0, 0.0); // args 2-4 are set to 0 since ky, exc, or somecustominfo do not change the sliceloop timing and we will
// only evaluate this on HOST where we are dry-running the sliceloop for timing reasons
}

Scenario 2:

If some of the remaining arguments to mysliceloop() (like ky, exc, somecustominfo) affect the sequence timing, the sliceloop wrapper function can be written as (using mysliceloop() above):

int mysliceloop_nargs(int nslices, int nargs, void **args) {
int ky = 0;
int exc = 0;
float somecustominfo = 0;
if (nargs >= 1 && args[0] != NULL) {
ky = *((int *) args[0]); // cast from (void *) to (int *) and dereference
}
if (nargs >= 2 && args[1] != NULL) {
exc = *((int *) args[1]); // cast from (void *) to (int *) and dereference
}
if (nargs >= 3 && args[2] != NULL) {
somecustominfo = *((float *) args[2]); // cast from (void *) to (float *) and dereference
}
return mysliceloop(nslices, ky, exc, somecustominfo);
}

For scenario #2, nargs and args must be set up before calling ks_eval_mintr(), e.g. like:

void *args[] = {(void *) &ky,
(void *) &exc,
(void *) &somecustominfo}; // pass on the args to mysliceloop() via mysliceloop_nargs()
int nargs = sizeof(args) / sizeof(void *);
ks_eval_mintr(nslices, &seqcollection, mysliceloop_nargs, nargs, args);

Calculation of the minimum TR in three steps using ks_eval_mintr():

  1. Set the .nseqinstances field to 0 for all KS_SEQ_CONTROL structs being a part of the KS_SEQ_COLLECTION using ks_eval_seqcollection_resetninst()
  2. Run the slice loop (wrapper) function for the psd (see above) passed in as a function pointer. This will set the .nseqinstances field for each sequence module equal to the number of playouts of that sequence module in the slice loop
  3. Call ks_eval_gradrflimits(), which will
    • use the .nseqinstances and .duration fields of each sequence module to determine the total net duration for the given number of slices
    • add potential extra time to keep us within SAR and hardware limits

The return value of ks_eval_gradrflimits() is also the return value of ks_eval_mintr()

Parameters
[in]nslicesNumber of slices to play out in the slice loop
[in]seqcollectionA KS_SEQ_COLLECTION struct, which have undergone a reset of seqctlptr[].nseqinstances followed by a single run of the sequence's slice loop or equivalent period
[in]gheatfactExperimental fudge factor in range [0.0, 1.0] to control how much to obey the gradient heating calculations using the old gradient heating model. It is up to the EPIC programmer to set this to a proper value for now. This input argument can hopefully be removed in the future
[in]play_loopFunction pointer to the sliceloop (wrapper) function of the sequence. This function should perform all time consuming tasks for one TR. It is a requirement that this function has three input arguments: int nslices, int nargs, void **args
[in]nargsNumber of extra arguments to the slice loop wrapper function. This can be 0 if the slice loop is only temporally dependent on nslices
[in]argsvoid * pointer array to extra arguments. This can be NULL if the slice loop is temporally dependent only on nslices
Return values
intMinimum time in [us] that is needed to safetly play out the .nseqinstances sequence modules over the intended interval (usually one TR for 2D sequences)
4554  {
4555 
4556  /* must be run before each call to function pointer `play_loop()` to set all `seqctrl.nseqinstances` to 0 */
4558 
4559  play_loop(nslices, nargs, args); /* => seqctrl.nseqinstances = # times each seq. module has been played out */
4560 
4561  return ks_eval_gradrflimits(NULL, seqcollection, gheatfact);
4562 
4563 } /* ks_eval_mintr() */
int ks_eval_gradrflimits(KS_SAR *sar, KS_SEQ_COLLECTION *seqcollection, float gheatfact)
Returns the time required to play out a certain number of sequence modules over some interval...
Definition: KSFoundation_host.c:4370
KS_SEQ_COLLECTION seqcollection
Definition: ksepi.e:80
void ks_eval_seqcollection_resetninst(KS_SEQ_COLLECTION *seqcollection)
Set the .nseqinstances field of each sequence module (KS_SEQ_CONTROL) equal to 0
Definition: KSFoundation_host.c:4590

◆ ks_eval_maxslicespertr()

int ks_eval_maxslicespertr ( int  TR,
KS_SEQ_COLLECTION seqcollection,
float  gheatfact,
int(*)(int, int, void **)  play_loop,
int  nargs,
void **  args 
)

Returns the maximum number of slices that can be played out in one TR, honoring SAR and hardware constraints

The maximum numbers of slices per TR is determined by calling ks_eval_mintr() with an increasing number of slices until the input TR value is reached

Parameters
[in]TRRepetition time in [us]
[in]seqcollectionA KS_SEQ_COLLECTION struct, which have undergone a reset of seqctlptr[].nseqinstances followed by a single run of the sequence's slice loop or equivalent period
[in]gheatfactExperimental fudge factor in range [0.0, 1.0] to control how much to obey the gradient heating calculations using the old gradient heating model. It is up to the EPIC programmer to set this to a proper value for now. This input argument can hopefully be removed in the future
[in]play_loopFunction pointer to the sliceloop (wrapper) function of the sequence. This function should perform all time consuming tasks for one TR. It is a requirement that this function has three input arguments: int nslices, int nargs, void **args
[in]nargsNumber of extra arguments to the slice loop wrapper function. This can be 0 if the slice loop is only temporally dependent on nslices
[in]argsvoid * pointer array to extra arguments. This can be NULL if the slice loop is temporally dependent only on nslices
Return values
intMaximum number of slices that can fit in one TR
4568  {
4569  int max_slquant1 = 0;
4570  int i, acqtime;
4571  for (i = 1; i < 1024; i++) {
4572 
4573  acqtime = ks_eval_mintr(i, seqcollection, gheatfact, play_loop, nargs, args);
4574 
4575  if (acqtime == KS_NOTSET) {
4576  return KS_NOTSET;
4577  }
4578  if (acqtime > TR) {
4579  return max_slquant1;
4580  }
4581  max_slquant1 = i;
4582  }
4583 
4584  return max_slquant1;
4585 }
#define KS_NOTSET
Definition: KSFoundation.h:103
int ks_eval_mintr(int nslices, KS_SEQ_COLLECTION *seqcollection, float gheatfact, int(*play_loop)(int, int, void **), int nargs, void **args)
Returns the minimum TR based on the content of a slice loop, adding additional time due to SAR and ha...
Definition: KSFoundation_host.c:4554
int32_t i
Definition: KSFoundation_tgt.c:1389
KS_SEQ_COLLECTION seqcollection
Definition: ksepi.e:80

◆ ks_eval_seqcollection_resetninst()

void ks_eval_seqcollection_resetninst ( KS_SEQ_COLLECTION seqcollection)

Set the .nseqinstances field of each sequence module (KS_SEQ_CONTROL) equal to 0

This function is called in ks_eval_mintr() and GEReq_eval_TR() to reset the sequence module counters before calling the psd's scanloop function, where the ks_scan_playsequence() functions internally increment .nseqinstances by one every time they are called

Parameters
[in,out]seqcollectionCollection of all sequence modules (KS_SEQ_COLLECTION). C.f. ks_eval_addtoseqcollection()
Returns
void
4590  {
4591  int i;
4592  if (seqcollection != NULL) {
4593  for (i = 0; i < seqcollection->numseq; i++) {
4594 #ifndef IPG
4596 #endif
4597  }
4598  }
4599 
4600 }
int32_t i
Definition: KSFoundation_tgt.c:1389
int nseqinstances
Definition: KSFoundation.h:1133
KS_SEQ_CONTROL * seqctrlptr[KS_MAXUNIQUE_SEQUENCES]
Definition: KSFoundation.h:1306
KS_SEQ_COLLECTION seqcollection
Definition: ksepi.e:80
int numseq
Definition: KSFoundation.h:1300

◆ ks_eval_seqcollection_gettotalduration()

int ks_eval_seqcollection_gettotalduration ( KS_SEQ_COLLECTION seqcollection)

Returns the total duration of the sequence collection

Based on the seqctrl.ninstances and seqctrl.duration fields for each KS_SEQ_CONTROL (sequence module) that have been added to the sequence collection struct (KS_SEQ_COLLECTION), this function returns the total duration of the sequence collection. No time is added to comply with SAR or hardware limits, hence this time may not be possible to use in practice. See ks_eval_mintr(), GEReq_eval_TR(), and ks_eval_gradrflimits() for more details.

Parameters
[in]seqcollectionCollection of all sequence modules (KS_SEQ_COLLECTION). C.f. ks_eval_addtoseqcollection()
Return values
intTime in [us]
4603  {
4604  int i;
4605  int nettime = 0;
4606 
4607  /* duration based on the sequence collection struct */
4608  for (i = 0; i < seqcollection->numseq; i++) {
4609  /*
4610  if (seqcollection->seqctrlptr[i]->duration > 0 && seqcollection->seqctrlptr[i]->nseqinstances == 0) {
4611  ks_error("%s: Sequence module #%d was not played out in sliceloop using ks_scan_playsequence()", __FUNCTION__, i);
4612  return KS_NOTSET;
4613  }
4614  */
4616  }
4617 
4618  if (nettime == 0) {
4619  ks_error("%s: Sum of durations of sequence modules used is 0", __FUNCTION__);
4620  }
4621 
4622  return nettime;
4623 } /* ks_eval_seqcollection_gettotalduration() */
STATUS ks_error(const char *format,...) __attribute__((format(printf
Common error message function for HOST and TGT
int32_t i
Definition: KSFoundation_tgt.c:1389
int duration
Definition: KSFoundation.h:1135
int nseqinstances
Definition: KSFoundation.h:1133
KS_SEQ_CONTROL * seqctrlptr[KS_MAXUNIQUE_SEQUENCES]
Definition: KSFoundation.h:1306
KS_SEQ_COLLECTION seqcollection
Definition: ksepi.e:80
int numseq
Definition: KSFoundation.h:1300

◆ ks_eval_seqcollection_gettotalminduration()

int ks_eval_seqcollection_gettotalminduration ( KS_SEQ_COLLECTION seqcollection)

Returns the total duration of the sequence collection

Based on the seqctrl.ninstances and seqctrl.min_duration fields for each KS_SEQ_CONTROL (sequence module) that have been added to the sequence collection struct (KS_SEQ_COLLECTION), this function returns the total minimum duration possible of the sequence collection, i.e. the time needed to play out the sequence waveforms without any deadtime. See ks_eval_mintr(), GEReq_eval_TR(), and ks_eval_gradrflimits() for more details

Parameters
[in]seqcollectionCollection of all sequence modules (KS_SEQ_COLLECTION). C.f. ks_eval_addtoseqcollection()
Return values
intTime in [us]
4626  {
4627  int i;
4628  int nettime = 0;
4629 
4630  /* duration based on the sequence collection struct */
4631  for (i = 0; i < seqcollection->numseq; i++) {
4632  /*
4633  if (seqcollection->seqctrlptr[i]->min_duration > 0 && seqcollection->seqctrlptr[i]->nseqinstances == 0) {
4634  ks_error("%s: Sequence module #%d was not played out in sliceloop using ks_scan_playsequence()", __FUNCTION__, i);
4635  return KS_NOTSET;
4636  }
4637  */
4639  }
4640 
4641  if (nettime == 0) {
4642  ks_error("%s: Sum of min_durations of sequence modules used is 0", __FUNCTION__);
4643  }
4644 
4645  return nettime;
4646 } /* ks_eval_seqcollection_gettotalminduration() */
STATUS ks_error(const char *format,...) __attribute__((format(printf
Common error message function for HOST and TGT
int32_t i
Definition: KSFoundation_tgt.c:1389
int nseqinstances
Definition: KSFoundation.h:1133
KS_SEQ_CONTROL * seqctrlptr[KS_MAXUNIQUE_SEQUENCES]
Definition: KSFoundation.h:1306
int min_duration
Definition: KSFoundation.h:1132
KS_SEQ_COLLECTION seqcollection
Definition: ksepi.e:80
int numseq
Definition: KSFoundation.h:1300

◆ ks_eval_seqcollection2rfpulse()

STATUS ks_eval_seqcollection2rfpulse ( RF_PULSE *  rfpulse,
KS_SEQ_COLLECTION seqcollection 
)

(Internal use) Sets up GE's rfpulse[] array from the contents of a KS_SEQ_COLLECTION struct

To use GE's maxsar() and maxseqrfamp() functions in ks_eval_gradrflimits() there needs to be an rfpulse[] array containing info about the RF pulses. This info is for standard EPIC psds stored on disk in a file grad_rf_<psdname>.h. KSFoundation EPIC psds with one or more sequence modules (KS_SEQ_CONTROL) have all RF information stored in seqcollection->seqctrlptr[i].gradrf.rfptr[]. This function takes all RF information from all sequence modules in the sequence collection and puts in the proper format in rfpulse[]. Then rfpulse[] can be passed on to maxsar() and maxseqrfamp() (see ks_eval_gradrflimits())

Parameters
[out]rfpulsePointer to rfpulse[] array
[in]seqcollectionPointer to the sequence collection struct for the sequence
Return values
STATUSSUCCESS or FAILURE
4651  {
4652  int i, j, k;
4653 
4654  /* clear the activity bits for all (KS_MAXUNIQUE_RF) available RF pulse slots */
4655  for (i = 0; i < KS_MAXUNIQUE_RF; i++) {
4656  rfpulse[i].activity = 0;
4657  }
4658 
4659  i = 0;
4660  for (k = 0; k < seqcollection->numseq; k++) {
4661 
4662  for (j = 0; j < seqcollection->seqctrlptr[k]->gradrf.numrf; j++) {
4663 
4664  if (i >= KS_MAXUNIQUE_RF) {
4665  return ks_error("%s: too many RF pulses, recompilation is needed with an increased KS_MAXUNIQUE_RF",
4666  __FUNCTION__);
4667  }
4668 
4671  }
4672  }
4673 
4674  return SUCCESS;
4675 
4676 } /* ks_eval_seqcollection2rfpulse */
#define KS_MAXUNIQUE_RF
Definition: KSFoundation.h:187
KS_RF * rfptr[KS_MAXUNIQUE_RF]
Definition: KSFoundation.h:966
RF_PULSE rfpulse[RF_FREE]
Definition: grad_rf_empty.h:55
RF_PULSE rfpulse
Definition: KSFoundation.h:948
STATUS ks_error(const char *format,...) __attribute__((format(printf
Common error message function for HOST and TGT
int32_t i
Definition: KSFoundation_tgt.c:1389
KS_SEQ_CONTROL * seqctrlptr[KS_MAXUNIQUE_SEQUENCES]
Definition: KSFoundation.h:1306
int numrf
Definition: KSFoundation.h:967
KS_GRADRFCTRL gradrf
Definition: KSFoundation.h:1143
void ks_eval_rf_relink(KS_RF *rf)
(Internal use) Relinks pointers between fields in a KS_RF object
Definition: KSFoundation_host.c:2771
KS_SEQ_COLLECTION seqcollection
Definition: ksepi.e:80
int numseq
Definition: KSFoundation.h:1300

◆ ks_calc_nextpow2()

unsigned int ks_calc_nextpow2 ( unsigned int  x)

Gives the next higher 2^N for a given number

Parameters
[in]xInteger (usually related to resolution)
Return values
nextpow2Unsigned integer with a value equal to the nearest larger power-of-2
4684  {
4685  n--;
4686  n |= n >> 1;
4687  n |= n >> 2;
4688  n |= n >> 4;
4689  n |= n >> 8;
4690  n |= n >> 16;
4691  n++;
4692  return n;
4693 }

◆ ks_calc_roundupms()

int ks_calc_roundupms ( int  time)

Rounds up a value in [us] to nearest whole [ms]

Parameters
[in]timeTime in [us]
Return values
newtimeTime in [us], rounded up to next whole [ms] unless already rounded
4696  {
4697  return (((val + 999) / 1000) * 1000);
4698 }

◆ ks_calc_filter()

STATUS ks_calc_filter ( FILTER_INFO *  echo_filter,
int  tsp,
int  duration 
)

Sets up GE's FILTER_INFO struct based on dwell (sample point) time and duration of an acquisition window

This function is used internally by ks_eval_read() as a part of the setup of a KS_READ sequence object, which contains the .filt (FILTER_INFO) field that is used with the corresponding acquisition window in the field .echo of the KS_READ object

ks_calc_filter() will set the field .filt.slot to KS_NOTSET (= -1). A final (global) hardware slot number is set when either setfilter() or GEReq_predownload_setfilter() is called in predownload(). With the assigned slot number, ks_pg_read() in pulsegen() will connect this filter to the acquisition window by internally calling setrfltrs()

Steps for a main sequence written entirely using KSFoundation

Parameters
[out]echo_filterFILTER_INFO struct needed for data acquisition
[in]tspDuration in [us] for each data sample (dwell time) in the acquisition window. Minimum: 2 [us]
[in]durationDuration in [us] of the entire data acquisition window
Return values
STATUSSUCCESS or FAILURE
4701  {
4702 
4703  echortf->decimation = tsp / 2;
4704  echortf->bw = 1.0e3 / (tsp * 2.0);
4705  echortf->tsp = tsp; /* dwell time [us] */
4706  echortf->tdaq = duration;
4707  echortf->outputs = duration / echortf->tsp;
4708  echortf->fslot = KS_NOTSET; /* set this to an unusable value so we are forced to call setfilter() or GEReq_predownload_setfilter() */
4709  echortf->prefills = 0;
4710  echortf->taps = -1;
4711 
4712  if ((floor(echortf->tsp) < echortf->tsp) || (duration % (int) echortf->tsp)) {
4713  return ks_error("ks_calc_filter: tsp must be an integer number and 2nd arg (duration) must be even (divisible by 'tsp'), in units of us");
4714  }
4715  if (echortf->outputs % 2) {
4716  return ks_error("ks_calc_filter: the number of sample outputs must be even");
4717  }
4718  return SUCCESS;
4719 }
#define KS_NOTSET
Definition: KSFoundation.h:103
STATUS ks_error(const char *format,...) __attribute__((format(printf
Common error message function for HOST and TGT

◆ ks_calc_bw2tsp()

int ks_calc_bw2tsp ( float  bw)

Convert receiver bandwidth to dwell time

Parameters
[in]bw+/- bandwidth per FOV in [kHz]. Maximum: 250
Return values
tspDuration in [us] for each data sample (dwell time) in the acquisition window
4722  {
4723  int tsp;
4724  int sysmintsp = 2;
4725 
4726  tsp = ((int) floor((float) HALF_KHZ_USEC / bw / (float) sysmintsp + 0.5)) * sysmintsp;
4727 
4728  if (tsp < sysmintsp)
4729  tsp = sysmintsp;
4730 
4731  return tsp;
4732 }
#define HALF_KHZ_USEC
Definition: KSFoundation.h:171

◆ ks_calc_tsp2bw()

float ks_calc_tsp2bw ( int  tsp)

Convert dwell time to receiver bandwidth

Parameters
[in]tspDuration in [us] for each data sample in the acquisition window. Minimum: 2 [us]
Return values
bw+/- bandwidth per FOV in [kHz]
4734  {
4735 
4736  return ( (float) HALF_KHZ_USEC / (float) tsp );
4737 
4738 }
#define HALF_KHZ_USEC
Definition: KSFoundation.h:171

◆ ks_calc_nearestbw()

float ks_calc_nearestbw ( float  bw)

Round receiver bandwidth to nearest valid value

As the dwell time is an integer and the reciprocal of receiver bandwidth, only certain values of receiver bandwidth are possible. This functions rounds a desired receiver bandwidth to the nearest valid value

Parameters
[in]bw+/- bandwidth per FOV in [kHz]. Maximum: 250
Return values
bwRounded +/- bandwidth per FOV in [kHz]
4740  {
4741  int tsp;
4742  if (bw > 0) {
4743  tsp = (int) ks_calc_bw2tsp(bw); /* convert rBW to dwell time [us] */
4744  return (ks_calc_tsp2bw(tsp)); /* round rBW to exacly match the dwell time */
4745  } else {
4746  return bw;
4747  }
4748 }
float ks_calc_tsp2bw(int tsp)
Convert dwell time to receiver bandwidth
Definition: KSFoundation_host.c:4734
int ks_calc_bw2tsp(float bw)
Convert receiver bandwidth to dwell time
Definition: KSFoundation_host.c:4722

◆ ks_calc_lower_rbw()

float ks_calc_lower_rbw ( float  rbw)
4750  {
4751  if (isNotSet(rbw)) {
4752  return rbw;
4753  }
4754  int tsp = ks_calc_bw2tsp(rbw);
4755  return ks_calc_nearestbw(500.0f / (tsp + 2));
4756 }
float ks_calc_nearestbw(float bw)
Round receiver bandwidth to nearest valid value
Definition: KSFoundation_host.c:4740
int ks_calc_bw2tsp(float bw)
Convert receiver bandwidth to dwell time
Definition: KSFoundation_host.c:4722
#define isNotSet(a)
Definition: KSFoundation.h:124

◆ ks_calc_higher_rbw()

float ks_calc_higher_rbw ( float  rbw)
4758  {
4759  int tsp = ks_calc_bw2tsp(rbw);
4760  if (tsp == 2 || isNotSet(rbw)) {
4761  return rbw;
4762  } else {
4763  return ks_calc_nearestbw(500.0f / (tsp - 2));
4764  }
4765 }
float ks_calc_nearestbw(float bw)
Round receiver bandwidth to nearest valid value
Definition: KSFoundation_host.c:4740
int ks_calc_bw2tsp(float bw)
Convert receiver bandwidth to dwell time
Definition: KSFoundation_host.c:4722
#define isNotSet(a)
Definition: KSFoundation.h:124

◆ ks_calc_max_rbw()

float ks_calc_max_rbw ( float  ampmax,
float  fov 
)

Find the maximum reciever bandwidth based on a max gradient amplitude and FOV

Calculates maximum receiver bandwidth

As the dwell time is an integer and the reciprocal of receiver bandwidth, only certain values of receiver bandwidth are possible. This functions rounds a desired receiver bandwidth to the nearest valid value

Parameters
[in]ampmaxMax gradient amplitude allowed
[in]fovFOV in the readout direction [mm]
Return values
bwRounded +/- bandwidth per FOV in [kHz]

Given a maximum allowed gradient amplitude in [G/cm] and a readout FOV in [mm], the largest rBW time (i.e. smallest dwell time) in [us] is calculated.

Parameters
[in]ampmaxThe maximum allowed gradient amplitude ([G/cm])
[in]fovFOV in the readout direction
Return values
rbwReceiver bandwidth in kHz.
4767  {
4768  float max_rbw = -1.0f;
4769  int dwelltime;
4770  for (dwelltime = 2; max_rbw < 0.0; dwelltime += 2) {
4771  float required_amp = 1.0/(GAM * fov * 0.1 * dwelltime * 0.000001);
4772  if (required_amp <= ampmax) {
4773  max_rbw = 500.0f / ((float) dwelltime);
4774  }
4775  }
4776  return max_rbw;
4777 }
float GAM

◆ ks_calc_trap_time2area()

int ks_calc_trap_time2area ( KS_TRAP trap,
float  area 
)

Calculates the time from start of gradient until area is reached

Parameters
[in]trapKS_TRAP* Trapezoid gradient
[in]areaArea to reach
Return values
timein [us]
4779  {
4780  float ramp_area = trap->amp * trap->ramptime / 2.0;
4781  float plateau_area = trap->amp * trap->plateautime;
4782  float slewrate = trap->amp / trap->ramptime;
4783  int time2area;
4784  if (area < ramp_area) {
4785  /* Target area reached on ramp */
4786  time2area = (int) sqrtf(area * 2.0 / slewrate);
4787  } else if (area < (ramp_area + plateau_area)) {
4788  /* Target area reached on plateau*/
4789  float area_remaining = area - ramp_area;
4790  time2area = trap->ramptime + (area_remaining / trap->amp);
4791  } else {
4792  /* Target area reached on ramp down */
4793  float area_remaining = area - ramp_area - plateau_area;
4794  time2area = (trap->amp - sqrtf(trap->amp*trap->amp - 2*area_remaining*slewrate) ) / slewrate;
4795  }
4796  return time2area;
4797 }
int plateautime
Definition: KSFoundation.h:584
float amp
Definition: KSFoundation.h:581
int ramptime
Definition: KSFoundation.h:583

◆ ks_calc_dixon_times_within_one_period()

int ks_calc_dixon_times_within_one_period ( float  t1,
float  t2,
float  B0 
)
4799  {
4800  float frequency = 42.5759*B0*3.4;
4801  float periods = fabs(t1 - t2) * frequency;
4802  if (periods > 1.0) {
4803  return FAILURE;
4804  } else {
4805  return SUCCESS;
4806  }
4807 }

◆ ks_calc_dixon_fat_nsa_2p()

float ks_calc_dixon_fat_nsa_2p ( float  t1,
float  t2,
float  B0,
float  F 
)
4809  {
4810  return ks_calc_dixon_water_nsa_2p(t1, t2, B0, 1-F);
4811 }
float ks_calc_dixon_water_nsa_2p(float t1, float t2, float B0, float W)
Definition: KSFoundation_host.c:4814

◆ ks_calc_dixon_water_nsa_2p()

float ks_calc_dixon_water_nsa_2p ( float  t1,
float  t2,
float  B0,
float  W 
)
4814  {
4815  float F = 1 - W;
4816  float omega = 2*PI*42.5759*B0*3.4;
4817  float c1 = cos(omega * t1);
4818  float c2 = cos(omega * t2);
4819  float scale = pow((c1 - c2) * (pow(F,2) - pow(W,2)),2); /* No time difference as it will cancel */
4820  float Mw = pow(W,4) * pow(c1,2) +
4821  pow(W,4) * pow(c2,2) +
4822  pow(F*W*c1,2) +
4823  pow(F*W*c2,2) +
4824  2 * pow(F,4) +
4825  2 * pow(F*W,2) +
4826  2 * c1 * (F*pow(W,3) + 2*W*pow(F,3)) +
4827  2 * c2 * (F*pow(W,3) + 2*W*pow(F,3)) +
4828  2 * c1 * c2 * (4*pow(F*W,2) + F*pow(W,3)*(c1 + c2));
4829  return (scale/Mw);
4830 }
STATUS scale(FLOAT(*inrotmat)[9], long(*outrotmat)[9], INT slquant, LOG_GRAD *lgrad, PHYS_GRAD *pgrad, INT contdebug)

◆ ks_calc_dixon_cond_2p()

double ks_calc_dixon_cond_2p ( double  t1,
double  t2,
double  B0 
)
4833  {
4834  double pha1 = 2*PI*42.5759*t1*B0*(1.3-4.7);
4835  double pha2 = 2*PI*42.5759*t2*B0*(1.3-4.7);
4836  double pha_diff = pha2 - pha1;
4837  double real_phasor = cos(pha_diff);
4838  double lambda1 = 2.0 + .5*sqrt(16 - 4*(2 - 2*real_phasor));
4839  double lambda2 = 2.0 - .5*sqrt(16 - 4*(2 - 2*real_phasor));
4840  double cond = lambda1 / (lambda2 + .00000001);
4841  return cond;
4842 }

◆ ks_calc_dixon_mean_nsa_2p_ss_weighted()

float ks_calc_dixon_mean_nsa_2p_ss_weighted ( float  t1,
float  t2,
float  B0,
float  w1,
float  w2 
)
4844  {
4845  /* Normalize scales s.t. w1+w2 = 1 */
4846  const float n = w1 + w2;
4847  w1 = w1 / n;
4848  w2 = w2 / n;
4849 
4850  const float omega = 2*PI*42.5759*B0*3.4; /* 3.4 ppm */
4851  const float c1 = cos(omega * t1);
4852  const float c2 = cos(omega * t2);
4853  /* Return scaled to [0,1] */
4854  return (w1 * w2 * pow(c1 - c2, 2) * (w1*c1*c1 + w2*c2*c2 + w1 + w2) / (2 * (w1 + w2) * (w1*c1*c1 + w2*c2*c2)) );
4855 }

◆ ks_calc_dixon_mean_nsa_2p_ss()

float ks_calc_dixon_mean_nsa_2p_ss ( float  t1,
float  t2,
float  B0 
)
4857  {
4858  const double omega = 2*PI*42.5759*B0*3.4; /* 3.4 ppm */
4859  const double c1 = cos(omega * t1);
4860  const double c2 = cos(omega * t2);
4861  return pow(c1 - c2, 2) * (c1*c1 + c2*c2 + 2) / (4 * (c1*c1 + c2*c2));
4862 }

◆ ks_calc_sliceplan()

STATUS ks_calc_sliceplan ( KS_SLICE_PLAN slice_plan,
int  nslices,
int  slperpass 
)

Calculates the data acquisition order for a standard interleaved 2D scans using one or more passes

This function calls ks_calc_slice_acquisition_order_interleaved() with the last argument being 2, indicating an interleave factor of 2. This will make odd slices in each pass to be acquired first, followed by the even slices.

Parameters
[out]slice_planThe slice plan (KS_SLICE_PLAN) set up earlier using ks_calc_sliceplan() or similar
[in]nslicesTotal number of slices (usually opslquant)
[in]slperpassNumber of slices per pass (i.e. per TR)
Return values
STATUSSUCCESS or FAILURE
4866  {
4867 
4868  return ks_calc_sliceplan_interleaved(slice_plan, nslices, slperpass, 2);
4869 
4870 }
STATUS ks_calc_sliceplan_interleaved(KS_SLICE_PLAN *slice_plan, int nslices, int slperpass, int ninterleaves)
Calculates the data acquisition order for custom interleaved 2D scans using one or more passes...
Definition: KSFoundation_host.c:4872

◆ ks_calc_sliceplan_interleaved()

STATUS ks_calc_sliceplan_interleaved ( KS_SLICE_PLAN slice_plan,
int  nslices,
int  slperpass,
int  inpass_interleaves 
)

Calculates the data acquisition order for custom interleaved 2D scans using one or more passes

The last argument to this function indicates the interleave factor of slices within each pass. If this value is

  • 1: The slices will be acquired in spatial order (within each pass)
  • 2: The odd slices will be acquired first, followed by the even slices in each pass (standard practice)
  • 3: etc.
Parameters
[out]slice_planPointer to the slice plan (KS_SLICE_PLAN) set up earlier using ks_calc_sliceplan() or similar
[in]nslicesTotal number of slices (usually opslquant)
[in]slperpassNumber of slices per pass (i.e. per TR)
[in]inpass_interleavesInterleave factor of slices in a pass
Return values
STATUSSUCCESS or FAILURE
4872  {
4873 
4874  if (!slice_plan) {
4875  return ks_error("%s: invalid input, slice_plan is NULL", __FUNCTION__);
4876  }
4877 
4878  if (nslices <= 0 || slperpass <= 0 || ninterleaves <= 0) {
4879  return ks_error("%s: invalid input (nslices: %d, slperpass: %d, ninterleaves: %d)",
4880  __FUNCTION__, nslices, slperpass, ninterleaves);
4881  }
4882 
4883  slice_plan->nslices = nslices;
4884  slice_plan->npasses = CEIL_DIV(nslices, slperpass);
4885  slice_plan->nslices_per_pass = CEIL_DIV(nslices, slice_plan->npasses);
4886 
4887  int nslicesthispass[slice_plan->npasses];
4888  int sllocthispass[slice_plan->npasses][slperpass];
4889  int i, p, s, k, t, interleaf;
4890  /*
4891  - slice_plan->npasses: a.k.a. number of acquisitions (standard GE CV is: `acqs`)
4892  - slperpass: maximum number of slices in a pass (some passes may have fewer slices)
4893  - nslicesthispass[slice_plan->npasses]: Array where each element indicates how many slices to acquired in this pass
4894  - sllocthispass[slice_plan->npasses][slperpass]: Spatial location index into the prescribed slice stack, i.e. all slices
4895  for current pass and pass_sliceindx (spatially sorted)
4896  */
4897 
4898  if (slice_plan->npasses == slice_plan->nslices) {
4899  /* Sequential scanning, opirmode = 1 (i.e. fill k-space fully for each slice, then change slice). We don't want
4900  to acquire them sequentially in space though to avoid cross talk. Therefore, interleaves are made over the slice stack. */
4901 
4902  p = 0;
4903  for (interleaf = 0; interleaf < ninterleaves; interleaf++) {
4904  /* interleaf: slice interleaves over stack (typically odd/even) */
4905  for (i = 0; i < CEIL_DIV(slice_plan->nslices, ninterleaves); i++) {
4906  k = interleaf + i * ninterleaves;
4907  slice_plan->acq_order[p].slloc = k; /* Spatial location index into the prescribed slice stack */
4908  slice_plan->acq_order[p].slpass = p; /* pass index */
4909  slice_plan->acq_order[p].sltime = 0; /* time index in current pass = 0 */
4910  p++;
4911  }
4912  }
4913 
4914  } else {
4915  /* Standard, interleaved scanning, opirmode = 0. Interleaves are made within passes. */
4916 
4917  for (p = 0; p < slice_plan->npasses; p++) {
4918  nslicesthispass[p] = 0;
4919  for (s = 0; s < slperpass; s++) {
4920  sllocthispass[p][s] = -1;
4921  if (s * slice_plan->npasses + p < nslices) {
4922  nslicesthispass[p]++;
4923  sllocthispass[p][s] = p + (s * slice_plan->npasses);
4924  }
4925  }
4926  }
4927 
4928  for (p = 0; p < slice_plan->npasses; p++) {
4929  t = 0; /* linearly increasing time index in current pass */
4930  for (interleaf = 0; interleaf < ninterleaves; interleaf++) {
4931  /* interleaf: slice interleaves *within* each pass (typically odd/even) */
4932  for (i = 0; i < CEIL_DIV(nslicesthispass[p], ninterleaves); i++) {
4933  k = interleaf + i * ninterleaves; /* pass_sliceindx */
4934  if (k < nslicesthispass[p]) {
4935  slice_plan->acq_order[sllocthispass[p][k]].slloc = sllocthispass[p][k]; /* Spatial location index into the prescribed slice stack */
4936  slice_plan->acq_order[sllocthispass[p][k]].slpass = p; /* pass index */
4937  slice_plan->acq_order[sllocthispass[p][k]].sltime = t++; /* time index in current pass. 0->nslicesthispass[p] */
4938  }
4939  }
4940  }
4941  }
4942 
4943  } /* sequential scanning or not */
4944 
4945 
4946  return SUCCESS;
4947 }
DATA_ACQ_ORDER acq_order[SLICE_FACTOR *DATA_ACQ_MAX]
Definition: KSFoundation.h:1354
STATUS ks_error(const char *format,...) __attribute__((format(printf
Common error message function for HOST and TGT
int32_t i
Definition: KSFoundation_tgt.c:1389
int nslices_per_pass
Definition: KSFoundation.h:1353
int npasses
Definition: KSFoundation.h:1352
int nslices
Definition: KSFoundation.h:1351

◆ ks_calc_sliceplan_sms()

STATUS ks_calc_sliceplan_sms ( KS_SLICE_PLAN slice_plan,
int  nslices,
int  slperpass,
int  multiband_factor 
)

Calculates the data acquisition order for simulaneous-multi-slice (SMS)2D scans

Parameters
[out]slice_planThe slice plan (KS_SLICE_PLAN) set up earlier using ks_calc_sliceplan() or similar
[in]nslicesTotal number of slices (usually opslquant)
[in]slperpassNumber of slices per pass (i.e. per TR)
[in]multiband_factorNumber of SMS slices
Return values
STATUSSUCCESS or FAILURE
4951  {
4952  int slices_per_stack, passes_per_stack;
4953  int i;
4954  STATUS status;
4955 
4956  /* sanitize the input */
4957  multiband_factor = multiband_factor < 1 ? 1 : multiband_factor;
4958  nslices = nslices < 0 ? 1 : nslices;
4959  slperpass = slperpass < 0 ? 1 : slperpass;
4960 
4961  if (slperpass > nslices)
4962  slperpass = nslices;
4963 
4964  /* no SMS?, fall back to standard slice plan */
4965  if (multiband_factor == 1) {
4966  status = ks_calc_sliceplan(slice_plan, nslices, slperpass);
4967  return status;
4968  }
4969 
4970  /* a 'stack' is a group of slices out of the prescribed slices (opslquant), where the #slices in the stack is equal to
4971  opslquant / multiband_factor. The stack does only cover 1/multiband_factor of the entire slice FOV (i.e. not interleaved).
4972  The exact number of slices in each stack is ceil(opslquant/multiband_factor), which may often lead to that more slices than
4973  prescribed need to be acquired for divisibility */
4974  slices_per_stack = CEIL_DIV(nslices, multiband_factor);
4975 
4976  /* number of passes per TR for one slice stack */
4977  passes_per_stack = CEIL_DIV(slices_per_stack, slperpass);
4978 
4979  if (passes_per_stack == 1) {
4980  /* we can fit all slices in the stack in 1 pass (acquisition), i.e. one TR. Do SMS-specific slice interleaving */
4981  ks_calc_slice_acquisition_order_smssingleacq(slice_plan->acq_order, slices_per_stack);
4982  } else {
4983  /* need 2+ acqs to acquire the 'slices_per_stack' slices. Do standard slice interleaving */
4984  ks_calc_sliceplan(slice_plan, slices_per_stack, slperpass);
4985  }
4986 
4987  /* copy to stacks 2->multiband_factor */
4988  slice_plan->nslices = slices_per_stack * multiband_factor;
4989  slice_plan->npasses = passes_per_stack * multiband_factor;
4990 
4991  for (i = slices_per_stack; i < slice_plan->nslices; i++) {
4992  int slice_in_stack = i % slices_per_stack;
4993  int stack = i / slices_per_stack;
4994  slice_plan->acq_order[i].slloc = i;
4995  slice_plan->acq_order[i].slpass = slice_plan->acq_order[slice_in_stack].slpass + stack * passes_per_stack;
4996  slice_plan->acq_order[i].sltime = slice_plan->acq_order[slice_in_stack].sltime;
4997  }
4998 
4999  return SUCCESS;
5000 }
DATA_ACQ_ORDER acq_order[SLICE_FACTOR *DATA_ACQ_MAX]
Definition: KSFoundation.h:1354
int32_t i
Definition: KSFoundation_tgt.c:1389
int npasses
Definition: KSFoundation.h:1352
STATUS ks_calc_sliceplan(KS_SLICE_PLAN *slice_plan, int nslices, int slperpass)
Calculates the data acquisition order for a standard interleaved 2D scans using one or more passes...
Definition: KSFoundation_host.c:4866
int nslices
Definition: KSFoundation.h:1351
int ks_calc_slice_acquisition_order_smssingleacq(DATA_ACQ_ORDER *dacq, int nslices)
Calculates the data acquisition order for single acquisition SMS
Definition: KSFoundation_host.c:5063

◆ ks_calc_sms_min_gap()

int ks_calc_sms_min_gap ( DATA_ACQ_ORDER *  dacq,
int  nslices 
)

Calculates the minimum time gap between adjacent slices in a SMS acquisition

Parameters
[out]dacqData acquisition order array (local)
[in]nslicesTotal number of slices prescribed
Return values
intThe minimum time gap between adjacent slices
5007  {
5008  int min_gap = nslices;
5009 
5010  int i;
5011  for (i=1; i<nslices; i++) {
5012  int gap = abs(dacq[i-1].sltime - dacq[i].sltime);
5013  if (gap < min_gap) {
5014  min_gap = gap;
5015  }
5016  }
5017 
5018  int last_to_first = nslices - dacq[nslices-1].sltime;
5019  if (last_to_first < min_gap) {
5020  min_gap = last_to_first;
5021  }
5022 
5023  return min_gap;
5024 }
int32_t i
Definition: KSFoundation_tgt.c:1389

◆ ks_calc_slice_acquisition_order_smssingleacq_impl()

int ks_calc_slice_acquisition_order_smssingleacq_impl ( DATA_ACQ_ORDER *  dacq,
int  nslices,
int  interleave 
)
5026  {
5027 
5028  const int ngroups = (nslices + interleave -1)/interleave;
5029  const int ngroups_full = nslices%ngroups ? nslices%ngroups : ngroups;
5030 
5031  int g, s, t=0;
5032  for (s=0; s<interleave; ++s) {
5033  for (g=0; g<ngroups_full; ++g) {
5034  int i = g*interleave + s;
5035  dacq[i].slloc = i;
5036  dacq[i].slpass = 0;
5037  dacq[i].sltime = t++;
5038  }
5039 
5040  if (s+1 == interleave) { continue; }
5041 
5042  for (g=ngroups_full; g<ngroups; ++g) {
5043  int i = ngroups_full + g*(interleave-1) + s;
5044  dacq[i].slloc = i;
5045  dacq[i].slpass = 0;
5046  dacq[i].sltime = t++;
5047  }
5048  }
5049 
5050  const int sl_in_last_group = nslices%interleave ? interleave-1 : interleave;
5051  const int sl_pairs_to_switch = (sl_in_last_group-1)/2;
5052  int i;
5053  for(i=1; i<=sl_pairs_to_switch; ++i) {
5054  int tmp;
5055  tmp = dacq[nslices-i].sltime;
5056  dacq[nslices-i].sltime = dacq[nslices-sl_in_last_group+i].sltime;
5057  dacq[nslices-sl_in_last_group+i].sltime = tmp;
5058  }
5059 
5060  return 1;
5061 }
int32_t i
Definition: KSFoundation_tgt.c:1389

◆ ks_calc_slice_acquisition_order_smssingleacq()

int ks_calc_slice_acquisition_order_smssingleacq ( DATA_ACQ_ORDER *  dacq,
int  nslices 
)

Calculates the data acquisition order for single acquisition SMS

Parameters
[out]dacqData acquisition order array (local)
[in]nslicesTotal number of slices prescribed
Return values
intResulting number of passes (or acqs) (in this case 1)
5063  {
5064 
5065  return nslices % 2 ?
5068 }
int ks_calc_slice_acquisition_order_smssingleacq_impl(DATA_ACQ_ORDER *dacq, int nslices, int interleave)
Definition: KSFoundation_host.c:5026

◆ ks_print_seqcollection()

void ks_print_seqcollection ( KS_SEQ_COLLECTION seqcollection,
FILE *  fp 
)

Print out the duration of each sequence module in the sequence collection to a file pointer

Besides printing out the duration of each sequence module, it will also show the number of occurences of each sequence module each TR. However, for this to be correct ks_eval_seqcollection_resetninst() should first be called followed by one call to the sequence's sliceloop function to increment each .seqctrl.nseqinstances field. See e.g. GEReq_eval_checkTR_SAR().

Parameters
[in]seqcollectionCollection of all sequence modules (KS_SEQ_COLLECTION). C.f. ks_eval_addtoseqcollection()
[in]fpFile pointer to file to write to
Returns
void
5078  {
5079  int i;
5080 
5081  /* duration based on the sequence collection struct */
5082  fprintf(fp, "\n----------------------------------------------\n");
5083  fprintf(fp, "Sequence modules in the sequence collection:\n");
5084  for (i = 0; i < seqcollection->numseq; i++) {
5087  }
5088  fprintf(fp, "Sum of durations: %d us\n", ks_eval_seqcollection_gettotalduration(seqcollection));
5089  fprintf(fp, "----------------------------------------------\n\n");
5090  fflush(fp);
5091 
5092  return;
5093 } /* ks_print_seqcollection() */
int ks_eval_seqcollection_gettotalduration(KS_SEQ_COLLECTION *seqcollection)
Returns the total duration of the sequence collection
Definition: KSFoundation_host.c:4603
int32_t i
Definition: KSFoundation_tgt.c:1389
int duration
Definition: KSFoundation.h:1135
int nseqinstances
Definition: KSFoundation.h:1133
KS_SEQ_CONTROL * seqctrlptr[KS_MAXUNIQUE_SEQUENCES]
Definition: KSFoundation.h:1306
KS_SEQ_COLLECTION seqcollection
Definition: ksepi.e:80
KS_DESCRIPTION description
Definition: KSFoundation.h:1141
int numseq
Definition: KSFoundation.h:1300

◆ ks_print_sliceplan()

void ks_print_sliceplan ( const KS_SLICE_PLAN  slice_plan,
FILE *  fp 
)

Writes a KS_SLICE_PLAN to a file pointer

Parameters
slice_planKS_SLICE_PLAN for the sequence
fpA file pointer (or stderr)
Returns
void
5097  {
5098  int i;
5099 
5100  fprintf(fp, "\nSlice Plan:\n");
5101  fprintf(fp, "=============================================\n");
5102  fprintf(fp, "Number of slices: %d\n", slice_plan.nslices);
5103  fprintf(fp, "Number of passes: %d\n", slice_plan.npasses);
5104  fprintf(fp, "Slices per pass: %d\n", slice_plan.nslices_per_pass);
5105  fprintf(fp, "\nslpass\tsltime\tslloc\n");
5106  fprintf(fp, "---------------------------------------------\n");
5107  for (i = 0; i < slice_plan.nslices; i++) { /* i = spatial location */
5108  fprintf(fp, "%03d \t%03d \t%03d\n", slice_plan.acq_order[i].slpass, slice_plan.acq_order[i].sltime, slice_plan.acq_order[i].slloc);
5109  }
5110  fprintf(fp, "=============================================\n\n");
5111 
5112 }
DATA_ACQ_ORDER acq_order[SLICE_FACTOR *DATA_ACQ_MAX]
Definition: KSFoundation.h:1354
int32_t i
Definition: KSFoundation_tgt.c:1389
int nslices_per_pass
Definition: KSFoundation.h:1353
int npasses
Definition: KSFoundation.h:1352
int nslices
Definition: KSFoundation.h:1351

◆ ks_print_scaninfo()

void ks_print_scaninfo ( const SCAN_INFO *  scan_info,
int  nslices,
const char *  desc,
FILE *  fp 
)

Print out the slice location info [mm]

Parameters
[in]scan_infoFile pointer to SCAN_INFO
[in]nslicesNumber of slices prescribed
[in]descDescription of the slice info (or NULL)
[in]fpFile pointer to file to write to
Returns
void
5116  {
5117  int i;
5118 
5119  fprintf(fp, "=============================================\n\n");
5120 
5121  if (desc != NULL) {
5122  fprintf(fp, "\nScan info %s (%d slices):\n", desc, nslices);
5123  } else {
5124  fprintf(fp, "\nScan info (%d slices):\n", nslices);
5125  }
5126  fprintf(fp, "---------------------------------------------\n");
5127  for (i = 0; i < nslices; i++) {
5128  fprintf(fp, "%.3f \t%.3f \t%.3f\n", scan_info[i].oprloc, scan_info[i].opphasoff, scan_info[i].optloc);
5129  }
5130  fprintf(fp, "=============================================\n\n");
5131 
5132 }
int32_t i
Definition: KSFoundation_tgt.c:1389

◆ ks_print_waveform()

void ks_print_waveform ( const KS_WAVEFORM  waveform,
const char *  filename,
int  res 
)

Writes a KS_WAVEFORM to disk with the specified filename

Parameters
[in]waveformA KS_WAVEFORM (i.e. float array) to be written
[in]filenameString with the output file name
[in]resNumber of elements in the waveform to be written
Returns
void
5135  {
5136 
5137  int i;
5138  FILE *asciiWaveFile;
5139  asciiWaveFile = fopen(filename, "w");
5140 
5141  if (asciiWaveFile == NULL) {
5142  return;
5143  }
5144 
5145  for (i = 0; i < res; i++) {
5146  fprintf(asciiWaveFile, "%d %f\n", i, waveform[i]); fflush(asciiWaveFile);
5147  }
5148 
5149  fclose(asciiWaveFile); /* close ascii file */
5150 }
int32_t i
Definition: KSFoundation_tgt.c:1389

◆ ks_print_read()

void ks_print_read ( KS_READ  read,
FILE *  fp 
)

Writes out the contents of a KS_READ sequence object for debugging

If 2nd argument is stderr or stdout, the printed result will be seen in WTools for simulation. On the MR system, stderr will show up in one of the mgd_term windows. A file pointer (opened with e.g. fp = fopen("/usr/g/mrraw/myname.txt","w")) saves a text file to disk

Parameters
[in]readA KS_READ object to be printed
[in]fpA file pointer (or stderr)
Returns
void
5153  {
5154  if (a.description == NULL || a.duration == 0)
5155  return;
5156  fprintf(fp, "%s.duration: %d\n", a.description, a.duration);
5157  fprintf(fp, "%s.rbw: %g\n", a.description, a.rbw);
5158  fprintf(fp, "%s.filt.decimation: %g\n", a.description, a.filt.decimation);
5159  fprintf(fp, "%s.filt.tdaq: %d\n", a.description, a.filt.tdaq);
5160  fprintf(fp, "%s.filt.bw: %g\n", a.description , a.filt.bw);
5161  fprintf(fp, "%s.filt.tsp: %g\n", a.description, a.filt.tsp);
5162  fprintf(fp, "%s.filt.outputs: %d\n", a.description, a.filt.outputs);
5163  fprintf(fp, "%s.filt.prefills: %d\n", a.description, a.filt.prefills);
5164  fprintf(fp, "%s.filt.taps: %d\n", a.description, a.filt.taps);
5165  fflush(fp);
5166 }
float rbw
Definition: KSFoundation.h:734
KS_DESCRIPTION description
Definition: KSFoundation.h:732
int duration
Definition: KSFoundation.h:733
FILTER_INFO filt
Definition: KSFoundation.h:735

◆ ks_print_trap()

void ks_print_trap ( KS_TRAP  trap,
FILE *  fp 
)

Writes out the contents of a KS_TRAP sequence object for debugging

If 2nd argument is stderr or stdout, the printed result will be seen in WTools for simulation. On the MR system, stderr will show up in one of the mgd_term windows. A file pointer (opened with e.g. fp = fopen("/usr/g/mrraw/myname.txt","w")) saves a text file to disk

Parameters
[in]trapA KS_TRAP object to be printed
[in]fpA file pointer (or stderr)
Returns
void
5168  {
5169  if (t.description == NULL || t.duration == 0)
5170  return;
5171  fprintf(fp, "%s.amp: %g\n", t.description, t.amp);
5172  fprintf(fp, "%s.ramptime: %d\n", t.description, t.ramptime);
5173  fprintf(fp, "%s.plateautime: %d\n", t.description, t.plateautime);
5174  fprintf(fp, "%s.duration: %d\n", t.description , t.duration);
5175  fprintf(fp, "%s.area: %g\n", t.description, t.area);
5176  fflush(fp);
5177 }
int plateautime
Definition: KSFoundation.h:584
float area
Definition: KSFoundation.h:582
float amp
Definition: KSFoundation.h:581
KS_DESCRIPTION description
Definition: KSFoundation.h:580
int duration
Definition: KSFoundation.h:585
int ramptime
Definition: KSFoundation.h:583

◆ ks_print_readtrap()

void ks_print_readtrap ( KS_READTRAP  readtrap,
FILE *  fp 
)

Writes out the contents of a KS_READTRAP sequence object for debugging

If 2nd argument is stderr or stdout, the printed result will be seen in WTools for simulation. On the MR system, stderr will show up in one of the mgd_term windows. A file pointer (opened with e.g. fp = fopen("/usr/g/mrraw/myname.txt","w")) saves a text file to disk

Parameters
[in]readtrapA KS_READTRAP object to be printed
[in]fpA file pointer (or stderr)
Returns
void
5179  {
5180  if (r.grad.description == NULL || r.grad.duration == 0)
5181  return;
5182  fprintf(fp, "\nKS_READTRAP (%s):\n", r.grad.description);
5183  fprintf(fp, "fov: %g\n", r.fov);
5184  fprintf(fp, "res: %d\n", r.res);
5185  fprintf(fp, "rampsampling: %d\n", r.rampsampling);
5186  fprintf(fp, "nover: %d\n", r.nover);
5187  fprintf(fp, "acqdelay: %d\n", r.acqdelay);
5188  fprintf(fp, "area2center: %g\n", r.area2center);
5189  fprintf(fp, "time2center: %d\n", r.time2center);
5190  fflush(fp);
5191  ks_print_read(r.acq, fp);
5192  ks_print_trap(r.grad, fp);
5193  if (r.omega.duration > 0) {
5194  ks_print_trap(r.omega, fp);
5195  }
5196 }
void ks_print_trap(KS_TRAP t, FILE *fp)
Writes out the contents of a KS_TRAP sequence object for debugging
Definition: KSFoundation_host.c:5168
float fov
Definition: KSFoundation.h:1576
KS_TRAP grad
Definition: KSFoundation.h:1586
KS_TRAP omega
Definition: KSFoundation.h:1587
int res
Definition: KSFoundation.h:1577
void ks_print_read(KS_READ a, FILE *fp)
Writes out the contents of a KS_READ sequence object for debugging
Definition: KSFoundation_host.c:5153
KS_DESCRIPTION description
Definition: KSFoundation.h:580
int nover
Definition: KSFoundation.h:1579
int rampsampling
Definition: KSFoundation.h:1578
KS_READ acq
Definition: KSFoundation.h:1574
int duration
Definition: KSFoundation.h:585
int time2center
Definition: KSFoundation.h:1585
float area2center
Definition: KSFoundation.h:1584
int acqdelay
Definition: KSFoundation.h:1580

◆ ks_print_phaser()

void ks_print_phaser ( KS_PHASER  phaser,
FILE *  fp 
)

Writes out the contents of a KS_PHASER sequence object for debugging

If 2nd argument is stderr or stdout, the printed result will be seen in WTools for simulation. On the MR system, stderr will show up in one of the mgd_term windows. A file pointer (opened with e.g. fp = fopen("/usr/g/mrraw/myname.txt","w")) saves a text file to disk

Parameters
[in]phaserA KS_PHASER object to be printed
[in]fpA file pointer (or stderr)
Returns
void
5198  {
5199  if (p.grad.description == NULL || p.grad.duration == 0)
5200  return;
5201  fprintf(fp, "\nKS_PHASER (%s):\n", p.grad.description);
5202  fprintf(fp, "fov: %g\n", p.fov);
5203  fprintf(fp, "res: %d\n", p.res);
5204  fprintf(fp, "nover: %d\n", p.nover);
5205  fprintf(fp, "R: %d\n", p.R);
5206  fprintf(fp, "nacslines: %d\n", p.nacslines);
5207  fprintf(fp, "areaoffset: %g\n", p.areaoffset);
5208  fprintf(fp, "numlinestoacq: %d\n", p.numlinestoacq);
5209  fflush(fp);
5210  ks_print_trap(p.grad, fp);
5211 }
int R
Definition: KSFoundation.h:1680
void ks_print_trap(KS_TRAP t, FILE *fp)
Writes out the contents of a KS_TRAP sequence object for debugging
Definition: KSFoundation_host.c:5168
int res
Definition: KSFoundation.h:1678
KS_TRAP grad
Definition: KSFoundation.h:1676
int nacslines
Definition: KSFoundation.h:1681
int numlinestoacq
Definition: KSFoundation.h:1684
float areaoffset
Definition: KSFoundation.h:1683
int nover
Definition: KSFoundation.h:1679
KS_DESCRIPTION description
Definition: KSFoundation.h:580
int duration
Definition: KSFoundation.h:585
float fov
Definition: KSFoundation.h:1677

◆ ks_print_gradrfctrl()

void ks_print_gradrfctrl ( KS_GRADRFCTRL  gradrfctrl,
FILE *  fp 
)

Writes out the contents of KS_GRADRFCTRL for debugging

If 2nd argument is stderr or stdout, the printed result will be seen in WTools for simulation. On the MR system, stderr will show up in one of the mgd_term windows. A file pointer (opened with e.g. fp = fopen("/usr/g/mrraw/myname.txt","w")) saves a text file to disk

Parameters
[in]gradrfctrlA KS_GRADRFCTRL object to be printed
[in]fpA file pointer (or stderr)
Returns
void
5213  {
5214  int i;
5215 
5216  for (i = 0; i < gradrfctrl.numrf; i++) {
5217  fprintf(fp, "RF[%d] '%s': %d times\n", i, gradrfctrl.rfptr[i]->rfwave.description, gradrfctrl.rfptr[i]->rfwave.base.ninst);
5218  }
5219  for (i = 0; i < gradrfctrl.numtrap; i++) {
5220  fprintf(fp, "TRAP[%d] '%s': [%d, %d, %d] times on [X,Y,Z]\n", i, gradrfctrl.trapptr[i]->description, gradrfctrl.trapptr[i]->gradnum[XGRAD], gradrfctrl.trapptr[i]->gradnum[YGRAD], gradrfctrl.trapptr[i]->gradnum[ZGRAD]);
5221  }
5222  for (i = 0; i < gradrfctrl.numwave; i++) {
5223  fprintf(fp, "WAVE[%d] '%s': [%d, %d, %d] times on [X,Y,Z]\n", i, gradrfctrl.waveptr[i]->description, gradrfctrl.waveptr[i]->gradnum[XGRAD], gradrfctrl.waveptr[i]->gradnum[YGRAD], gradrfctrl.waveptr[i]->gradnum[ZGRAD]);
5224  }
5225 
5226 }
KS_RF * rfptr[KS_MAXUNIQUE_RF]
Definition: KSFoundation.h:966
int numtrap
Definition: KSFoundation.h:969
int ninst
Definition: KSFoundation.h:409
int32_t i
Definition: KSFoundation_tgt.c:1389
int numrf
Definition: KSFoundation.h:967
int gradnum[3]
Definition: KSFoundation.h:670
KS_WAVE * waveptr[KS_MAXUNIQUE_WAVE]
Definition: KSFoundation.h:970
KS_DESCRIPTION description
Definition: KSFoundation.h:666
KS_WAVE rfwave
Definition: KSFoundation.h:949
KS_BASE base
Definition: KSFoundation.h:665
KS_TRAP * trapptr[KS_MAXUNIQUE_TRAP]
Definition: KSFoundation.h:968
KS_DESCRIPTION description
Definition: KSFoundation.h:580
int numwave
Definition: KSFoundation.h:971
int gradnum[3]
Definition: KSFoundation.h:586

◆ ks_print_epi()

void ks_print_epi ( KS_EPI  epi,
FILE *  fp 
)

Writes out the contents of a KS_EPI sequence object for debugging

If 2nd argument is stderr or stdout, the printed result will be seen in WTools for simulation. On the MR system, stderr will show up in one of the mgd_term windows. A file pointer (opened with e.g. fp = fopen("/usr/g/mrraw/myname.txt","w")) saves a text file to disk

Parameters
[in]epiA KS_EPI object to be printed
[in]fpA file pointer (or stderr)
Returns
void
5231  {
5232  fprintf(fp, "\nKS_EPI (%s):\n", s.read.grad.description);
5233  fprintf(fp, "etl: %d\n", s.etl);
5234  fprintf(fp, "read_spacing: %.2f [ms]\n", s.read_spacing / 1000.0);
5235  fprintf(fp, "duration: %.2f [ms]\n", s.duration / 1000.0);
5236  fprintf(fp, "time2center: %.2f [ms]\n", s.time2center / 1000.0);
5237  ks_print_readtrap(s.read, fp);
5238  ks_print_trap(s.readphaser, fp);
5239  ks_print_trap(s.blip, fp);
5240  ks_print_phaser(s.blipphaser, fp);
5241 } /* ks_print_epi */
KS_TRAP blip
Definition: KSFoundation.h:1839
void ks_print_trap(KS_TRAP t, FILE *fp)
Writes out the contents of a KS_TRAP sequence object for debugging
Definition: KSFoundation_host.c:5168
KS_PHASER blipphaser
Definition: KSFoundation.h:1840
KS_TRAP grad
Definition: KSFoundation.h:1586
int duration
Definition: KSFoundation.h:1845
int etl
Definition: KSFoundation.h:1843
KS_READTRAP read
Definition: KSFoundation.h:1837
KS_DESCRIPTION description
Definition: KSFoundation.h:580
int time2center
Definition: KSFoundation.h:1846
KS_TRAP readphaser
Definition: KSFoundation.h:1838
void ks_print_readtrap(KS_READTRAP r, FILE *fp)
Writes out the contents of a KS_READTRAP sequence object for debugging
Definition: KSFoundation_host.c:5179
void ks_print_phaser(KS_PHASER p, FILE *fp)
Writes out the contents of a KS_PHASER sequence object for debugging
Definition: KSFoundation_host.c:5198
int read_spacing
Definition: KSFoundation.h:1844

◆ ks_print_rfpulse()

void ks_print_rfpulse ( RF_PULSE  rfpulse,
FILE *  fp 
)

Writes out the contents of an RF_PULSE struct for debugging

If 2nd argument is stderr or stdout, the printed result will be seen in WTools for simulation. On the MR system, stderr will show up in one of the mgd_term windows. A file pointer (opened with e.g. fp = fopen("/usr/g/mrraw/myname.txt","w")) saves a text file to disk

Parameters
[in]rfpulseAn RF_PULSE struct (<rf>.rfpulse) to be printed
[in]fpA file pointer (or stderr)
Returns
void
5246  {
5247  fprintf(fp, " rfpulse.pw: %d\n", *(rfpulse.pw));
5248  fprintf(fp, " rfpulse.amp: %g\n", *(rfpulse.amp));
5249  fprintf(fp, " rfpulse.abswidth: %g\n", rfpulse.abswidth);
5250  fprintf(fp, " rfpulse.effwidth: %g\n", rfpulse.effwidth);
5251  fprintf(fp, " rfpulse.area: %g\n", rfpulse.area);
5252  fprintf(fp, " rfpulse.dtycyc: %g\n", rfpulse.dtycyc);
5253  fprintf(fp, " rfpulse.maxpw: %g\n", rfpulse.maxpw);
5254  fprintf(fp, " rfpulse.num: %d\n", (int)rfpulse.num);
5255  fprintf(fp, " rfpulse.max_b1: %g\n", rfpulse.max_b1);
5256  fprintf(fp, " rfpulse.max_int_b1_sq: %g\n", rfpulse.max_int_b1_sq);
5257  fprintf(fp, " rfpulse.max_rms_b1: %g\n", rfpulse.max_rms_b1);
5258  fprintf(fp, " rfpulse.nom_fa: %g\n", rfpulse.nom_fa);
5259  fprintf(fp, " rfpulse.act_fa: %g\n", *(rfpulse.act_fa));
5260  fprintf(fp, " rfpulse.nom_pw: %g\n", rfpulse.nom_pw);
5261  fprintf(fp, " rfpulse.nom_bw: %g\n", rfpulse.nom_bw);
5262  fprintf(fp, " rfpulse.activity: %d\n", rfpulse.activity);
5263  fprintf(fp, " rfpulse.isodelay: %d\n", rfpulse.isodelay);
5264  fprintf(fp, " rfpulse.scale: %g\n", rfpulse.scale);
5265  fprintf(fp, " rfpulse.res: %d\n", *(rfpulse.res));
5266  fprintf(fp, " rfpulse.extgradfile: %d\n", rfpulse.extgradfile);
5267 
5268  fflush(fp);
5269 
5270 }
RF_PULSE rfpulse[RF_FREE]
Definition: grad_rf_empty.h:55

◆ ks_print_rf()

void ks_print_rf ( KS_RF  rf,
FILE *  fp 
)

Writes out the contents of a KS_RF sequence object for debugging

If 2nd argument is stderr or stdout, the printed result will be seen in WTools for simulation. On the MR system, stderr will show up in one of the mgd_term windows. A file pointer (opened with e.g. fp = fopen("/usr/g/mrraw/myname.txt","w")) saves a text file to disk

Parameters
[in]rfA KS_RF object to be printed
[in]fpA file pointer (or stderr)
Returns
void
5273  {
5274  if (r.rfpulse.activity == 0) {
5275  return;
5276  }
5277 
5278  fprintf(fp, "\nKS_RF (%s):\n", r.rfwave.description);
5279  fprintf(fp, "%s\n", r.designinfo);
5280  switch (r.role) {
5281  case KS_RF_ROLE_NOTSET: fprintf(fp, "role: KS_RF_ROLE_NOTSET\n"); break;
5282  case KS_RF_ROLE_EXC: fprintf(fp, "role: KS_RF_ROLE_EXC\n"); break;
5283  case KS_RF_ROLE_REF: fprintf(fp, "role: KS_RF_ROLE_REF\n"); break;
5284  case KS_RF_ROLE_CHEMSAT: fprintf(fp, "role: KS_RF_ROLE_CHEMSAT\n"); break;
5285  case KS_RF_ROLE_SPSAT: fprintf(fp, "role: KS_RF_ROLE_SPSAT\n"); break;
5286  case KS_RF_ROLE_INV: fprintf(fp, "role: KS_RF_ROLE_INV\n"); break;
5287  }
5288  fprintf(fp, "rfwave.res: %d\n", r.rfwave.res);
5289  fprintf(fp, "rfwave.duration: %d\n", r.rfwave.duration);
5290  fprintf(fp, "amp: %g\n", r.amp);
5291  fprintf(fp, "flip: %g\n", r.flip);
5292  fprintf(fp, "bw: %g\n", r.bw);
5293  fprintf(fp, "cf_offset: %g\n", r.cf_offset);
5294  fprintf(fp, "start2iso: %d\n", r.start2iso);
5295  fprintf(fp, "iso2end: %d\n", r.iso2end);
5296 
5297  ks_print_rfpulse(r.rfpulse, fp);
5298 
5299  if (r.rfwave.res == 0)
5300  fprintf(fp, " rfwave: OFF\n");
5301  else
5302  fprintf(fp, " rfwave: Waveform assigned\n");
5303 
5304  if (r.thetawave.res == 0)
5305  fprintf(fp, " thetawave: OFF\n");
5306  else
5307  fprintf(fp, " thetawave: Waveform assigned\n");
5308 
5309  fflush(fp);
5310 
5311 } /* ks_print_rf */
float cf_offset
Definition: KSFoundation.h:942
int start2iso
Definition: KSFoundation.h:944
KS_DESCRIPTION designinfo
Definition: KSFoundation.h:947
int role
Definition: KSFoundation.h:939
RF_PULSE rfpulse
Definition: KSFoundation.h:948
Definition: KSFoundation.h:1953
Definition: KSFoundation.h:1953
Definition: KSFoundation.h:1953
float flip
Definition: KSFoundation.h:940
KS_WAVE thetawave
Definition: KSFoundation.h:951
KS_DESCRIPTION description
Definition: KSFoundation.h:666
KS_WAVE rfwave
Definition: KSFoundation.h:949
Definition: KSFoundation.h:1953
float amp
Definition: KSFoundation.h:943
int iso2end
Definition: KSFoundation.h:945
Definition: KSFoundation.h:1953
void ks_print_rfpulse(RF_PULSE rfpulse, FILE *fp)
Writes out the contents of an RF_PULSE struct for debugging
Definition: KSFoundation_host.c:5246
int res
Definition: KSFoundation.h:667
Definition: KSFoundation.h:1953
int duration
Definition: KSFoundation.h:668
float bw
Definition: KSFoundation.h:941

◆ ks_print_selrf()

void ks_print_selrf ( KS_SELRF  selrf,
FILE *  fp 
)

Writes out the contents of a KS_SELRF sequence object for debugging

If 2nd argument is stderr or stdout, the printed result will be seen in WTools for simulation. On the MR system, stderr will show up in one of the mgd_term windows. A file pointer (opened with e.g. fp = fopen("/usr/g/mrraw/myname.txt","w")) saves a text file to disk

Parameters
[in]selrfA KS_SELRF object to be printed
[in]fpA file pointer (or stderr)
Returns
void
5316  {
5317 
5318  if (r.rf.rfpulse.activity == 0 || r.rf.rfwave.description == NULL || r.rf.rfwave.duration == 0) {
5319  return;
5320  }
5321 
5322  fprintf(fp, "\nKS_SELRF(%s):\n", r.rf.rfwave.description);
5323 
5324  fprintf(fp, "slthick: %g\n", r.slthick);
5325  if (r.rf.role == KS_RF_ROLE_REF)
5326  fprintf(fp, "crusherscale: %g\n", r.crusherscale);
5327 
5328  ks_print_trap(r.pregrad, fp);
5329  ks_print_trap(r.grad, fp);
5330  if (r.gradwave.res > 0) {
5331  fprintf(fp, " gradwave: Waveform assigned\n");
5332  }
5333  ks_print_trap(r.postgrad, fp);
5334  ks_print_rf(r.rf, fp);
5335 
5336 } /* ks_print_selrf */
KS_TRAP grad
Definition: KSFoundation.h:1491
void ks_print_trap(KS_TRAP t, FILE *fp)
Writes out the contents of a KS_TRAP sequence object for debugging
Definition: KSFoundation_host.c:5168
int role
Definition: KSFoundation.h:939
KS_WAVE gradwave
Definition: KSFoundation.h:1493
RF_PULSE rfpulse
Definition: KSFoundation.h:948
Definition: KSFoundation.h:1953
KS_TRAP pregrad
Definition: KSFoundation.h:1490
KS_RF rf
Definition: KSFoundation.h:1485
KS_DESCRIPTION description
Definition: KSFoundation.h:666
KS_WAVE rfwave
Definition: KSFoundation.h:949
KS_TRAP postgrad
Definition: KSFoundation.h:1492
void ks_print_rf(KS_RF r, FILE *fp)
Writes out the contents of a KS_RF sequence object for debugging
Definition: KSFoundation_host.c:5273
float crusherscale
Definition: KSFoundation.h:1488
int res
Definition: KSFoundation.h:667
int duration
Definition: KSFoundation.h:668
float slthick
Definition: KSFoundation.h:1487

◆ ks_eval_clear_readwave()

int ks_eval_clear_readwave ( KS_READWAVE readwave)
5339  {
5340  KS_READWAVE def_readwave;
5341  ks_init_readwave(&def_readwave);
5342  def_readwave.freqoffHz = readwave->freqoffHz;
5343  def_readwave.fov = readwave->fov;
5344  def_readwave.res = readwave->res;
5345  *readwave = def_readwave;
5346  return SUCCESS;
5347 }
float fov
Definition: KSFoundation.h:1599
float freqoffHz
Definition: KSFoundation.h:1598
void ks_init_readwave(KS_READWAVE *readwave)
Definition: KSFoundation_host.c:104
Definition: KSFoundation.h:1591
int res
Definition: KSFoundation.h:1600

◆ ks_eval_clear_dualreadtrap()

int ks_eval_clear_dualreadtrap ( KS_DIXON_DUALREADTRAP dual_read)
5349  {
5350  ks_eval_clear_readwave(&dual_read->readwave);
5351  KS_DIXON_DUALREADTRAP def_dualread;
5352  ks_init_dixon_dualreadtrap(&def_dualread);
5353  def_dualread.readwave = dual_read->readwave;
5354  def_dualread.available_acq_time = dual_read->available_acq_time;
5355  def_dualread.spare_time_pre = dual_read->spare_time_pre;
5356  def_dualread.spare_time_post = dual_read->spare_time_post;
5357  def_dualread.allowed_overflow_post = dual_read->allowed_overflow_post;
5358  def_dualread.paddingarea = dual_read->paddingarea;
5359  def_dualread.min_nover = dual_read->min_nover;
5360  *dual_read = def_dualread;
5361  return SUCCESS;
5362 }
int available_acq_time
Definition: KSFoundation.h:1866
int spare_time_post
Definition: KSFoundation.h:1868
int allowed_overflow_post
Definition: KSFoundation.h:1869
int ks_eval_clear_readwave(KS_READWAVE *readwave)
Definition: KSFoundation_host.c:5339
KS_READWAVE readwave
Definition: KSFoundation.h:1863
int spare_time_pre
Definition: KSFoundation.h:1867
int min_nover
Definition: KSFoundation.h:1871
void ks_init_dixon_dualreadtrap(KS_DIXON_DUALREADTRAP *dual_readtrap)
Resets a KS_DIXON_DUALREADTRAP sequence object to its default value (KS_INIT_DIXON_DUALREADTRAP)
Definition: KSFoundation_host.c:100
float paddingarea
Definition: KSFoundation.h:1870
Definition: KSFoundation.h:1862

◆ ks_file_exist()

int ks_file_exist ( char *  filename)
5365  {
5366  struct stat buffer;
5367  return (stat (filename, &buffer) == 0);
5368 }

◆ ks_plot_host_slicetime_path()

void ks_plot_host_slicetime_path ( char *  path)
5370  {
5371 #ifdef PSD_HW /* on MR-scanner */
5372  sprintf(path, "/usr/g/mrraw/plot/%s/slicetime/", ks_psdname);
5373 #else /* in Simulation */
5374  sprintf(path, "./plot/slicetime/");
5375 #endif
5376 }
char ks_psdname[256]
Definition: GERequired.e:221

◆ ks_plot_host_slicetime_fullfile()

void ks_plot_host_slicetime_fullfile ( char *  fullfile)
5378  {
5379  char path[250];
5381  sprintf(fullfile, "%s%s_slicetime.json", path, ks_psdname);
5382 }
void ks_plot_host_slicetime_path(char *path)
Definition: KSFoundation_host.c:5370
char ks_psdname[256]
Definition: GERequired.e:221

◆ ks_plot_host_slicetime_delete()

void ks_plot_host_slicetime_delete ( )
5384  {
5385  char fname[500];
5387  remove(fname);
5388 }
void ks_plot_host_slicetime_fullfile(char *fullfile)
Definition: KSFoundation_host.c:5378

◆ ks_plot_host_slicetime_begin()

void ks_plot_host_slicetime_begin ( )
5390  {
5391  if (ks_plot_filefmt == KS_PLOT_OFF) {
5392  return;
5393  }
5394  extern int optr;
5395  extern float pitscan;
5396  char path[250];
5397  char fname[500];
5398  char cmd[300];
5401 
5402  sprintf(cmd, "mkdir -p %s > /dev/null", path);
5403  system(cmd);
5404  /* Reset file */
5405  FILE* fp = fopen(fname, "w");
5406  fprintf(fp,
5407  "{\n"
5408  "\"metadata\": {\n"
5409  "\t\"psdname\": \"%s\",\n"
5410  "\t\"optr\": %d,\n"
5411  "\t\"pitscan\": %.0f\n"
5412  "},\n"
5413  "\"passes\": [ {\n"
5414  "\t\"slicegroups\": [\n\t[{},\n\t",
5415  ks_psdname, optr, pitscan);
5416  fclose(fp);
5417 }
void ks_plot_host_slicetime_fullfile(char *fullfile)
Definition: KSFoundation_host.c:5378
Definition: KSFoundation.h:329
void ks_plot_host_slicetime_path(char *path)
Definition: KSFoundation_host.c:5370
int ks_plot_filefmt
Definition: GERequired.e:245
char ks_psdname[256]
Definition: GERequired.e:221

◆ ks_plot_host_slicetime_endofslicegroup()

void ks_plot_host_slicetime_endofslicegroup ( const char *  desc,
const KS_PLOT_SLICEGROUP_MODE  mode 
)
5420  {
5421  if (ks_plot_filefmt == KS_PLOT_OFF) {
5422  return;
5423  }
5424  char fname[500];
5426  if (! ks_file_exist(fname)) {
5427  return;
5428  }
5429  char modestr[128];
5430  if (mode == KS_PLOT_SG_NOTSET) { strcpy(modestr,"KS_PLOT_SG_NOTSET"); }
5431  else if (mode == KS_PLOT_SG_ACQUISITION) { strcpy(modestr,"KS_PLOT_SG_ACQUISITION"); }
5432  else if (mode == KS_PLOT_SG_DUMMY) { strcpy(modestr,"KS_PLOT_SG_DUMMY"); }
5433  else if (mode == KS_PLOT_SG_CALIBRATION) { strcpy(modestr,"KS_PLOT_SG_CALIBRATION"); }
5434 
5435  FILE* fp = fopen(fname, "r+");
5436  if (fp == NULL) {return;}
5437  fseek(fp, 0, SEEK_END);
5438  fprintf(fp,
5439  "{\n\t"
5440  "\"groupdescription\": \"%s\",\n\t"
5441  "\"mode\": \"%s\"\n\t"
5442  "}", desc == NULL ? "N/A" : desc,
5443  modestr);
5444  fputs("],[\n\t{},\n\t", fp);
5445  fflush(fp);
5446  fclose(fp);
5447 }
void ks_plot_host_slicetime_fullfile(char *fullfile)
Definition: KSFoundation_host.c:5378
Definition: KSFoundation.h:329
int ks_plot_filefmt
Definition: GERequired.e:245
Definition: KSFoundation.h:347
int ks_file_exist(char *filename)
Definition: KSFoundation_host.c:5365
Definition: KSFoundation.h:345
Definition: KSFoundation.h:344
Definition: KSFoundation.h:346

◆ ks_plot_host_slicetime_endofpass()

void ks_plot_host_slicetime_endofpass ( KS_PLOT_PASS_MODE  pass_mode)
5450  {
5451  if (ks_plot_filefmt == KS_PLOT_OFF) {
5452  return;
5453  }
5454  static FILE *fp;
5455  char fname[500];
5457  if (! ks_file_exist(fname)) {
5458  return;
5459  }
5460 
5461  char passmode_str[50];
5462  switch (pass_mode) {
5463  case KS_PLOT_PASS_WAS_DUMMY: strcpy(passmode_str, "KS_PLOT_PASS_WAS_DUMMY"); break;
5464  case KS_PLOT_PASS_WAS_CALIBRATION: strcpy(passmode_str, "KS_PLOT_PASS_WAS_CALIBRATION"); break;
5465  case KS_PLOT_PASS_WAS_STANDARD: strcpy(passmode_str, "KS_PLOT_PASS_WAS_STANDARD"); break;
5466  }
5467 
5468  fp = fopen(fname, "r+");
5469  if (fp == NULL) {return;}
5470  fseek(fp, -9, SEEK_END);
5471  fputs("],\n", fp);
5472  fprintf(fp,
5473  "\t\"passmode\": \"%s\"\n"
5474  "\t},{\n"
5475  "\t\"slicegroups\": [\n\t[{},\n\t", passmode_str);
5476  fflush(fp);
5477  fclose(fp);
5478 }
void ks_plot_host_slicetime_fullfile(char *fullfile)
Definition: KSFoundation_host.c:5378
Definition: KSFoundation.h:329
Definition: KSFoundation.h:339
int ks_plot_filefmt
Definition: GERequired.e:245
int ks_file_exist(char *filename)
Definition: KSFoundation_host.c:5365
Definition: KSFoundation.h:341
Definition: KSFoundation.h:340

◆ ks_plot_host_slicetime()

void ks_plot_host_slicetime ( KS_SEQ_CONTROL ctrl,
int  nslices,
float *  slicepos_mm,
float  slthick_mm,
KS_PLOT_EXCITATION_MODE  excmode 
)
5485  {
5486  if (ks_plot_filefmt == KS_PLOT_OFF || ctrl->duration <= 0) {
5487  return;
5488  }
5489  static FILE *fp;
5490  int i;
5491  char excmode_str[50];
5492  char fname[1000];
5493 
5495  if (! ks_file_exist(fname)) {
5496  return;
5497  }
5498 
5499  switch (excmode) {
5500  case KS_PLOT_STANDARD: strcpy(excmode_str, "KS_PLOT_STANDARD"); break;
5501  case KS_PLOT_NO_EXCITATION: strcpy(excmode_str, "KS_PLOT_NO_EXCITATION"); break;
5502  }
5503 
5504  if (ctrl->duration > 0) {
5505  fp = fopen(fname, "r+");
5506  if (fp == NULL) {return;}
5507  fseek(fp, 0, SEEK_END);
5508  /* fputs(" {\n", fp); */
5509  fprintf(fp,
5510  "{\n"
5511  "\t\t\"description\": \"%s\",\n"
5512  "\t\t\"excitationmode\": \"%s\",\n"
5513  "\t\t\"duration\": %0.3f,\n"
5514  "\t\t\"nseqinstances\": %d,\n"
5515  "\t\t\"minduration\": %0.3f,\n"
5516  "\t\t\"numrf\": %d,\n"
5517  "\t\t\"numtrap\": %d,\n"
5518  "\t\t\"numwave\": %d,\n"
5519  "\t\t\"numacq\": %d,\n"
5520  "\t\t\"slicethickness\": %0.3f,\n"
5521  "\t\t\"slicepos\": [",
5522  ctrl->description, excmode_str, ctrl->duration / 1000.00, ctrl->nseqinstances , ctrl->min_duration / 1000.00, ctrl->gradrf.numrf, ctrl->gradrf.numtrap, ctrl->gradrf.numwave, ctrl->gradrf.numacq, slthick_mm);
5523 
5524  /* SMS slice locations */
5525  for (i = 0; i < (nslices - 1); i++) {
5526  fprintf(fp, "%0.3f, ", slicepos_mm == NULL ? 0.0 : slicepos_mm[i]);
5527  }
5528  fprintf(fp, "%0.3f]\n\t},", slicepos_mm == NULL ? 0.0 : slicepos_mm[nslices - 1]);
5529  fflush(fp);
5530  fclose(fp);
5531  }
5532 
5533 }
void ks_plot_host_slicetime_fullfile(char *fullfile)
Definition: KSFoundation_host.c:5378
Definition: KSFoundation.h:329
int numtrap
Definition: KSFoundation.h:969
int duration
Definition: KSFoundation.h:1135
int nseqinstances
Definition: KSFoundation.h:1133
int numrf
Definition: KSFoundation.h:967
KS_GRADRFCTRL gradrf
Definition: KSFoundation.h:1143
int min_duration
Definition: KSFoundation.h:1132
Definition: KSFoundation.h:336
Definition: KSFoundation.h:335
int ks_plot_filefmt
Definition: GERequired.e:245
int numwave
Definition: KSFoundation.h:971
int ks_file_exist(char *filename)
Definition: KSFoundation_host.c:5365
KS_DESCRIPTION description
Definition: KSFoundation.h:1141
int numacq
Definition: KSFoundation.h:973

◆ ks_plot_host_slicetime_end()

void ks_plot_host_slicetime_end ( )
5536  {
5537  if (ks_plot_filefmt == KS_PLOT_OFF) {
5538  return;
5539  }
5540  char outputdir_uid[250];
5541  char cmd[1000];
5542 
5543  char fname[500];
5545  if (! ks_file_exist(fname)) {
5546  return;
5547  }
5548 
5549  FILE *fp = fopen(fname, "r+");
5550  int fd = fileno(fp);
5551  if (!fp) {return;}
5552  fseek(fp, -28, SEEK_END);
5553  fprintf(fp, "]\n}");
5554  fflush(fp);
5555  ftruncate(fd, ftell(fp));
5556  fclose(fp);
5557 
5558  outputdir_uid[0] = '\0';
5559 #ifdef PSD_HW
5560  /* on MR-scanner */
5561  char outputdir[250];
5562  char pythonpath[] = "/usr/local/bin/apython";
5563  char psdplotpath[] = "/usr/local/bin/psdplot_slicetime.py";
5564  sprintf(outputdir, "/usr/g/mrraw/plot/%s/", ks_psdname);
5565  if (ks_plot_kstmp) {
5566  sprintf(outputdir_uid, "/usr/g/mrraw/kstmp/%010d/plot/", rhkacq_uid);
5567  }
5568 #else
5569  /* in Simulation */
5570  char outputdir[] = "./plot/";
5571  char pythonpath[] = "apython";
5572  char psdplotpath[] = "../KSFoundation/psdplot/psdplot_slicetime.py";
5573 #endif
5574 
5575  sprintf(cmd, "%s %s "
5576  "%s "
5577  "--outputdir %s %s &",
5578  pythonpath, psdplotpath, fname, outputdir, outputdir_uid);
5579  system(cmd);
5580 
5581  return;
5582 }
int ftruncate(int fd, off_t length)
void ks_plot_host_slicetime_fullfile(char *fullfile)
Definition: KSFoundation_host.c:5378
Definition: KSFoundation.h:329
int rhkacq_uid
int ks_plot_kstmp
Definition: GERequired.e:246
int ks_plot_filefmt
Definition: GERequired.e:245
int ks_file_exist(char *filename)
Definition: KSFoundation_host.c:5365
char ks_psdname[256]
Definition: GERequired.e:221

◆ ks_plot_host()

void ks_plot_host ( KS_SEQ_COLLECTION seqcollection,
KS_PHASEENCODING_PLAN plan 
)
5585  {
5586  int i;
5587  for (i = 0; i < seqcollection->numseq; i++) {
5589  }
5590  return;
5591 }
KS_SEQ_CONTROL * seqctrlptr[KS_MAXUNIQUE_SEQUENCES]
Definition: KSFoundation.h:1306
void ks_plot_host_seqctrl(KS_SEQ_CONTROL *ctrl, KS_PHASEENCODING_PLAN *plan)
Definition: KSFoundation_host.c:5594
KS_SEQ_COLLECTION seqcollection
Definition: ksepi.e:80
int numseq
Definition: KSFoundation.h:1300

◆ ks_plot_host_seqctrl()

void ks_plot_host_seqctrl ( KS_SEQ_CONTROL ctrl,
KS_PHASEENCODING_PLAN plan 
)
5594  {
5595  ks_plot_host_seqctrl_manyplans(ctrl, &plan, 1);
5596 }
void ks_plot_host_seqctrl_manyplans(KS_SEQ_CONTROL *ctrl, KS_PHASEENCODING_PLAN **plans, const int num_plans)
Definition: KSFoundation_host.c:5599

◆ ks_plot_host_seqctrl_manyplans()

void ks_plot_host_seqctrl_manyplans ( KS_SEQ_CONTROL ctrl,
KS_PHASEENCODING_PLAN **  plans,
const int  num_plans 
)
5599  {
5600 
5601  char boardc[8] = "xyzrRto"; /* x,y,z,rho1, theta, omega */
5602  char fname[1000];
5603  char cmd[250];
5604  char outputdir_uid[250];
5605  FILE* fp;
5606  int plan_idx, i, j, w;
5607  KS_SEQLOC loc;
5608  KS_PHASEENCODING_PLAN* plan;
5609 
5610  if (ks_plot_filefmt == KS_PLOT_OFF || ctrl->duration == 0 || ctrl->nseqinstances == 0) {
5611  return;
5612  }
5613 
5614  char fileformat_str[10];
5615  if (ks_plot_filefmt == KS_PLOT_MAKEPDF) { strcpy(fileformat_str, "pdf"); }
5616  else if (ks_plot_filefmt == KS_PLOT_MAKEPNG ) { strcpy(fileformat_str, "png"); }
5617  else if (ks_plot_filefmt == KS_PLOT_MAKESVG ) { strcpy(fileformat_str, "svg"); }
5618 
5619  outputdir_uid[0] = '\0';
5620 
5621 #ifdef PSD_HW
5622  /* on MR-scanner */
5623  char path[250];
5624  sprintf(path, "/usr/g/mrraw/plot/%s/host/", ks_psdname);
5625  if (ks_plot_kstmp) {
5626  sprintf(outputdir_uid, "/usr/g/mrraw/kstmp/%010d/plot/", rhkacq_uid);
5627  }
5628  sprintf(cmd, "mkdir -p %s > /dev/null", outputdir_uid);
5629  system(cmd);
5630  char outputdir[250];
5631  sprintf(outputdir, "/usr/g/mrraw/plot/%s/", ks_psdname);
5632  char pythonpath[] = "/usr/local/bin/apython";
5633  char psdplotpath[] = "/usr/local/bin/psdplot.py";
5634 #ifdef IPG
5635  /* on IPG and on scanner, i.e. we are actually scanning. return quietly */
5636  return;
5637 #endif
5638 #else
5639  /* in Simulation */
5640  char path[] = "./plot/host/";
5641  char outputdir[] = "./plot/";
5642  char pythonpath[] = "apython";
5643  char psdplotpath[] = "../KSFoundation/psdplot/psdplot.py";
5644 #endif
5645  sprintf(cmd, "mkdir -p %s > /dev/null", path);
5646  system(cmd);
5647  if (ctrl->duration <= 0) {
5648  return;
5649  }
5650  /* open files */
5651  sprintf(fname, "%spsdplot_%s_%s.json", path, ks_psdname, ctrl->description);
5652  fp = fopen(fname, "w");
5653  fprintf(fp, "{\n");
5654  fprintf(fp, "\"metadata\": {\n");
5655  fprintf(fp, "\t\"mode\": \"host\",\n");
5656  fprintf(fp, "\t\"psdname\": \"%s\",\n", ks_psdname);
5657  fprintf(fp, "\t\"sequence\": \"%s\",\n", ctrl->description);
5658  fprintf(fp, "\t\"duration\": %0.3f,\n", ctrl->duration/1000.0);
5659  fprintf(fp, "\t\"min_duration\": %0.3f,\n", ctrl->min_duration/1000.0);
5660  fprintf(fp, "\t\"ssi_time\": %0.3f,\n", ctrl->ssi_time/1000.0);
5661  fprintf(fp, "\t\"momentstart\": %.3f,\n", ctrl->momentstart/1000.0);
5662  fprintf(fp, "\t\"optr_desc\": \"%s\"\n", _optr.descr); /* optr declared as extern int in epic.ext.h included by KSFoundation_private.h */
5663  fprintf(fp, "},\n");
5664 
5665  /* PHASE ENCODING PLANS */
5666  fprintf(fp, "\"phaseencplans\": [\n");
5667  for (plan_idx = 0; plan_idx < num_plans; plan_idx++) {
5668  plan = plans[plan_idx];
5669  if (plan) {
5670  fprintf(fp, "{\n");
5671  fprintf(fp, "\t\"description\": \"%s\",\n", plan->description);
5672  fprintf(fp, "\t\"num_shots\": %d,\n", plan->num_shots);
5673  fprintf(fp, "\t\"etl\": %d,\n", plan->etl);
5674  fprintf(fp, "\t\"entries\": [\n");
5675  int echo, shot;
5676  for (shot = 0; shot < plan->num_shots; shot++) {
5677  fprintf(fp, "\t\t[");
5678  for (echo = 0; echo < plan->etl; echo++) {
5679  KS_PHASEENCODING_COORD coord = ks_phaseencoding_get(plan, echo, shot);
5680  fprintf(fp, "[%d, %d]", coord.ky, coord.kz);
5681  if (echo < (plan->etl - 1)) {
5682  fprintf(fp, ", ");
5683  }
5684  }
5685  fprintf(fp, "]");
5686  if (shot < (plan->num_shots - 1)) {
5687  fprintf(fp, ",\n");
5688  }
5689  }
5690  fprintf(fp, "]\n");
5691 
5692  if (plan_idx < (num_plans-1)) {
5693  fprintf(fp, "},\n");
5694  } else {
5695  fprintf(fp, "}\n");
5696  }
5697  }
5698  }
5699  fprintf(fp, "],\n");
5700 
5701  /* TRAPEZOIDS */
5702  fprintf(fp, "\"frames\": [\n");
5703  fprintf(fp, "{},\n");
5704  fprintf(fp, "{\n\"trapz\": {\n");
5705  for (i = 0; i < ctrl->gradrf.numtrap; i++) { /* each unique trap object */
5706  KS_TRAP* trap = ctrl->gradrf.trapptr[i];
5707  fprintf(fp, "\t\"%s\": {\n", trap->description);
5708  fprintf(fp, "\t\t\"ramptime\": %0.3f,\n", trap->ramptime / 1000.0);
5709  fprintf(fp, "\t\t\"plateautime\": %0.3f,\n", trap->plateautime / 1000.0);
5710  fprintf(fp, "\t\t\"duration\": %0.3f,\n", trap->duration / 1000.0);
5711  fprintf(fp, "\t\t\"amp\": %0.6f,\n", isnan(trap->amp) ? 0.0f : trap->amp);
5712  fprintf(fp, "\t\t\"instances\": [{\n");
5713 
5714  for (j = 0; j < trap->base.ninst; j++) { /* each instance of the trap */
5715  loc = trap->locs[j];
5716  fprintf(fp, "\t\t\t\"board\": \"%c\",\n", boardc[loc.board]);
5717  fprintf(fp,"\t\t\t\"time\": %0.3f,\n", loc.pos / 1000.0);
5718  fprintf(fp,"\t\t\t\"rtscale\": %0.6f,\n", (trap->rtscale == NULL) ? 1.0f : trap->rtscale[j]);
5719  fprintf(fp,"\t\t\t\"ampscale\": %0.6f\n", loc.ampscale);
5720  if (j == (trap->base.ninst -1)) {
5721  fprintf(fp, "\t\t}]\n");
5722  } else {
5723  fprintf(fp, "\t\t},{\n");
5724  }
5725 
5726  }
5727  if (i == (ctrl->gradrf.numtrap -1)) {
5728  fprintf(fp, "\t}\n");
5729  } else {
5730  fprintf(fp, "\t},\n");
5731  }
5732  }
5733  fprintf(fp, "},\n");
5734 
5735  /* ACQUISITIONS */
5736  fprintf(fp, "\"acquisitions\": [\n");
5737  for (i = 0; i < ctrl->gradrf.numacq; i++) { /* Each unique KS_READ object */
5738  KS_READ* acq = ctrl->gradrf.readptr[i];
5739  fprintf(fp, "\t{\n");
5740  fprintf(fp, "\t\t\"description\": \"%s\",\n", acq->description);
5741  fprintf(fp, "\t\t\"duration\": %0.3f,\n", acq->duration / 1000.0);
5742  fprintf(fp, "\t\t\"rbw\": %0.3f,\n", acq->rbw);
5743  fprintf(fp, "\t\t\"samples\": %d,\n", acq->filt.outputs);
5744  fprintf(fp, "\t\t\"time\": [");
5745  for (j = 0; j < acq->base.ninst; ++j) {
5746  fprintf(fp, "%0.3f", acq->pos[j] / 1000.0);
5747  if (j == (acq->base.ninst -1)) {
5748  fprintf(fp, "]\n");
5749  } else {
5750  fprintf(fp, ", ");
5751  }
5752  }
5753  if (i == (ctrl->gradrf.numacq -1)) {
5754  fprintf(fp, "\t}\n");
5755  } else {
5756  fprintf(fp, "\t},\n");
5757  }
5758  }
5759  fprintf(fp, "],\n");
5760 
5761  /* RF (RHO1) */
5762  fprintf(fp, "\"rf\": {\n");
5763  for (i = 0; i < ctrl->gradrf.numrf; i++) { /* each unique RF object */
5764  KS_RF* rfptr = ctrl->gradrf.rfptr[i];
5765  if (rfptr->rfwave.res > 0) {
5766  char roledesc[50];
5767  if (rfptr->role == KS_RF_ROLE_EXC) {
5768  strcpy(roledesc,"KS_RF_ROLE_EXC");
5769  } else if (rfptr->role == KS_RF_ROLE_REF) {
5770  strcpy(roledesc,"KS_RF_ROLE_REF");
5771  } else if (rfptr->role == KS_RF_ROLE_SPSAT) {
5772  strcpy(roledesc,"KS_RF_ROLE_SPSAT");
5773  } else if (rfptr->role == KS_RF_ROLE_CHEMSAT) {
5774  strcpy(roledesc, "KS_RF_ROLE_CHEMSAT");
5775  } else if (rfptr->role == KS_RF_ROLE_INV) {
5776  strcpy(roledesc, "KS_RF_ROLE_INV");
5777  } else if (rfptr->role == KS_RF_ROLE_SPSAT) {
5778  strcpy(roledesc, "KS_RF_ROLE_SPSAT");
5779  } else {
5780  strcpy(roledesc,"NONE");
5781  }
5782  fprintf(fp, "\t\"%s\": {\n", rfptr->rfwave.description);
5783  fprintf(fp, "\t\t\"description\": \"%s\",\n", rfptr->rfwave.description);
5784  fprintf(fp, "\t\t\"flipangle\": %0.3f,\n", rfptr->flip);
5785  fprintf(fp, "\t\t\"nominal_flipangle\": %0.3f,\n", rfptr->rfpulse.nom_fa);
5786  fprintf(fp, "\t\t\"max_b1\": %0.6f,\n", rfptr->rfpulse.max_b1);
5787  fprintf(fp, "\t\t\"amp\": %0.6f,\n", rfptr->amp);
5788  fprintf(fp, "\t\t\"isodelay\": %0.3f,\n", rfptr->start2iso / 1000.0);
5789  fprintf(fp, "\t\t\"duration\": %0.3f,\n", rfptr->rfwave.duration / 1000.0);
5790  fprintf(fp, "\t\t\"role\": \"%s\",\n", roledesc);
5791 
5792  fprintf(fp, "\t\t\"omega\": {\n");
5793  if (rfptr->omegawave.res > 0) {
5794  fprintf(fp, "\t\t\t\"description\": \"%s\",\n", rfptr->omegawave.description);
5795  fprintf(fp, "\t\t\t\"duration\": %0.3f,\n", rfptr->omegawave.duration / 1000.0);
5796  fprintf(fp, "\t\t\t\"waveform\": [0.0,");
5797  for (w = 0; w < rfptr->omegawave.res; w++) {
5798  fprintf(fp, "%0.6f,", rfptr->omegawave.waveform[w]);
5799  }
5800  fprintf(fp, "0.0],\n");
5801  fprintf(fp, "\t\t\t\"instances\": [{\n");
5802  for (j = 0; j < rfptr->omegawave.base.ninst; j++) {
5803  loc = rfptr->omegawave.locs[j];
5804  fprintf(fp, "\t\t\t\t\"time\": %0.3f,\n", loc.pos/1000.0);
5805  fprintf(fp, "\t\t\t\t\"board\": \"%c\",\n", boardc[loc.board]);
5806  fprintf(fp,"\t\t\t\t\"rtscale\": %0.6f,\n", (rfptr->omegawave.rtscale == NULL) ? 1.0f : rfptr->omegawave.rtscale[j]);
5807  fprintf(fp, "\t\t\t\t\"ampscale\": %0.6f\n", loc.ampscale);
5808  if (j == (rfptr->omegawave.base.ninst -1)) {
5809  fprintf(fp, "\t\t\t}]\n");
5810  } else {
5811  fprintf(fp, "\t\t\t},{\n");
5812  }
5813  }
5814  }
5815  fprintf(fp, "\t\t},\n"); /* Omega */
5816 
5817  fprintf(fp, "\t\t\"theta\": {\n");
5818  if (rfptr->thetawave.res > 0) {
5819  fprintf(fp, "\t\t\t\"description\": \"%s\",\n", rfptr->thetawave.description);
5820  fprintf(fp, "\t\t\t\"duration\": %0.3f,\n", rfptr->thetawave.duration / 1000.0);
5821  fprintf(fp, "\t\t\t\"waveform\": [0.0,");
5822  for (w = 0; w < rfptr->thetawave.res; w++) {
5823  fprintf(fp, "%0.6f,", rfptr->thetawave.waveform[w]);
5824  }
5825  fprintf(fp, "0.0],\n");
5826  fprintf(fp, "\t\t\t\"instances\": [{\n");
5827  for (j = 0; j < rfptr->thetawave.base.ninst; j++) {
5828  loc = rfptr->thetawave.locs[j];
5829  fprintf(fp, "\t\t\t\t\"time\": %0.3f,\n", loc.pos/1000.0);
5830  fprintf(fp, "\t\t\t\t\"board\": \"%c\",\n", boardc[loc.board]);
5831  fprintf(fp,"\t\t\t\t\"rtscale\": %0.6f,\n", (rfptr->omegawave.rtscale == NULL) ? 1.0f : rfptr->omegawave.rtscale[j]);
5832  fprintf(fp, "\t\t\t\t\"ampscale\": %0.6f\n", loc.ampscale);
5833  if (j == (rfptr->thetawave.base.ninst -1)) {
5834  fprintf(fp, "\t\t\t}]\n");
5835  } else {
5836  fprintf(fp, "\t\t\t},{\n");
5837  }
5838  }
5839  }
5840  fprintf(fp, "\t\t},\n"); /* Theta */
5841 
5842  fprintf(fp, "\t\t\"waveform\": [0.0,");
5843  for (w = 0; w <= rfptr->rfwave.res; w++) {
5844  fprintf(fp, "%0.6f,", rfptr->rfwave.waveform[w]);
5845  }
5846  fprintf(fp, "0.0],\n");
5847  fprintf(fp, "\t\t\"instances\": [{\n");
5848  for (j = 0; j < rfptr->rfwave.base.ninst; j++) { /* each instance of the wave (RHO1) */
5849  loc = rfptr->rfwave.locs[j];
5850  fprintf(fp, "\t\t\t\"time\": %0.3f,\n", loc.pos/1000.0);
5851  fprintf(fp, "\t\t\t\"board\": \"%c\",\n", boardc[loc.board]);
5852  fprintf(fp,"\t\t\t\"rtscale\": %0.6f,\n", (rfptr->rfwave.rtscale == NULL) ? 1.0f : rfptr->rfwave.rtscale[j]);
5853  fprintf(fp, "\t\t\t\"ampscale\": %0.6f\n", loc.ampscale);
5854  if (j == (rfptr->rfwave.base.ninst -1)) {
5855  fprintf(fp, "\t\t}]\n");
5856  } else {
5857  fprintf(fp, "\t\t},{\n");
5858  }
5859  }
5860 
5861  if (i == (ctrl->gradrf.numrf -1)) {
5862  fprintf(fp, "\t}\n");
5863  } else {
5864  fprintf(fp, "\t},\n");
5865  }
5866  } /* res > 0 */
5867  } /* rf */
5868  fprintf(fp, "},\n");
5869 
5870  fprintf(fp, "\"waves\": {\n");
5871  /* other (non-RF) waves */
5872  for (i = 0; i < ctrl->gradrf.numwave; i++) {
5873  KS_WAVE* waveptr = ctrl->gradrf.waveptr[i];
5874  if (waveptr->res > 0) {
5875  fprintf(fp, "\t\"%s\": {\n", waveptr->description);
5876  fprintf(fp, "\t\t\"description\": \"%s\",\n", waveptr->description);
5877  fprintf(fp, "\t\t\"duration\": %0.3f,\n", waveptr->duration / 1000.0);
5878  fprintf(fp, "\t\t\"waveform\": [0.0,");
5879  for (w = 0; w < waveptr->res; w++) {
5880  fprintf(fp, "%0.6f,", waveptr->waveform[w]);
5881  }
5882  fprintf(fp, "0.0],\n");
5883 
5884  fprintf(fp, "\t\t\"instances\": [{\n");
5885  for (j = 0; j < waveptr->base.ninst; j++) { /* each instance of the wave */
5886  loc = waveptr->locs[j];
5887  fprintf(fp, "\t\t\t\"board\": \"%c\",\n", boardc[loc.board]);
5888  fprintf(fp, "\t\t\t\"time\": %0.3f,\n", loc.pos/1000.0);
5889  fprintf(fp,"\t\t\t\"rtscale\": %0.6f,\n", (waveptr->rtscale == NULL) ? 1.0f : waveptr->rtscale[j]);
5890  fprintf(fp, "\t\t\t\"ampscale\": %0.6f\n", loc.ampscale);
5891  if (j == (waveptr->base.ninst -1)) {
5892  fprintf(fp, "\t\t}]\n");
5893  } else {
5894  fprintf(fp, "\t\t},{\n");
5895  }
5896  }
5897  if (i == (ctrl->gradrf.numwave -1)) {
5898  fprintf(fp, "\t}\n");
5899  } else {
5900  fprintf(fp, "\t},\n");
5901  }
5902  } /* res > 0 */
5903  }
5904  fprintf(fp, "}\n"); /* End waves */
5905  fprintf(fp, "}\n"); /* End frame */
5906  fprintf(fp, "]\n"); /* End frames */
5907  fprintf(fp, "}"); /* End */
5908  fflush(fp);
5909  fclose(fp);
5910 
5911  {
5912  char cmd[1000];
5913  sprintf(cmd, "%s %s %s "
5914  "--outputdir %s &",
5915  pythonpath, psdplotpath, fname,
5916  outputdir);
5917  ks_dbg("%s", cmd);
5918  system(cmd);
5919  #ifdef PSD_HW
5920 /* on MR-scanner */
5921  if (ks_plot_kstmp) {
5922  sprintf(outputdir_uid, "/usr/g/mrraw/kstmp/%010d/plot/", rhkacq_uid);
5923  sprintf(cmd, "cp %s%s_%s.html %s",
5924  outputdir, ks_psdname, ctrl->description,
5925  outputdir_uid);
5926  ks_dbg("%s", cmd);
5927  system(cmd);
5928  sprintf(cmd, "cp %s*.json %s > /dev/null", path, outputdir_uid);
5929  ks_dbg("%s", cmd);
5930  system(cmd);
5931  }
5932 #endif
5933  }
5934  return;
5935 }
float * rtscale
Definition: KSFoundation.h:675
int plateautime
Definition: KSFoundation.h:584
Core sequence object for making trapezoids on X,Y,Z, and OMEGA boards
Definition: KSFoundation.h:578
int num_shots
Definition: KSFoundation.h:1739
int start2iso
Definition: KSFoundation.h:944
Phase encoding plan for the sequence (2D and 3D)
Definition: KSFoundation.h:1738
KS_BASE base
Definition: KSFoundation.h:579
Definition: KSFoundation.h:329
int pos
Definition: KSFoundation.h:389
KS_RF * rfptr[KS_MAXUNIQUE_RF]
Definition: KSFoundation.h:966
int role
Definition: KSFoundation.h:939
Core sequence object making arbitrary waveforms on any board (using float data format)
Definition: KSFoundation.h:664
Definition: KSFoundation.h:332
RF_PULSE rfpulse
Definition: KSFoundation.h:948
KS_SEQLOC locs[KS_MAXINSTANCES]
Definition: KSFoundation.h:672
Definition: KSFoundation.h:1953
Definition: KSFoundation.h:1953
int numtrap
Definition: KSFoundation.h:969
int ninst
Definition: KSFoundation.h:409
int momentstart
Definition: KSFoundation.h:1136
int ky
Definition: KSFoundation.h:1705
int board
Definition: KSFoundation.h:388
float rbw
Definition: KSFoundation.h:734
int duration
Definition: KSFoundation.h:1135
int nseqinstances
Definition: KSFoundation.h:1133
Definition: KSFoundation.h:1953
int numrf
Definition: KSFoundation.h:967
float ampscale
Definition: KSFoundation.h:390
float flip
Definition: KSFoundation.h:940
int kz
Definition: KSFoundation.h:1706
KS_BASE base
Definition: KSFoundation.h:731
KS_GRADRFCTRL gradrf
Definition: KSFoundation.h:1143
KS_DESCRIPTION description
Definition: KSFoundation.h:732
Composite sequence object for RF (with optional OMEGA & THETA pulses)
Definition: KSFoundation.h:938
KS_WAVE thetawave
Definition: KSFoundation.h:951
Struct holding a 3D k-space phase encoding location (ky,kz)
Definition: KSFoundation.h:1704
KS_PHASEENCODING_COORD ks_phaseencoding_get(const KS_PHASEENCODING_PLAN *phaseenc_plan_ptr, int echo, int shot)
Get [ky,kz] coordinate from KS_PHASEENCODING_PLAN for given echo and shot
Definition: KSFoundation_common.c:375
int rhkacq_uid
int ks_plot_kstmp
Definition: GERequired.e:246
KS_WAVE * waveptr[KS_MAXUNIQUE_WAVE]
Definition: KSFoundation.h:970
KS_DESCRIPTION description
Definition: KSFoundation.h:1741
int etl
Definition: KSFoundation.h:1740
KS_DESCRIPTION description
Definition: KSFoundation.h:666
KS_WAVE rfwave
Definition: KSFoundation.h:949
int min_duration
Definition: KSFoundation.h:1132
KS_BASE base
Definition: KSFoundation.h:665
Core sequence object that handles a data acquisition window
Definition: KSFoundation.h:730
int duration
Definition: KSFoundation.h:733
float amp
Definition: KSFoundation.h:943
float amp
Definition: KSFoundation.h:581
typedef struct used as argument to ks_pg_*** functions to control where and when to place a sequence ...
Definition: KSFoundation.h:387
Definition: KSFoundation.h:330
int ks_plot_filefmt
Definition: GERequired.e:245
KS_SEQLOC locs[KS_MAXINSTANCES]
Definition: KSFoundation.h:589
KS_TRAP * trapptr[KS_MAXUNIQUE_TRAP]
Definition: KSFoundation.h:968
int ssi_time
Definition: KSFoundation.h:1134
KS_DESCRIPTION description
Definition: KSFoundation.h:580
int numwave
Definition: KSFoundation.h:971
char ks_psdname[256]
Definition: GERequired.e:221
Definition: KSFoundation.h:1953
FILTER_INFO filt
Definition: KSFoundation.h:735
Definition: KSFoundation.h:331
KS_WAVE omegawave
Definition: KSFoundation.h:950
STATUS STATUS ks_dbg(const char *format,...) __attribute__((format(printf
Common debug message function for HOST and TGT
KS_DESCRIPTION description
Definition: KSFoundation.h:1141
int duration
Definition: KSFoundation.h:585
KS_READ * readptr[KS_MAXUNIQUE_READ]
Definition: KSFoundation.h:972
int res
Definition: KSFoundation.h:667
float * rtscale
Definition: KSFoundation.h:592
Definition: KSFoundation.h:1953
KS_WAVEFORM waveform
Definition: KSFoundation.h:669
int ramptime
Definition: KSFoundation.h:583
int duration
Definition: KSFoundation.h:668
int numacq
Definition: KSFoundation.h:973
int pos[KS_MAXUNIQUE_READ]
Definition: KSFoundation.h:736

Variable Documentation

◆ cfswgut

int cfswgut

◆ cfswrfut

int cfswrfut

◆ cfsrmode

int cfsrmode

◆ cfgcoiltype

int cfgcoiltype

◆ GAM

float GAM

◆ rhkacq_uid

int rhkacq_uid

◆ ks_plot_filefmt

int ks_plot_filefmt

◆ ks_plot_kstmp

int ks_plot_kstmp

◆ ks_psdname

char ks_psdname[256]

◆ gradHeatMethod

int gradHeatMethod

◆ gradDCsafeMethod

int gradDCsafeMethod

◆ minseqcable_t

int minseqcable_t

◆ minseqbusbar_t

int minseqbusbar_t