KSFoundation  [April-2021]
A platform for simpler EPIC programming on GE MR systems
KSFoundation.h File Reference

Data Structures

struct  KSEPI_DATATAG
 
struct  KS_DATASTORE_STRUCT_DYNAMIC
 
struct  KS_DATASTORE_STRUCT_INFO
 
struct  KS_DATASTORETAG
 
struct  KS_SEQLOC
 
struct  KS_WFINSTANCE
 
struct  KS_BASE
 
struct  KS_WAIT
 
struct  KS_ISIROT
 
struct  KS_TRAP
 
struct  KS_WAVE
 
struct  KS_READ
 
struct  KS_RF
 
struct  KS_GRADRFCTRL
 
struct  KS_SEQ_HANDLE
 
struct  KS_SEQ_CONTROL
 
struct  KS_SEQ_COLLECTION
 
struct  KS_SAR
 
struct  KS_SLICE_PLAN
 
struct  KS_SMS_INFO
 
struct  KS_SELRF
 
struct  KS_READTRAP
 
struct  KS_READWAVE
 
struct  KS_PHASER
 
struct  KS_PHASEENCODING_COORD
 
struct  KS_PHASEENCODING_PLAN
 
struct  KS_EPI
 
struct  KS_DIXON_ASYMREADWAVE
 
struct  KS_DIXON_DUALREADTRAP
 

Macros

#define WARN_UNUSED_RESULT
 
#define DEFAULT_SCAN_INFO_HEAD   0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0
 
#define DEFAULT_SCAN_INFO_TAIL
 
#define DEFAULT_AXIAL_SCAN_INFO
 
#define DEFAULT_AX_SCAN_INFO_FREQ_LR   DEFAULT_AXIAL_SCAN_INFO
 
#define DEFAULT_AX_SCAN_INFO_FREQ_AP
 
#define DEFAULT_SAG_SCAN_INFO_FREQ_SI
 
#define DEFAULT_SAG_SCAN_INFO_FREQ_AP
 
#define DEFAULT_COR_SCAN_INFO_FREQ_SI
 
#define DEFAULT_COR_SCAN_INFO_FREQ_LR
 
#define KS_NOTSET   -1
 
#define KS_INFINITY   (1.0/0.0)
 
#define _cvdesc(cv, string)
 
#define areSame(a, b)   (fabs((a) - (b)) < 1e-5)
 
#define isNotSet(a)   areSame((a), KS_NOTSET)
 
#define isSet(a)   !isNotSet(a)
 
#define KS_ISI_predelay   256
 
#define KS_ISI_time   256
 
#define KS_ISI_rotupdatetime   12
 
#define KS_ISI_postdelay   256
 
#define KS_MINPLATEAUTIME   8
 
#define KS_DEFAULT_SSI_TIME   1500
 
#define KS_DEFAULT_SSI_TIME_MR750w   500
 
#define KS_DEFAULT_SSI_TIME_MR750   1000
 
#define KS_DEFAULT_SSI_TIME_MR450W   500
 
#define KS_DEFAULT_SSI_TIME_MR450   1000
 
#define KS_DEFAULT_SSI_TIME_PREMIER   500
 
#define KS_DEFAULT_SSI_TIME_ICE   200
 
#define KS_DEFAULT_SSI_TIME_OTHERWISE   1500
 
#define KS_RFSSP_PRETIME   64
 
#define KS_RFSSP_POSTTIME   56
 
#define KS_RF_STANDARD_CRUSHERAREA   2000.0
 
#define KS_EPI_MIN_INTERTRAIN_SSPGAP   80
 
#define L_REF   10
 
#define PSD_HRMW_COIL   13
 
#define HAVE_PREMIER_GRADIENTS   (cfgcoiltype == PSD_HRMW_COIL)
 
#define HALF_KHZ_USEC   500.0
 
#define KS_SAVEPFILES   RHXC_AUTO_LOCK
 
#define KS_GERECON   (RHXC_AUTO_DISPLAY+RHXC_XFER_IM)
 
#define KS_3D_SELECTED   (opimode == PSD_3D || opimode == PSD_3DM)
 
#define KS_3D_SINGLESLAB_SELECTED   (opimode == PSD_3D)
 
#define KS_3D_MULTISLAB_SELECTED   (opimode == PSD_3DM)
 
#define KS_DESCRIPTION_LENGTH   128
 
#define KS_MAXWAVELEN   10000
 
#define KS_MAXUNIQUE_RF   20
 
#define KS_MAXUNIQUE_TRAP   200
 
#define KS_MAXUNIQUE_WAVE   200
 
#define KS_MAXUNIQUE_SEQUENCES   200
 
#define KS_MAX_PHASEDYN   2049
 
#define KS_MAXUNIQUE_READ   500
 
#define KS_MAXINSTANCES   500
 
#define KS_EPI_MINBLIPAREA   100
 
#define KS_INITZEROS(n)   {[0 ... (n-1)] = 0}
 
#define KS_INITVALUE(n, a)   {[0 ... (n-1)] = a}
 
#define KS_INIT_DESC   KS_INITZEROS(KS_DESCRIPTION_LENGTH)
 
#define KS_INIT_SEQLOC   {KS_NOTSET, KS_NOTSET, 1.0, 1.0}
 
#define KS_INIT_WFINSTANCE   {NULL, 0, NULL, {KS_INIT_SEQLOC}}
 
#define KS_INIT_WAIT   {{0,0,NULL,sizeof(KS_WAIT)}, KS_INIT_DESC, 0, KS_INITVALUE(KS_MAXINSTANCES, KS_INIT_SEQLOC), NULL, NULL}
 
#define KS_INIT_ISIROT   {KS_INIT_WAIT, KS_INIT_WAIT, NULL, 0, 0, 0, 0}
 
#define KS_INIT_TRAP   {{0,0,NULL,sizeof(KS_TRAP)}, KS_INIT_DESC, 0.0, 0.0, 0, 0, 0, {0, 0, 0}, {0, 0, 0}, {1, 1, 1}, KS_INITVALUE(KS_MAXINSTANCES,KS_INIT_SEQLOC), NULL, NULL, NULL}
 
#define KS_INIT_READ   {{0,0,NULL,sizeof(KS_READ)}, KS_INIT_DESC, 0, KS_NOTSET, {0.0, 0, 0.0, 0.0, KS_NOTSET, 0, 0, 0}, KS_INITVALUE(KS_MAXINSTANCES,KS_NOTSET), 0, NULL}
 
#define KS_INIT_READ_EPI   {{0,0,NULL,sizeof(KS_READ)}, KS_INIT_DESC, 0, 250.0, {0.0, 0, 0.0, 0.0, KS_NOTSET, 0, 0, 0}, KS_INITVALUE(KS_MAXINSTANCES,KS_NOTSET), 0, NULL}
 
#define KS_INIT_READTRAP   {KS_INIT_READ, 240.0, 256, 0, 0, 0, 0.0, 0.0, 0.0, 0, KS_INIT_TRAP, KS_INIT_TRAP}
 
#define KS_INIT_READTRAP_EPI   {KS_INIT_READ_EPI, 240.0, 64, 0, 0, 0, 0.0, 0.0, 0.0, 0, KS_INIT_TRAP, KS_INIT_TRAP}
 
#define KS_INIT_PHASEENCTABLE   KS_INITZEROS(KS_MAX_PHASEDYN)
 
#define KS_INIT_PHASER   {KS_INIT_TRAP, 240.0, KS_NOTSET, 0, 1, 0, 0, 0.0, 0, KS_INIT_PHASEENCTABLE}
 
#define KS_INIT_PHASEENCODING_COORD   {KS_NOTSET, KS_NOTSET}
 
#define KS_INIT_PHASEENCODING_PLAN   {KS_NOTSET, KS_NOTSET, KS_INIT_DESC, NULL, NULL, NULL, FALSE}
 
#define KS_INIT_PHASEENCODING_ALLEPIPLANS   KS_INITVALUE(KS_EPI_PEPLAN_NUMPLANS, KS_INIT_PHASEENCODING_PLAN)
 
#define KS_INIT_PHASEENCODING_PLAN_16PTRS   KS_INITVALUE(16, NULL)
 
#define KS_INIT_EPI   {KS_INIT_READTRAP_EPI, KS_INIT_TRAP, KS_INIT_TRAP, KS_INIT_PHASER, KS_INIT_PHASER, 1, 0, 0, 0, 0, 1.0, KS_EPI_MINBLIPAREA}
 
#define KS_INIT_WAVEVALUE   0
 
#define KS_INIT_WAVEFORM   KS_INITVALUE(KS_MAXWAVELEN, KS_INIT_WAVEVALUE)
 
#define KS_INIT_WAVE   {{0,0,NULL,sizeof(KS_WAVE)}, KS_INIT_DESC, 0, 0, KS_INIT_WAVEFORM, {0,0,0}, KS_NOTSET, KS_INITVALUE(KS_MAXINSTANCES,KS_INIT_SEQLOC), NULL, NULL, NULL}
 
#define KS_INIT_READWAVE   {KS_INITVALUE(3,KS_INIT_WAVE), KS_INIT_WAVE, KS_INIT_WAVE, KS_INIT_READ, KS_INITVALUE(3, 0), KS_INITVALUE(3, 0.0), 0.0, 0.0, 0, 0, 0}
 
#define KS_INIT_DIXON_ASYMREADWAVE   {KS_INIT_READWAVE, KS_INIT_DESC, KS_INITVALUE(2,0), 0, 0.0, 0, 0.0, KS_INITVALUE(8,0.0)}
 
#define KS_INIT_DIXON_DUALREADTRAP   {KS_INIT_READWAVE, KS_INIT_DESC, 0,0,0,0,0.0,0, KS_INITVALUE(2,0),0,0.0,0,0,0,0.0,0.0, KS_INITVALUE(4,0)}
 
#define RX27_APPLY_HADAMARD
 
#define KS_INIT_RFPULSE   {NULL, NULL, 0.0,0.0,0.0,0.0,0.0,0,0.0,0.0,0.0,0.0,NULL,0.0,0.0,0,0,-1,0.0,NULL,0,NULL RX27_APPLY_HADAMARD}
 
#define KS_INIT_RF   {KS_RF_ROLE_NOTSET, KS_NOTSET, KS_NOTSET, 0.0, 0.0, KS_NOTSET, KS_NOTSET, KS_NOTSET, KS_INIT_DESC, KS_INIT_RFPULSE, KS_INIT_WAVE, KS_INIT_WAVE, KS_INIT_WAVE}
 
#define KS_INIT_SELRF   {KS_INIT_RF, KS_NOTSET, 1.0, KS_INIT_TRAP, KS_INIT_TRAP, KS_INIT_TRAP, KS_INIT_WAVE, KS_INIT_SMS_INFO}
 
#define KS_INIT_GRADRFCTRL   {KS_INITZEROS(KS_MAXUNIQUE_RF), 0, KS_INITZEROS(KS_MAXUNIQUE_TRAP), 0, KS_INITZEROS(KS_MAXUNIQUE_WAVE), 0, KS_INITZEROS(KS_MAXUNIQUE_READ), 0, FALSE /* is_cleared_on_tgt */ }
 
#define KS_INIT_SEQ_HANDLE   {NULL, 0, NULL}
 
#define KS_INIT_SEQ_CONTROL   {0, 0, KS_DEFAULT_SSI_TIME, 0, KS_NOTSET, FALSE, KS_INIT_DESC, KS_INIT_SEQ_HANDLE, KS_INIT_GRADRFCTRL}
 
#define KS_INIT_SEQ_COLLECTION   {0, KS_SEQ_COLLECTION_ALLOWNEW, FALSE, KS_INITVALUE(KS_MAXUNIQUE_SEQUENCES, NULL)}
 
#define KS_INIT_SAR   {0, 0, 0, 0}
 
#define KS_INIT_SLICEPLAN   {0,0,0,{{0,0,0}}}
 
#define KS_INIT_SMS_INFO   {1, 0, 0}
 
#define KS_INIT_LOGGRD   {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 }
 
#define KS_INIT_PHYSGRD   { 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 }
 
#define KS_MAT3x3_IDENTITY   {1,0,0,0,1,0,0,0,1}
 
#define KS_MAT4x4_IDENTITY   {1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1}
 
#define KS_CUSTOM_DATASTORE_LENGTH   22
 
#define KSEPI_INIT_DATATAG   {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
 
#define KS_INIT_DATASTORE_STRUCT_DYNAMIC   {-1, -1, -1, -1, -1, {0.0}}
 
#define KS_INIT_DATASTORE_STRUCT_INFO   {TRUE, 0}
 
#define KS_INIT_DATASTORETAG   {KS_INIT_DATASTORE_STRUCT_DYNAMIC, KS_INIT_DATASTORE_STRUCT_INFO, KS_INITZEROS(KS_CUSTOM_DATASTORE_LENGTH), NULL}
 
#define cvset(cv, min, max, def, desc)
 

Typedefs

typedef char KS_DESCRIPTION[KS_DESCRIPTION_LENGTH]
 
typedef float KS_WAVEFORM[KS_MAXWAVELEN]
 
typedef short KS_IWAVE[KS_MAXWAVELEN]
 
typedef double KS_MAT3x3[9]
 
typedef double KS_MAT4x4[16]
 
typedef KS_PHASEENCODING_PLAN KS_PHASEENCODING_ALLEPIPLANS[KS_EPI_PEPLAN_NUMPLANS]
 
typedef KS_PHASEENCODING_PLANKS_PHASEENCODING_PLAN_16PTRS[16]
 

Enumerations

enum  KS_PLOT_FILEFORMATS { KS_PLOT_OFF, KS_PLOT_MAKEPDF, KS_PLOT_MAKESVG, KS_PLOT_MAKEPNG }
 
enum  KS_PLOT_EXCITATION_MODE { KS_PLOT_STANDARD, KS_PLOT_NO_EXCITATION }
 
enum  KS_PLOT_PASS_MODE { KS_PLOT_PASS_WAS_DUMMY, KS_PLOT_PASS_WAS_CALIBRATION, KS_PLOT_PASS_WAS_STANDARD }
 
enum  KS_PLOT_SLICEGROUP_MODE { KS_PLOT_SG_DUMMY, KS_PLOT_SG_CALIBRATION, KS_PLOT_SG_ACQUISITION, KS_PLOT_SG_NOTSET }
 
enum  ks_enum_pf_earlylate_te { KS_PF_EARLY_TE, KS_PF_LATE_TE }
 
enum  ks_enum_sweep_order { KS_SWEEP_ORDER_TOP_DOWN = 0, KS_SWEEP_ORDER_BOTTOM_UP = 1, KS_SWEEP_ORDER_CENTER_OUT = 2, KS_SWEEP_ORDER_OUTSIDE_IN = 3 }
 
enum  ks_snr_mode { KS_SNR_MEAS_OFF = 0, KS_SNR_MEAS_FREQUENCY = 1, KS_SNR_MEAS_PHASE = 2 }
 
enum  ks_enum_trapparts { G_ATTACK, G_PLATEAU, G_DECAY }
 
enum  ks_enum_boarddef {
  KS_X = TYPXGRAD, KS_Y = TYPYGRAD, KS_Z = TYPZGRAD, KS_RHO = TYPRHO1,
  KS_RHO2 = TYPRHO2, KS_THETA = TYPTHETA, KS_OMEGA = TYPOMEGA, KS_SSP = TYPSSP,
  KS_FREQX_PHASEY, KS_FREQY_PHASEX, KS_FREQX_PHASEZ, KS_FREQZ_PHASEX,
  KS_FREQY_PHASEZ, KS_FREQZ_PHASEY, KS_XYZ, KS_ALL
}
 
enum  ks_enum_epiblipsign { KS_EPI_POSBLIPS = 1, KS_EPI_NOBLIPS = 0, KS_EPI_NEGBLIPS = -1 }
 
enum  ks_enum_epiplans {
  KS_EPI_PEPLAN_EARLYTE_BOTTOMUP_UNDERSAMPLED, KS_EPI_PEPLAN_EARLYTE_BOTTOMUP_MULTISHOT, KS_EPI_PEPLAN_EARLYTE_TOPDOWN_UNDERSAMPLED, KS_EPI_PEPLAN_EARLYTE_TOPDOWN_MULTISHOT,
  KS_EPI_PEPLAN_LATETE_BOTTOMUP_UNDERSAMPLED, KS_EPI_PEPLAN_LATETE_BOTTOMUP_MULTISHOT, KS_EPI_PEPLAN_LATETE_TOPDOWN_UNDERSAMPLED, KS_EPI_PEPLAN_LATETE_TOPDOWN_MULTISHOT,
  KS_EPI_PEPLAN_NUMPLANS
}
 
enum  ks_enum_diffusion { KS_EPI_DIFFUSION_OFF, KS_EPI_DIFFUSION_ON }
 
enum  ks_enum_imsize { KS_IMSIZE_NATIVE, KS_IMSIZE_POW2, KS_IMSIZE_MIN256 }
 
enum  ks_enum_datadestination { KS_DONT_SEND = -1, KS_SENDTOBAM = 0, KS_SENDTORTP = 1 }
 
enum  ks_enum_accelmenu { KS_ACCELMENU_FRACT, KS_ACCELMENU_INT }
 
enum  ks_enum_rfrole {
  KS_RF_ROLE_NOTSET, KS_RF_ROLE_EXC, KS_RF_ROLE_REF, KS_RF_ROLE_CHEMSAT,
  KS_RF_ROLE_SPSAT, KS_RF_ROLE_INV
}
 
enum  ks_enum_sincwin {
  KS_RF_SINCWIN_OFF, KS_RF_SINCWIN_HAMMING, KS_RF_SINCWIN_HANNING, KS_RF_SINCWIN_BLACKMAN,
  KS_RF_SINCWIN_BARTLETT
}
 
enum  ks_enum_wavebuf { KS_WF_MAIN, KS_WF_BUF1, KS_WF_BUF2, KS_WF_SIZE }
 
enum  ks_enum_smstype {
  KS_SELRF_SMS_MB, KS_SELRF_SMS_PINS, KS_SELRF_SMS_PINS_DANTE, KS_SELRF_SMS_MULTI_PINS,
  KS_SELRF_SMSTYPE_SIZE
}
 
enum  ks_enum_sms_phase_mod {
  KS_SELRF_SMS_PHAS_MOD_OFF, KS_SELRF_SMS_PHAS_MOD_PHAS, KS_SELRF_SMS_PHAS_MOD_AMPL, KS_SELRF_SMS_PHAS_MOD_QUAD,
  KS_SELRF_SMS_PHAS_MOD_SIZE
}
 
enum  { KS_SEQ_COLLECTION_ALLOWNEW, KS_SEQ_COLLECTION_LOCKED }
 
enum  ks_enum_grad_scaling_policy { KS_GRADWAVE_ABSOLUTE, KS_GRADWAVE_RELATIVE }
 

Functions

int isNaN (float a)
 
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_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) WARN_UNUSED_RESULT
 
STATUS ks_eval_addtoseqcollection (KS_SEQ_COLLECTION *seqcollection, KS_SEQ_CONTROL *seqctrl) WARN_UNUSED_RESULT
 
STATUS ks_eval_seqcollection_isadded (KS_SEQ_COLLECTION *seqcollection, KS_SEQ_CONTROL *seqctrl)
 
STATUS ks_eval_addtraptogradrfctrl (KS_GRADRFCTRL *gradrfctrl, KS_TRAP *trap) WARN_UNUSED_RESULT
 
STATUS ks_eval_addwavetogradrfctrl (KS_GRADRFCTRL *gradrfctrl, KS_WAVE *wave) WARN_UNUSED_RESULT
 
STATUS ks_eval_addrftogradrfctrl (KS_GRADRFCTRL *gradrfctrl, KS_RF *rf) WARN_UNUSED_RESULT
 
STATUS ks_eval_addreadtogradrfctrl (KS_GRADRFCTRL *gradrfctrl, KS_READ *read) WARN_UNUSED_RESULT
 
STATUS ks_eval_wait (KS_WAIT *wait, const char *const desc, int maxduration) WARN_UNUSED_RESULT
 
STATUS ks_eval_isirot (KS_ISIROT *isirot, const char *const desc, int isinumber) WARN_UNUSED_RESULT
 
STATUS ks_eval_read (KS_READ *read, const char *const desc) WARN_UNUSED_RESULT
 
STATUS ks_eval_trap_constrained (KS_TRAP *trap, const char *const desc, float ampmax, float slewrate, int minduration) WARN_UNUSED_RESULT
 
STATUS ks_eval_trap (KS_TRAP *trap, const char *const desc) WARN_UNUSED_RESULT
 
STATUS ks_eval_trap2 (KS_TRAP *trap, const char *const desc) WARN_UNUSED_RESULT
 
STATUS ks_eval_trap1 (KS_TRAP *trap, const char *const desc) WARN_UNUSED_RESULT
 
STATUS ks_eval_trap_constrained_time_maxarea (KS_TRAP *trap, const char *const desc, float ampmax, float slewrate, float maxarea, int derate_slewrate) WARN_UNUSED_RESULT
 
STATUS ks_eval_set_asym_padding (KS_READTRAP *readtrap, float wanted_paddingarea_pre, float wanted_paddingarea_post) WARN_UNUSED_RESULT
 
STATUS ks_eval_trap1p (KS_TRAP *trap, const char *const desc) WARN_UNUSED_RESULT
 
STATUS ks_eval_readtrap_constrained (KS_READTRAP *readtrap, const char *const desc, float ampmax, float slewrate) WARN_UNUSED_RESULT
 
STATUS ks_eval_readtrap (KS_READTRAP *readtrap, const char *const desc) WARN_UNUSED_RESULT
 
STATUS ks_eval_readtrap2 (KS_READTRAP *readtrap, const char *const desc) WARN_UNUSED_RESULT
 
STATUS ks_eval_readtrap1 (KS_READTRAP *readtrap, const char *const desc) WARN_UNUSED_RESULT
 
STATUS ks_eval_readtrap_constrained_sample_duration (KS_READTRAP *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) WARN_UNUSED_RESULT
 
STATUS ks_eval_dixon_wang (struct _dixon_dualreadtrap_s *dual_read, const char *const desc, const float PF, float M)
 
STATUS ks_eval_dixon_dualreadtrap (struct _dixon_dualreadtrap_s *dual_read, const char *const desc, int flip)
 
STATUS ks_verify_dualreadtrap_setup (struct _dixon_dualreadtrap_s *dual_read)
 
STATUS ks_eval_dixon_dualreadtrap_slim (struct _dixon_dualreadtrap_s *dual_read, const char *const desc, int flip)
 
STATUS ks_eval_dixon_asymtriangle_padded (struct _dixon_asymreadwave_s *asym_readwave, const char *const desc, const int tas, const int time2center, const float area_pre, const float area_acq, const float fov, const int duration) WARN_UNUSED_RESULT
 
STATUS ks_eval_dixon_asymspline_padded (struct _dixon_asymreadwave_s *asym_readwave, const char *const desc, const int tas, const int time2center, const int tae, const float Apre, const float Aacq, const float Apost, const float fov, const int duration) WARN_UNUSED_RESULT
 
STATUS ks_eval_dixon_asymspline (struct _dixon_asymreadwave_s *asym_readwave, const char *const desc, const int time2center, const float area, const float fov, const int duration, const float paddingarea) WARN_UNUSED_RESULT
 
STATUS ks_eval_dixon_asymtriangle (struct _dixon_asymreadwave_s *asym_readwave, const char *const desc, const int ttc, const float area, const float fov, const int duration, const float paddingarea) WARN_UNUSED_RESULT
 
STATUS ks_eval_readtrap1p (KS_READTRAP *readtrap, const char *const desc) WARN_UNUSED_RESULT
 
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) WARN_UNUSED_RESULT
 
STATUS ks_eval_phaser_setaccel (KS_PHASER *phaser, int min_acslines, float R) WARN_UNUSED_RESULT
 
STATUS ks_eval_phaser_constrained (KS_PHASER *phaser, const char *const desc, float ampmax, float slewrate, int minplateautime) WARN_UNUSED_RESULT
 
STATUS ks_eval_phaser (KS_PHASER *phaser, const char *const desc) WARN_UNUSED_RESULT
 
STATUS ks_eval_phaser2 (KS_PHASER *phaser, const char *const desc) WARN_UNUSED_RESULT
 
STATUS ks_eval_phaser1 (KS_PHASER *phaser, const char *const desc) WARN_UNUSED_RESULT
 
STATUS ks_eval_phaser1p (KS_PHASER *phaser, const char *const desc) WARN_UNUSED_RESULT
 
STATUS ks_eval_wave (KS_WAVE *wave, const char *const desc, int res, int duration, KS_WAVEFORM waveform) WARN_UNUSED_RESULT
 
STATUS ks_eval_mirrorwave (KS_WAVE *wave)
 
STATUS ks_eval_wave_file (KS_WAVE *wave, const char *const desc, int res, int duration, const char *const filename, const char *const format) WARN_UNUSED_RESULT
 
STATUS ks_eval_rf_sinc (KS_RF *rf, const char *const desc, double bw, double tbw, float flip, int wintype) WARN_UNUSED_RESULT
 
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) WARN_UNUSED_RESULT
 
STATUS ks_eval_rf_hard_optimal_duration (KS_RF *rf, const char *const desc, int order, float flip, float offsetFreq) WARN_UNUSED_RESULT
 
STATUS ks_eval_rf_binomial (KS_RF *rf, const char *const desc, int offResExc, int nPulses, float flip, float offsetFreq) WARN_UNUSED_RESULT
 
STATUS ks_eval_rfstat (KS_RF *rf) WARN_UNUSED_RESULT
 
STATUS ks_eval_rf (KS_RF *rf, const char *const desc) WARN_UNUSED_RESULT
 
void ks_eval_rf_relink (KS_RF *rf)
 
STATUS ks_eval_stretch_rf (KS_RF *rf, float stretch_factor)
 
STATUS ks_eval_seltrap (KS_TRAP *trap, const char *const desc, float slewrate, float slthick, float bw, int rfduration) WARN_UNUSED_RESULT
 
STATUS ks_eval_selrf_constrained (KS_SELRF *selrf, const char *const desc, float ampmax, float slewrate) WARN_UNUSED_RESULT
 
STATUS ks_eval_selrf (KS_SELRF *selrf, const char *const desc) WARN_UNUSED_RESULT
 
STATUS ks_eval_selrf2 (KS_SELRF *selrf, const char *const desc) WARN_UNUSED_RESULT
 
STATUS ks_eval_selrf1 (KS_SELRF *selrf, const char *const desc) WARN_UNUSED_RESULT
 
STATUS ks_eval_selrf1p (KS_SELRF *selrf, const char *const desc) WARN_UNUSED_RESULT
 
STATUS ks_eval_sms_get_phase_modulation (float *sms_phase_modulation, const int sms_multiband_factor, const int sms_phase_modulation_mode) WARN_UNUSED_RESULT
 
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) WARN_UNUSED_RESULT
 
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_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) WARN_UNUSED_RESULT
 
STATUS ks_eval_append_two_waves (KS_WAVE *first_wave, KS_WAVE *second_wave) WARN_UNUSED_RESULT
 
STATUS ks_eval_concatenate_waves (int num_waves, KS_WAVE *target_wave, KS_WAVE **waves_to_append) WARN_UNUSED_RESULT
 
STATUS ks_eval_epi_constrained (KS_EPI *epi, const char *const desc, float ampmax, float slewrate) WARN_UNUSED_RESULT
 
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) WARN_UNUSED_RESULT
 
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)
 
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) WARN_UNUSED_RESULT
 
STATUS ks_eval_seqcollection_durations_atleastminimum (KS_SEQ_COLLECTION *seqcollection) WARN_UNUSED_RESULT
 
void ks_eval_seqcollection_resetninst (KS_SEQ_COLLECTION *seqcollection)
 
void ks_print_seqcollection (KS_SEQ_COLLECTION *seqcollection, FILE *fp)
 
void ks_print_scaninfo (const SCAN_INFO *scan_info, int nslices, const char *desc, FILE *fp)
 
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)
 
GRAD_PULSE ks_eval_makegradpulse (KS_TRAP *trp, int gradchoice)
 
void ks_read_header_pool (int *exam_number, int *series_number, int *run_number)
 
unsigned int ks_calc_nextpow2 (unsigned int x)
 
int ks_calc_roundupms (int time)
 
STATUS ks_calc_filter (FILTER_INFO *echo_filter, int tsp, int duration) WARN_UNUSED_RESULT
 
int ks_calc_bw2tsp (float bw)
 
float ks_calc_tsp2bw (int tsp)
 
int ks_calc_trap_time2area (KS_TRAP *trap, float area)
 
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)
 
float ks_calc_minfov (float ampmax, int tsp)
 
float ks_calc_minslthick (float bw)
 
int ks_calc_mintsp (float ampmax, float fov)
 
float ks_calc_fov2gradareapixel (float fov)
 
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 (float t1, float t2, float B0)
 
float ks_calc_dixon_mean_nsa_2p_ss_weighted (float t1, float t2, float B0, float w1, float w2)
 
void ks_phaseencoding_init_tgt (KS_PHASEENCODING_PLAN *phaseenc_plan_ptr)
 
KS_PHASEENCODING_COORD ks_phaseencoding_get (const KS_PHASEENCODING_PLAN *phaseenc_plan_ptr, int echo, int shot)
 
void ks_phaseencoding_set (KS_PHASEENCODING_PLAN *phaseenc_plan_ptr, int echo, int shot, int ky, int kz)
 
void ks_phaseencoding_print (const KS_PHASEENCODING_PLAN *phaseenc_plan_ptr)
 
STATUS ks_phaseencoding_resize (KS_PHASEENCODING_PLAN *phaseenc_plan_ptr, int etl, int num_shots)
 
STATUS ks_phaseencoding_generate_simple (KS_PHASEENCODING_PLAN *phaseenc_plan_ptr, const char *const desc, KS_PHASER *phaser, KS_PHASER *zphaser)
 
STATUS ks_phaseencoding_generate_simple_ellipse (KS_PHASEENCODING_PLAN *phaseenc_plan_ptr, const char *const desc, KS_PHASER *phaser, KS_PHASER *zphaser)
 
STATUS ks_fse_calcecho (double *bestecho, double *optecho, int *nacqlines_to_kspacecenter, KS_PHASER *pe, ks_enum_pf_earlylate_te pf_direction, int TE, int etl, int esp)
 
STATUS ks_phaseencoding_generate_2Dfse (KS_PHASEENCODING_PLAN *phaseenc_plan_ptr, const char *const desc, KS_PHASER *phaser, KS_PHASER *zphaser, ks_enum_pf_earlylate_te pf_direction, int TE, int etl, int esp)
 
STATUS ks_phaseencoding_generate_epi (KS_PHASEENCODING_PLAN *phaseenc_plan_ptr, const char *const desc, const KS_EPI *epitrain, const ks_enum_epiblipsign blipsign, const ks_enum_pf_earlylate_te pf_direction, const int numileavestoacq, ks_enum_sweep_order sweep_order, int numsegments, const int caipi_delta)
 
STATUS ks_phaseencoding_generate_allepiplans (KS_PHASEENCODING_ALLEPIPLANS phaseenc_plans, const char *const desc, const KS_EPI *epitrain, const int caipi_delta)
 
STATUS ks_phaseencoding_allepiplans_setechoes (KS_PHASEENCODING_PLAN_16PTRS peplan_echoes, KS_PHASEENCODING_ALLEPIPLANS allepiplans, int nechoes, int multishot_flag, int min1stTE, int mirrorTE, ks_enum_epiblipsign blipsign)
 
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 inpass_interleaves)
 
STATUS ks_calc_sliceplan_sms (KS_SLICE_PLAN *slice_plan, int nslices, int slperpass, int multiband_factor)
 
int ks_calc_slice_acquisition_order_smssingleacq (DATA_ACQ_ORDER *dacq, int nslices)
 
int ks_calc_sms_min_gap (DATA_ACQ_ORDER *dacq, int nslices)
 
void ks_print_sliceplan (const KS_SLICE_PLAN slice_plan, FILE *fp)
 
void ks_print_waveform (const KS_WAVEFORM waveform, const char *filename, int res)
 
void ks_print_read (KS_READ read, FILE *fp)
 
void ks_print_trap (KS_TRAP trap, FILE *fp)
 
void ks_print_readtrap (KS_READTRAP readtrap, FILE *fp)
 
void ks_print_phaser (KS_PHASER phaser, FILE *fp)
 
void ks_print_epi (KS_EPI epi, FILE *fp)
 
void ks_print_rf (KS_RF rf, FILE *fp)
 
void ks_print_rfpulse (RF_PULSE rfpulse, FILE *fp)
 
void ks_print_selrf (KS_SELRF selrf, FILE *fp)
 
void ks_print_gradrfctrl (KS_GRADRFCTRL gradrfctrl, FILE *fp)
 
STATUS ks_error (const char *format,...) __attribute__((format(printf
 
STATUS STATUS ks_dbg (const char *format,...) __attribute__((format(printf
 
STATUS STATUS STATUS existfile (const char *fname)
 
int ks_syslimits_hasICEhardware ()
 
void ks_dbg_reset ()
 
float ks_syslimits_ampmax (LOG_GRAD loggrd)
 
float ks_syslimits_ampmax2 (LOG_GRAD loggrd)
 
float ks_syslimits_ampmax1 (LOG_GRAD loggrd)
 
float ks_syslimits_ampmax1p (LOG_GRAD loggrd)
 
int ks_syslimits_ramptimemax (LOG_GRAD loggrd)
 
float ks_syslimits_slewrate (LOG_GRAD loggrd)
 
float ks_syslimits_slewrate2 (LOG_GRAD loggrd)
 
float ks_syslimits_slewrate1 (LOG_GRAD loggrd)
 
float ks_syslimits_slewrate1p (LOG_GRAD loggrd)
 
float ks_syslimits_gradtarget (LOG_GRAD loggrd, int board)
 
float ks_syslimits_ampmax_phys ()
 
int ks_syslimits_ramptimemax_phys ()
 
float ks_syslimits_slewrate_phys ()
 
short ks_phase_radians2hw (float radphase)
 
short ks_phase_degrees2hw (float degphase)
 
float ks_calc_selgradamp (float rfbw, float slthick)
 
int ks_wave_res (const KS_WAVE *wave)
 
float ks_waveform_max (const KS_WAVEFORM waveform, int res)
 
float ks_wave_max (const KS_WAVE *wave)
 
float ks_waveform_min (const KS_WAVEFORM waveform, int res)
 
float ks_wave_min (const KS_WAVE *wave)
 
float ks_waveform_absmax (const KS_WAVEFORM waveform, int res)
 
float ks_waveform_maxslew (const KS_WAVEFORM waveform, int res, int duration)
 
float ks_wave_maxslew (const KS_WAVE *wave)
 
short ks_iwave_absmax (const KS_IWAVE waveform, int res)
 
float ks_wave_absmax (const KS_WAVE *wave)
 
float ks_waveform_area (const KS_WAVEFORM waveform, int start, int end, int dwelltime)
 
float ks_wave_area (const KS_WAVE *wave, int start, int end)
 
float ks_waveform_sum (const KS_WAVEFORM waveform, int res)
 
float ks_wave_sum (const KS_WAVE *wave)
 
float ks_waveform_norm (const KS_WAVEFORM waveform, int res)
 
float ks_wave_norm (const KS_WAVE *wave)
 
void ks_waveform_cumsum (KS_WAVEFORM cumsumwaveform, const KS_WAVEFORM waveform, int res)
 
void ks_wave_cumsum (KS_WAVE *cumsumwave, const KS_WAVE *wave)
 
void ks_waveform_multiply (KS_WAVEFORM waveform_mod, const KS_WAVEFORM waveform, int res)
 
void ks_wave_multiply (KS_WAVE *wave_mod, const KS_WAVE *wave)
 
void ks_waveform_add (KS_WAVEFORM waveform_mod, const KS_WAVEFORM waveform, int res)
 
void ks_wave_add (KS_WAVE *wave_mod, const KS_WAVE *wave)
 
void ks_waveform_multiplyval (KS_WAVEFORM waveform, float val, int res)
 
void ks_wave_multiplyval (KS_WAVE *wave, float val)
 
void ks_waveform_addval (KS_WAVEFORM waveform, float val, int res)
 
void ks_wave_addval (KS_WAVE *wave, float val)
 
STATUS ks_waveform2iwave (KS_IWAVE iwave, const KS_WAVEFORM waveform, int res, int board) WARN_UNUSED_RESULT
 
STATUS ks_wave2iwave (KS_IWAVE iwave, const KS_WAVE *wave, int board) WARN_UNUSED_RESULT
 
WF_PULSE * ks_pg_echossp (WF_PULSE *echo, const char *suffix)
 
STATUS ks_pg_trap (KS_TRAP *trap, KS_SEQLOC loc, KS_SEQ_CONTROL *ctrl) WARN_UNUSED_RESULT
 
STATUS ks_pg_read (KS_READ *read, int pos, KS_SEQ_CONTROL *ctrl) WARN_UNUSED_RESULT
 
STATUS ks_pg_phaser (KS_PHASER *phaser, KS_SEQLOC loc, KS_SEQ_CONTROL *ctrl) WARN_UNUSED_RESULT
 
STATUS ks_pg_readtrap (KS_READTRAP *readtrap, KS_SEQLOC loc, KS_SEQ_CONTROL *ctrl) WARN_UNUSED_RESULT
 
STATUS ks_pg_wave (KS_WAVE *wave, KS_SEQLOC loc, KS_SEQ_CONTROL *ctrl) WARN_UNUSED_RESULT
 
STATUS ks_pg_rf (KS_RF *rf, KS_SEQLOC loc, KS_SEQ_CONTROL *ctrl) WARN_UNUSED_RESULT
 
STATUS ks_pg_selrf (KS_SELRF *selrf, KS_SEQLOC loc, KS_SEQ_CONTROL *ctrl) WARN_UNUSED_RESULT
 
STATUS ks_pg_wait (KS_WAIT *wait, KS_SEQLOC loc, KS_SEQ_CONTROL *ctrl) WARN_UNUSED_RESULT
 
STATUS ks_pg_isirot (KS_ISIROT *isirot, SCAN_INFO scan_info, int pos, void(*rotfun)(), KS_SEQ_CONTROL *ctrl) WARN_UNUSED_RESULT
 
STATUS ks_pg_epi_dephasers (KS_EPI *epi, KS_SEQLOC loc, KS_SEQ_CONTROL *ctrl) WARN_UNUSED_RESULT
 
STATUS ks_pg_epi_rephasers (KS_EPI *epi, KS_SEQLOC loc, KS_SEQ_CONTROL *ctrl) WARN_UNUSED_RESULT
 
STATUS ks_pg_epi_echo (KS_EPI *epi, KS_SEQLOC loc, KS_SEQ_CONTROL *ctrl) WARN_UNUSED_RESULT
 
STATUS ks_pg_epi (KS_EPI *epi, KS_SEQLOC loc, KS_SEQ_CONTROL *ctrl) WARN_UNUSED_RESULT
 
void ks_mat4_zero (KS_MAT4x4 m)
 
void ks_mat4_identity (KS_MAT4x4 m)
 
void ks_mat4_print (const KS_MAT4x4 m)
 
void ks_mat4_multiply (KS_MAT4x4 lhs, const KS_MAT4x4 rhs_left, const KS_MAT4x4 rhs_right)
 
void ks_mat4_invert (KS_MAT4x4 lhs, const KS_MAT4x4 rhs)
 
void ks_mat4_setgeometry (KS_MAT4x4 lhs, float x, float y, float z, float xr, float yr, float zr)
 
void ks_mat4_setrotation1axis (KS_MAT4x4 rhs, float rot, char axis)
 
void ks_mat4_extractrotation (KS_MAT3x3 R, const KS_MAT4x4 M)
 
void ks_mat4_extracttranslation (double *T, const KS_MAT4x4 M)
 
void ks_mat3_identity (KS_MAT3x3 m)
 
void ks_mat3_multiply (KS_MAT3x3 lhs, const KS_MAT3x3 rhs_left, const KS_MAT3x3 rhs_right)
 
void ks_mat3_print (const KS_MAT3x3 m)
 
void ks_mat3_apply (double *w, const KS_MAT3x3 R, const double *v)
 
void ks_mat3_invapply (double *w, const KS_MAT3x3 R, const double *v)
 
void ks_scan_update_slice_location (SCAN_INFO *new_loc, const SCAN_INFO orig_loc, const KS_MAT4x4 M_physical, const KS_MAT4x4 M_logical)
 
void ks_scan_rotate (SCAN_INFO slice_info)
 
void ks_scan_isirotate (KS_ISIROT *isirot)
 
STATUS ks_pg_fse_flip_angle_taperoff (double *flip_angles, int etl, double flip1, double flip2, double flip3, double target_flip, int start_middle)
 
void ks_pg_mod_fse_rfpulse_structs (KS_SELRF *rf1, KS_SELRF *rf2, KS_SELRF *rf3, const double *flip_angles, const int etl)
 
void ks_instancereset_trap (KS_TRAP *trap)
 
void ks_instancereset_wait (KS_WAIT *wait)
 
void ks_instancereset_phaser (KS_PHASER *phaser)
 
void ks_instancereset_readtrap (KS_READTRAP *readtrap)
 
void ks_instancereset_rf (KS_RF *rf)
 
void ks_instancereset_selrf (KS_SELRF *selrf)
 
void ks_instancereset_epi (KS_EPI *epi)
 
int ks_compare_wfi_by_timeboard (const KS_WFINSTANCE *a, const KS_WFINSTANCE *b)
 
int ks_compare_wfi_by_boardtime (const KS_WFINSTANCE *a, const KS_WFINSTANCE *b)
 
int ks_compare_wfp_by_time (const WF_PULSE *a, const WF_PULSE *b)
 
int ks_compare_pint (const void *v1, const void *v2)
 
int ks_compare_pshort (const void *v1, const void *v2)
 
int ks_compare_pfloat (const void *v1, const void *v2)
 
int ks_compare_int (const void *v1, const void *v2)
 
int ks_compare_short (const void *v1, const void *v2)
 
int ks_compare_float (const void *v1, const void *v2)
 
void ks_sort_getsortedindx (int *sortedindx, int *array, int n)
 
void ks_sort_getsortedindx_s (int *sortedindx, short *array, int n)
 
void ks_sort_getsortedindx_f (int *sortedindx, float *array, int n)
 
void ks_sort_wfi_by_timeboard (KS_WFINSTANCE *a, int nitems)
 
void ks_sort_wfi_by_boardtime (KS_WFINSTANCE *a, int nitems)
 
void ks_sort_wfp_by_time (WF_PULSE *a, int nitems)
 
int ks_file_exist (char *filename)
 
void ks_plot_host_slicetime_delete ()
 
void ks_plot_slicetime_begin ()
 
void ks_plot_slicetime (KS_SEQ_CONTROL *ctrl, int nslices, float *slicepos_mm, float slthick_mm, KS_PLOT_EXCITATION_MODE exctype)
 
void ks_plot_slicetime_endofslicegroup (const char *desc)
 
void ks_plot_slicetime_endofslicegroup_tagged (const char *desc, const KS_PLOT_SLICEGROUP_MODE tag)
 
void ks_plot_slicetime_endofpass (KS_PLOT_PASS_MODE)
 
void ks_plot_slicetime_end ()
 
void ks_plot_host_slicetime_begin ()
 
void ks_plot_host_slicetime (KS_SEQ_CONTROL *ctrl, int nslices, float *slicepos_mm, float slthick_mm, KS_PLOT_EXCITATION_MODE exctype)
 
void ks_plot_host_slicetime_endofslicegroup (const char *desc, const KS_PLOT_SLICEGROUP_MODE tag)
 
void ks_plot_host_slicetime_endofpass (KS_PLOT_PASS_MODE)
 
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 **plan, const int num_plans)
 
void ks_plot_tgt_reset (KS_SEQ_CONTROL *ctrl)
 
void ks_plot_tgt_addframe (KS_SEQ_CONTROL *ctrl)
 
void ks_scan_rf_ampscale (KS_RF *rf, int instanceno, float ampscale)
 
void ks_scan_rf_on (KS_RF *rf, int instanceno)
 
void ks_scan_rf_on_chop (KS_RF *rf, int instanceno)
 
void ks_scan_rf_off (KS_RF *rf, int instanceno)
 
void ks_scan_selrf_setfreqphase (KS_SELRF *selrf, int instanceno, SCAN_INFO sliceinfo, float rfphase)
 
void ks_scan_selrf_setfreqphase_pins (KS_SELRF *selrf, int instanceno, SCAN_INFO sliceinfo, int sms_multiband_factor, float sms_slice_gap, float rfphase)
 
void ks_scan_rf_setphase (KS_RF *rf, int instanceno, float rfphase)
 
void ks_scan_wave2hardware (KS_WAVE *wave, const KS_WAVEFORM newwave)
 
void ks_scan_offsetfov_iso (KS_READTRAP *readtrap, int instanceno, SCAN_INFO sliceinfo, double ky, double kz, double rcvphase)
 
void ks_scan_offsetfov (KS_READTRAP *readtrap, int instanceno, SCAN_INFO sliceinfo, float view, float phasefovratio, float rcvphase)
 
void ks_scan_offsetfov3D (KS_READTRAP *readtrap, int instanceno, SCAN_INFO sliceinfo, float kyview, float phasefovratio, float kzview, float zphasefovratio, float rcvphase)
 
void ks_scan_offsetfov_readwave (KS_READWAVE *readwave, int instanceno, SCAN_INFO sliceinfo, float kyview, float phasefovratio, float rcvphase)
 
void ks_scan_omegatrap_hz (KS_TRAP *trap, int instanceno, float Hz)
 
void ks_scan_omegawave_hz (KS_WAVE *wave, int instanceno, float Hz)
 
void ks_scan_wait (KS_WAIT *wait, int waitperiod)
 
void ks_scan_trap_ampscale (KS_TRAP *trap, int instanceno, float ampscale)
 
void ks_scan_trap_ampscale_slice (KS_TRAP *trap, int start, int skip, int count, float ampscale)
 
void ks_scan_phaser_kmove (KS_PHASER *phaser, int instanceno, double pixelunits)
 
void ks_scan_phaser_toline (KS_PHASER *phaser, int instanceno, int view)
 
void ks_scan_phaser_fromline (KS_PHASER *phaser, int instanceno, int view)
 
int ks_scan_getsliceloc (const KS_SLICE_PLAN *slice_plan, int passindx, int sltimeinpass)
 
int ks_scan_getslicetime (const KS_SLICE_PLAN *slice_plan, int passindx, int slloc)
 
ks_enum_epiblipsign ks_scan_epi_verify_phaseenc_plan (KS_EPI *epi, KS_PHASEENCODING_PLAN *phaseenc_plan, int shot)
 
void ks_scan_epi_shotcontrol (KS_EPI *epi, int echo, SCAN_INFO sliceinfo, KS_PHASEENCODING_PLAN *phaseenc_plan, int shot, int exc, float rcvphase)
 
void ks_scan_epi_loadecho_with_tag (KS_EPI *epi, int echo, int storeecho, int slice, KS_PHASEENCODING_PLAN *phaseenc_plan, int shot, KSEPI_DATATAG *datatag, KS_DATASTORETAG *datastoretag)
 
void ks_scan_epi_loadecho_with_datastoretag (KS_EPI *epi, int echo, int storeecho, int slice, KS_PHASEENCODING_PLAN *phaseenc_plan, int shot, KS_DATASTORETAG *datastoretag)
 
void ks_scan_epi_loadecho_with_epidatatag (KS_EPI *epi, int echo, int storeecho, int slice, KS_PHASEENCODING_PLAN *phaseenc_plan, int shot, KSEPI_DATATAG *datatag)
 
void ks_scan_epi_loadecho (KS_EPI *epi, int echo, int storeecho, int slice, KS_PHASEENCODING_PLAN *phaseenc_plan, int shot)
 
void ks_loaddab (WF_PULSE_ADDR echo, char *dab_array)
 
void ks_loaddab_datastoretag (WF_PULSE_ADDR echo, KS_DATASTORETAG *datastoretag)
 
void ks_scan_acq_to_rtp (KS_READ *read, TYPDAB_PACKETS dabacqctrl, float fatoffset)
 
void ks_scan_switch_to_sequence (KS_SEQ_CONTROL *ctrl)
 
int ks_scan_playsequence (KS_SEQ_CONTROL *ctrl)
 
STATUS ks_scan_loaddabwithindices (WF_PULSE_ADDR pulse, LONG slice, LONG echo, LONG view, uint8_t acq, uint8_t vol, TYPDAB_PACKETS acqon_flag)
 
STATUS ks_scan_loaddabwithindices_nex (WF_PULSE_ADDR pulse, LONG slice, LONG echo, LONG view, uint8_t acq, uint8_t vol, LONG operation, TYPDAB_PACKETS acqon_flag)
 
int ks_scan_wait_for_rtp (void *rtpmsg, int maxmsgsize, int maxwait, KS_SEQ_CONTROL *waitctrl)
 
void ks_copy_and_reset_obj (void *pobj)
 
void ks_show_clock (FLOAT scantime)
 
float ks_scan_rf_phase_spoiling (int counter)
 
void ks_tgt_reset_gradrfctrl (KS_GRADRFCTRL *gradrfctrl)
 
int ks_eval_clear_readwave (KS_READWAVE *readwave)
 
int ks_eval_clear_dualreadtrap (KS_DIXON_DUALREADTRAP *dual_read)
 
void ks_polyval (const double *coeffs, const int order, const float *x, const int numx, float *values)
 

Variables

int abort_on_kserror
 
int ks_rhoboard
 
LOG_GRAD loggrd
 
PHYS_GRAD phygrd
 
int pscR1
 
int rspent
 

Detailed Description

This file contains the most documentation, and contains all definitions, sequence objects (typedef structs) and function prototypes for KSFoundation. @inline this file in the main sequence.

Macro Definition Documentation

◆ WARN_UNUSED_RESULT

#define WARN_UNUSED_RESULT

Compiler flag for DV25 and higher, causing a compiler error if the returned function value is not used by the calling function

◆ DEFAULT_SCAN_INFO_HEAD

#define DEFAULT_SCAN_INFO_HEAD   0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0

◆ DEFAULT_SCAN_INFO_TAIL

#define DEFAULT_SCAN_INFO_TAIL

◆ DEFAULT_AXIAL_SCAN_INFO

#define DEFAULT_AXIAL_SCAN_INFO
Value:
{ DEFAULT_SCAN_INFO_HEAD, {1.0, 0.0, 0.0, \
0.0, 1.0, 0.0, \
0.0, 0.0, 1.0}, DEFAULT_SCAN_INFO_TAIL}
#define DEFAULT_SCAN_INFO_TAIL
Definition: KSFoundation.h:65
#define DEFAULT_SCAN_INFO_HEAD
Definition: KSFoundation.h:61

◆ DEFAULT_AX_SCAN_INFO_FREQ_LR

#define DEFAULT_AX_SCAN_INFO_FREQ_LR   DEFAULT_AXIAL_SCAN_INFO

◆ DEFAULT_AX_SCAN_INFO_FREQ_AP

#define DEFAULT_AX_SCAN_INFO_FREQ_AP
Value:
{ DEFAULT_SCAN_INFO_HEAD, {0.0, 1.0, 0.0, \
-1.0, 0.0, 0.0, \
0.0, 0.0, 1.0}, DEFAULT_SCAN_INFO_TAIL}
#define DEFAULT_SCAN_INFO_TAIL
Definition: KSFoundation.h:65
#define DEFAULT_SCAN_INFO_HEAD
Definition: KSFoundation.h:61

◆ DEFAULT_SAG_SCAN_INFO_FREQ_SI

#define DEFAULT_SAG_SCAN_INFO_FREQ_SI
Value:
{ DEFAULT_SCAN_INFO_HEAD, {0.0, 0.0, 1.0, \
0.0, 1.0, 0.0, \
-1.0, 0.0, 0.0}, DEFAULT_SCAN_INFO_TAIL}
#define DEFAULT_SCAN_INFO_TAIL
Definition: KSFoundation.h:65
#define DEFAULT_SCAN_INFO_HEAD
Definition: KSFoundation.h:61

◆ DEFAULT_SAG_SCAN_INFO_FREQ_AP

#define DEFAULT_SAG_SCAN_INFO_FREQ_AP
Value:
{ DEFAULT_SCAN_INFO_HEAD, {0.0, 0.0, 1.0, \
1.0, 0.0, 0.0, \
0.0, 1.0, 0.0}, DEFAULT_SCAN_INFO_TAIL}
#define DEFAULT_SCAN_INFO_TAIL
Definition: KSFoundation.h:65
#define DEFAULT_SCAN_INFO_HEAD
Definition: KSFoundation.h:61

◆ DEFAULT_COR_SCAN_INFO_FREQ_SI

#define DEFAULT_COR_SCAN_INFO_FREQ_SI
Value:
{ DEFAULT_SCAN_INFO_HEAD, {0.0, 1.0, 0.0, \
0.0, 0.0, 1.0, \
1.0, 0.0, 0.0}, DEFAULT_SCAN_INFO_TAIL}
#define DEFAULT_SCAN_INFO_TAIL
Definition: KSFoundation.h:65
#define DEFAULT_SCAN_INFO_HEAD
Definition: KSFoundation.h:61

◆ DEFAULT_COR_SCAN_INFO_FREQ_LR

#define DEFAULT_COR_SCAN_INFO_FREQ_LR
Value:
{ DEFAULT_SCAN_INFO_HEAD, {-1.0, 0.0, 0.0, \
0.0, 0.0, 1.0, \
0.0, 1.0, 0.0}, DEFAULT_SCAN_INFO_TAIL}
#define DEFAULT_SCAN_INFO_TAIL
Definition: KSFoundation.h:65
#define DEFAULT_SCAN_INFO_HEAD
Definition: KSFoundation.h:61

◆ KS_NOTSET

#define KS_NOTSET   -1

Unrealistic value used on some variables to trigger errors

◆ KS_INFINITY

#define KS_INFINITY   (1.0/0.0)

Infinity

◆ _cvdesc

#define _cvdesc (   cv,
  string 
)
Value:
{\
if( NULL != string ) {\
if( FALSE == (cv)->staticdescrflag ) {\
free( (void *)((cv)->descr) );\
}\
(cv)->descr = strdup( string );\
(cv)->staticdescrflag = FALSE;\
}\
}

◆ areSame

#define areSame (   a,
 
)    (fabs((a) - (b)) < 1e-5)

◆ isNotSet

#define isNotSet (   a)    areSame((a), KS_NOTSET)

◆ isSet

#define isSet (   a)    !isNotSet(a)

◆ KS_ISI_predelay

#define KS_ISI_predelay   256

◆ KS_ISI_time

#define KS_ISI_time   256

◆ KS_ISI_rotupdatetime

#define KS_ISI_rotupdatetime   12

◆ KS_ISI_postdelay

#define KS_ISI_postdelay   256

◆ KS_MINPLATEAUTIME

#define KS_MINPLATEAUTIME   8

Shortest plateautime of a trapezoid (KS_TRAP)

◆ KS_DEFAULT_SSI_TIME

#define KS_DEFAULT_SSI_TIME   1500

◆ KS_DEFAULT_SSI_TIME_MR750w

#define KS_DEFAULT_SSI_TIME_MR750w   500

◆ KS_DEFAULT_SSI_TIME_MR750

#define KS_DEFAULT_SSI_TIME_MR750   1000

◆ KS_DEFAULT_SSI_TIME_MR450W

#define KS_DEFAULT_SSI_TIME_MR450W   500

◆ KS_DEFAULT_SSI_TIME_MR450

#define KS_DEFAULT_SSI_TIME_MR450   1000

◆ KS_DEFAULT_SSI_TIME_PREMIER

#define KS_DEFAULT_SSI_TIME_PREMIER   500

◆ KS_DEFAULT_SSI_TIME_ICE

#define KS_DEFAULT_SSI_TIME_ICE   200

◆ KS_DEFAULT_SSI_TIME_OTHERWISE

#define KS_DEFAULT_SSI_TIME_OTHERWISE   1500

◆ KS_RFSSP_PRETIME

#define KS_RFSSP_PRETIME   64

Time in [us] before an RF pulse that is needed for SSP packets. May sometimes be taken into account

◆ KS_RFSSP_POSTTIME

#define KS_RFSSP_POSTTIME   56

Time in [us] after an RF pulse that is needed for SSP packets. May sometimes be taken into account

◆ KS_RF_STANDARD_CRUSHERAREA

#define KS_RF_STANDARD_CRUSHERAREA   2000.0

◆ KS_EPI_MIN_INTERTRAIN_SSPGAP

#define KS_EPI_MIN_INTERTRAIN_SSPGAP   80

EXPERIMENTAL. Check how the SSP packets in the beginning/end of the EPI train can be packed to minimize this one for all possible acq. parameters

◆ L_REF

#define L_REF   10

◆ PSD_HRMW_COIL

#define PSD_HRMW_COIL   13

◆ HAVE_PREMIER_GRADIENTS

#define HAVE_PREMIER_GRADIENTS   (cfgcoiltype == PSD_HRMW_COIL)

◆ HALF_KHZ_USEC

#define HALF_KHZ_USEC   500.0

◆ KS_SAVEPFILES

#define KS_SAVEPFILES   RHXC_AUTO_LOCK

Bit for rhexecctrl to dump Pfiles

◆ KS_GERECON

#define KS_GERECON   (RHXC_AUTO_DISPLAY+RHXC_XFER_IM)

Bit for rhexecctrl for GE product image reconstruction

◆ KS_3D_SELECTED

#define KS_3D_SELECTED   (opimode == PSD_3D || opimode == PSD_3DM)

◆ KS_3D_SINGLESLAB_SELECTED

#define KS_3D_SINGLESLAB_SELECTED   (opimode == PSD_3D)

◆ KS_3D_MULTISLAB_SELECTED

#define KS_3D_MULTISLAB_SELECTED   (opimode == PSD_3DM)

◆ KS_DESCRIPTION_LENGTH

#define KS_DESCRIPTION_LENGTH   128

Max # of characters in the description of the various sequence objects

◆ KS_MAXWAVELEN

#define KS_MAXWAVELEN   10000

KS_MAXWAVELEN (10000) sample points

◆ KS_MAXUNIQUE_RF

#define KS_MAXUNIQUE_RF   20

Maximum number of different RF pulses in the sequence (c.f. also grad_rf_empty**.h)

◆ KS_MAXUNIQUE_TRAP

#define KS_MAXUNIQUE_TRAP   200

Maximum number of different KS_TRAPs in the sequence

◆ KS_MAXUNIQUE_WAVE

#define KS_MAXUNIQUE_WAVE   200

Maximum number of different KS_WAVEs in the sequence

◆ KS_MAXUNIQUE_SEQUENCES

#define KS_MAXUNIQUE_SEQUENCES   200

Maximum number of pulses sequence modules, including core, (i.e. with own SEQLENGTH/bofffset()) in this pulse sequence

◆ KS_MAX_PHASEDYN

#define KS_MAX_PHASEDYN   2049

Maximum number of phase encoding steps in a KS_PHASER sequence object

◆ KS_MAXUNIQUE_READ

#define KS_MAXUNIQUE_READ   500

Maximum number of readout instances

◆ KS_MAXINSTANCES

#define KS_MAXINSTANCES   500

◆ KS_EPI_MINBLIPAREA

#define KS_EPI_MINBLIPAREA   100

Default value of lowest EPI blip area until blip is made wider to avoid discretization errors

◆ KS_INITZEROS

#define KS_INITZEROS (   n)    {[0 ... (n-1)] = 0}

C macro to initialize arrays with zeros at declaration

◆ KS_INITVALUE

#define KS_INITVALUE (   n,
 
)    {[0 ... (n-1)] = a}

C macro to initialize each element in an array with a number at declaration

◆ KS_INIT_DESC

#define KS_INIT_DESC   KS_INITZEROS(KS_DESCRIPTION_LENGTH)

Default values for KS_DESCRIPTION strings

◆ KS_INIT_SEQLOC

#define KS_INIT_SEQLOC   {KS_NOTSET, KS_NOTSET, 1.0, 1.0}

Default values for KS_SEQLOC structs

◆ KS_INIT_WFINSTANCE

#define KS_INIT_WFINSTANCE   {NULL, 0, NULL, {KS_INIT_SEQLOC}}

Default values for KS_WFINSTANCE structs (internal use)

◆ KS_INIT_WAIT

#define KS_INIT_WAIT   {{0,0,NULL,sizeof(KS_WAIT)}, KS_INIT_DESC, 0, KS_INITVALUE(KS_MAXINSTANCES, KS_INIT_SEQLOC), NULL, NULL}

Default values for KS_WAIT sequence objects

◆ KS_INIT_ISIROT

#define KS_INIT_ISIROT   {KS_INIT_WAIT, KS_INIT_WAIT, NULL, 0, 0, 0, 0}

◆ KS_INIT_TRAP

#define KS_INIT_TRAP   {{0,0,NULL,sizeof(KS_TRAP)}, KS_INIT_DESC, 0.0, 0.0, 0, 0, 0, {0, 0, 0}, {0, 0, 0}, {1, 1, 1}, KS_INITVALUE(KS_MAXINSTANCES,KS_INIT_SEQLOC), NULL, NULL, NULL}

Default values for KS_TRAP sequence objects

◆ KS_INIT_READ

#define KS_INIT_READ   {{0,0,NULL,sizeof(KS_READ)}, KS_INIT_DESC, 0, KS_NOTSET, {0.0, 0, 0.0, 0.0, KS_NOTSET, 0, 0, 0}, KS_INITVALUE(KS_MAXINSTANCES,KS_NOTSET), 0, NULL}

Default values for KS_READ sequence objects

◆ KS_INIT_READ_EPI

#define KS_INIT_READ_EPI   {{0,0,NULL,sizeof(KS_READ)}, KS_INIT_DESC, 0, 250.0, {0.0, 0, 0.0, 0.0, KS_NOTSET, 0, 0, 0}, KS_INITVALUE(KS_MAXINSTANCES,KS_NOTSET), 0, NULL}

Default values for KS_READ sequence objects in KS_EPI

◆ KS_INIT_READTRAP

#define KS_INIT_READTRAP   {KS_INIT_READ, 240.0, 256, 0, 0, 0, 0.0, 0.0, 0.0, 0, KS_INIT_TRAP, KS_INIT_TRAP}

Default values for KS_READTRAP sequence objects

◆ KS_INIT_READTRAP_EPI

#define KS_INIT_READTRAP_EPI   {KS_INIT_READ_EPI, 240.0, 64, 0, 0, 0, 0.0, 0.0, 0.0, 0, KS_INIT_TRAP, KS_INIT_TRAP}

Default values for the KS_READTRAP sequence objects in KS_EPI

◆ KS_INIT_PHASEENCTABLE

#define KS_INIT_PHASEENCTABLE   KS_INITZEROS(KS_MAX_PHASEDYN)

Default values for the phase encoding table *.linetoacq* in the KS_PHASER sequence object

◆ KS_INIT_PHASER

#define KS_INIT_PHASER   {KS_INIT_TRAP, 240.0, KS_NOTSET, 0, 1, 0, 0, 0.0, 0, KS_INIT_PHASEENCTABLE}

Default values for KS_PHASER sequence objects

◆ KS_INIT_PHASEENCODING_COORD

#define KS_INIT_PHASEENCODING_COORD   {KS_NOTSET, KS_NOTSET}

◆ KS_INIT_PHASEENCODING_PLAN

#define KS_INIT_PHASEENCODING_PLAN   {KS_NOTSET, KS_NOTSET, KS_INIT_DESC, NULL, NULL, NULL, FALSE}

◆ KS_INIT_PHASEENCODING_ALLEPIPLANS

#define KS_INIT_PHASEENCODING_ALLEPIPLANS   KS_INITVALUE(KS_EPI_PEPLAN_NUMPLANS, KS_INIT_PHASEENCODING_PLAN)

◆ KS_INIT_PHASEENCODING_PLAN_16PTRS

#define KS_INIT_PHASEENCODING_PLAN_16PTRS   KS_INITVALUE(16, NULL)

◆ KS_INIT_EPI

Default values for KS_EPI sequence objects

◆ KS_INIT_WAVEVALUE

#define KS_INIT_WAVEVALUE   0

Default value for one element in a KS_WAVEFORM

◆ KS_INIT_WAVEFORM

#define KS_INIT_WAVEFORM   KS_INITVALUE(KS_MAXWAVELEN, KS_INIT_WAVEVALUE)

Default values for KS_WAVEFORM

◆ KS_INIT_WAVE

#define KS_INIT_WAVE   {{0,0,NULL,sizeof(KS_WAVE)}, KS_INIT_DESC, 0, 0, KS_INIT_WAVEFORM, {0,0,0}, KS_NOTSET, KS_INITVALUE(KS_MAXINSTANCES,KS_INIT_SEQLOC), NULL, NULL, NULL}

Default values for KS_WAVE sequence objects

◆ KS_INIT_READWAVE

#define KS_INIT_READWAVE   {KS_INITVALUE(3,KS_INIT_WAVE), KS_INIT_WAVE, KS_INIT_WAVE, KS_INIT_READ, KS_INITVALUE(3, 0), KS_INITVALUE(3, 0.0), 0.0, 0.0, 0, 0, 0}

◆ KS_INIT_DIXON_ASYMREADWAVE

#define KS_INIT_DIXON_ASYMREADWAVE   {KS_INIT_READWAVE, KS_INIT_DESC, KS_INITVALUE(2,0), 0, 0.0, 0, 0.0, KS_INITVALUE(8,0.0)}

◆ KS_INIT_DIXON_DUALREADTRAP

#define KS_INIT_DIXON_DUALREADTRAP   {KS_INIT_READWAVE, KS_INIT_DESC, 0,0,0,0,0.0,0, KS_INITVALUE(2,0),0,0.0,0,0,0,0.0,0.0, KS_INITVALUE(4,0)}

Default values for KS_DIXON_DUALREADTRAP sequence objects

◆ RX27_APPLY_HADAMARD

#define RX27_APPLY_HADAMARD

◆ KS_INIT_RFPULSE

#define KS_INIT_RFPULSE   {NULL, NULL, 0.0,0.0,0.0,0.0,0.0,0,0.0,0.0,0.0,0.0,NULL,0.0,0.0,0,0,-1,0.0,NULL,0,NULL RX27_APPLY_HADAMARD}

Default values for GE's RF_PULSE structure

◆ KS_INIT_RF

Default values for KS_RF sequence objects

◆ KS_INIT_SELRF

Default values for KS_SELRF sequence objects

◆ KS_INIT_GRADRFCTRL

#define KS_INIT_GRADRFCTRL   {KS_INITZEROS(KS_MAXUNIQUE_RF), 0, KS_INITZEROS(KS_MAXUNIQUE_TRAP), 0, KS_INITZEROS(KS_MAXUNIQUE_WAVE), 0, KS_INITZEROS(KS_MAXUNIQUE_READ), 0, FALSE /* is_cleared_on_tgt */ }

Default values for KS_GRADRFCTRL control handler

◆ KS_INIT_SEQ_HANDLE

#define KS_INIT_SEQ_HANDLE   {NULL, 0, NULL}

Default values for the KS_SEQ_HANDLE struct

◆ KS_INIT_SEQ_CONTROL

#define KS_INIT_SEQ_CONTROL   {0, 0, KS_DEFAULT_SSI_TIME, 0, KS_NOTSET, FALSE, KS_INIT_DESC, KS_INIT_SEQ_HANDLE, KS_INIT_GRADRFCTRL}

Default values for the KS_SEQ_CONTROL struct

◆ KS_INIT_SEQ_COLLECTION

#define KS_INIT_SEQ_COLLECTION   {0, KS_SEQ_COLLECTION_ALLOWNEW, FALSE, KS_INITVALUE(KS_MAXUNIQUE_SEQUENCES, NULL)}

Default values for the KS_SEQ_CONTROL struct

◆ KS_INIT_SAR

#define KS_INIT_SAR   {0, 0, 0, 0}

Default values for the KS_SAR struct

◆ KS_INIT_SLICEPLAN

#define KS_INIT_SLICEPLAN   {0,0,0,{{0,0,0}}}

Default values for the KS_SLICE_PLAN struct

◆ KS_INIT_SMS_INFO

#define KS_INIT_SMS_INFO   {1, 0, 0}

Default values for the KS_INIT_SMS_INFO struct

◆ KS_INIT_LOGGRD

#define KS_INIT_LOGGRD   {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 }

Default values for GE's loggrd struct

◆ KS_INIT_PHYSGRD

#define KS_INIT_PHYSGRD   { 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 }

Default values for GE's phygrd struct

◆ KS_MAT3x3_IDENTITY

#define KS_MAT3x3_IDENTITY   {1,0,0,0,1,0,0,0,1}

Identity matrix initialization for KS_MAT3x3

◆ KS_MAT4x4_IDENTITY

#define KS_MAT4x4_IDENTITY   {1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1}

Identity matrix initialization for KS_MAT4x4

◆ KS_CUSTOM_DATASTORE_LENGTH

#define KS_CUSTOM_DATASTORE_LENGTH   22

◆ KSEPI_INIT_DATATAG

#define KSEPI_INIT_DATATAG   {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}

◆ KS_INIT_DATASTORE_STRUCT_DYNAMIC

#define KS_INIT_DATASTORE_STRUCT_DYNAMIC   {-1, -1, -1, -1, -1, {0.0}}

◆ KS_INIT_DATASTORE_STRUCT_INFO

#define KS_INIT_DATASTORE_STRUCT_INFO   {TRUE, 0}

◆ KS_INIT_DATASTORETAG

◆ cvset

#define cvset (   cv,
  min,
  max,
  def,
  desc 
)
Value:
cvmin(cv,min);\
cvmax(cv,max);\
cvdef(cv,def);\
if (strcmp(desc,"")) {cvdesc(cv,desc)};\
errornum(cv,0);\
errorstring(cv,"");\
if (CONCAT2(_,cv).fixedflag == 0) { cv = CONCAT2(_,cv).defval; }

Macro to set CVs directly with min/max/def

Typedef Documentation

◆ KS_DESCRIPTION

typedef char KS_DESCRIPTION[KS_DESCRIPTION_LENGTH]

Char array used in all KS_**** sequence objects

◆ KS_WAVEFORM

typedef float KS_WAVEFORM[KS_MAXWAVELEN]

Waveform array for RF, Theta or custom gradients

◆ KS_IWAVE

typedef short KS_IWAVE[KS_MAXWAVELEN]

Interim short integer array to copy to hardware (internal use)

◆ KS_MAT3x3

typedef double KS_MAT3x3[9]

Array to store a row major 3x3 matrix

◆ KS_MAT4x4

typedef double KS_MAT4x4[16]

Array to store a row major 4x4 matrix

◆ KS_PHASEENCODING_ALLEPIPLANS

typedef KS_PHASEENCODING_PLAN KS_PHASEENCODING_ALLEPIPLANS[KS_EPI_PEPLAN_NUMPLANS]

Readable names KS

◆ KS_PHASEENCODING_PLAN_16PTRS

typedef KS_PHASEENCODING_PLAN* KS_PHASEENCODING_PLAN_16PTRS[16]

KS_PHASEENCODING_PLAN array containing all 8 possible phase encoding plans for EPI

Enumeration Type Documentation

◆ KS_PLOT_FILEFORMATS

Enumerator
KS_PLOT_OFF 
KS_PLOT_MAKEPDF 
KS_PLOT_MAKESVG 
KS_PLOT_MAKEPNG 
328  {
329  KS_PLOT_OFF,
Definition: KSFoundation.h:329
Definition: KSFoundation.h:332
KS_PLOT_FILEFORMATS
Definition: KSFoundation.h:328
Definition: KSFoundation.h:330
Definition: KSFoundation.h:331

◆ KS_PLOT_EXCITATION_MODE

Enumerator
KS_PLOT_STANDARD 
KS_PLOT_NO_EXCITATION 
334  {
KS_PLOT_EXCITATION_MODE
Definition: KSFoundation.h:334
Definition: KSFoundation.h:336
Definition: KSFoundation.h:335

◆ KS_PLOT_PASS_MODE

Enumerator
KS_PLOT_PASS_WAS_DUMMY 
KS_PLOT_PASS_WAS_CALIBRATION 
KS_PLOT_PASS_WAS_STANDARD 
338  {
Definition: KSFoundation.h:339
KS_PLOT_PASS_MODE
Definition: KSFoundation.h:338
Definition: KSFoundation.h:341
Definition: KSFoundation.h:340

◆ KS_PLOT_SLICEGROUP_MODE

Enumerator
KS_PLOT_SG_DUMMY 
KS_PLOT_SG_CALIBRATION 
KS_PLOT_SG_ACQUISITION 
KS_PLOT_SG_NOTSET 
343  {
KS_PLOT_SLICEGROUP_MODE
Definition: KSFoundation.h:343
Definition: KSFoundation.h:347
Definition: KSFoundation.h:345
Definition: KSFoundation.h:344
Definition: KSFoundation.h:346

◆ ks_enum_pf_earlylate_te

Enumerator
KS_PF_EARLY_TE 
KS_PF_LATE_TE 
ks_enum_pf_earlylate_te
Definition: KSFoundation.h:1899
Definition: KSFoundation.h:1899
Definition: KSFoundation.h:1899

◆ ks_enum_sweep_order

Enumerator
KS_SWEEP_ORDER_TOP_DOWN 
KS_SWEEP_ORDER_BOTTOM_UP 
KS_SWEEP_ORDER_CENTER_OUT 
KS_SWEEP_ORDER_OUTSIDE_IN 
Definition: KSFoundation.h:1904
ks_enum_sweep_order
Definition: KSFoundation.h:1902
Definition: KSFoundation.h:1905
Definition: KSFoundation.h:1902
Definition: KSFoundation.h:1903

◆ ks_snr_mode

Enumerator
KS_SNR_MEAS_OFF 
KS_SNR_MEAS_FREQUENCY 
KS_SNR_MEAS_PHASE 
Definition: KSFoundation.h:1907
Definition: KSFoundation.h:1907
Definition: KSFoundation.h:1907

◆ ks_enum_trapparts

Enumerator
G_ATTACK 
G_PLATEAU 
G_DECAY 
Definition: KSFoundation.h:1910
Definition: KSFoundation.h:1910
Definition: KSFoundation.h:1910

◆ ks_enum_boarddef

Enumerator
KS_X 
KS_Y 
KS_Z 
KS_RHO 
KS_RHO2 
KS_THETA 
KS_OMEGA 
KS_SSP 
KS_FREQX_PHASEY 
KS_FREQY_PHASEX 
KS_FREQX_PHASEZ 
KS_FREQZ_PHASEX 
KS_FREQY_PHASEZ 
KS_FREQZ_PHASEY 
KS_XYZ 
KS_ALL 
1911  {KS_X = TYPXGRAD,
1912  KS_Y = TYPYGRAD,
1913  KS_Z = TYPZGRAD,
1914  KS_RHO = TYPRHO1,
1915  KS_RHO2 = TYPRHO2,
1916  KS_THETA = TYPTHETA,
1917  KS_OMEGA = TYPOMEGA,
1918  KS_SSP = TYPSSP,
1925  KS_XYZ,
1926  KS_ALL};
Definition: KSFoundation.h:1919
Definition: KSFoundation.h:1912
Definition: KSFoundation.h:1921
Definition: KSFoundation.h:1915
Definition: KSFoundation.h:1917
Definition: KSFoundation.h:1914
Definition: KSFoundation.h:1911
Definition: KSFoundation.h:1916
Definition: KSFoundation.h:1925
Definition: KSFoundation.h:1924
Definition: KSFoundation.h:1913
Definition: KSFoundation.h:1926
Definition: KSFoundation.h:1922
Definition: KSFoundation.h:1920
Definition: KSFoundation.h:1918
Definition: KSFoundation.h:1923

◆ ks_enum_epiblipsign

Enumerator
KS_EPI_POSBLIPS 
KS_EPI_NOBLIPS 
KS_EPI_NEGBLIPS 
1929 {KS_EPI_POSBLIPS = 1, KS_EPI_NOBLIPS = 0, KS_EPI_NEGBLIPS = -1} ks_enum_epiblipsign; /* positive or negative blips. Don't change the enum values (+1, -1)! */
Definition: KSFoundation.h:1929
ks_enum_epiblipsign
Definition: KSFoundation.h:1929
Definition: KSFoundation.h:1929
Definition: KSFoundation.h:1929

◆ ks_enum_epiplans

Enumerator
KS_EPI_PEPLAN_EARLYTE_BOTTOMUP_UNDERSAMPLED 
KS_EPI_PEPLAN_EARLYTE_BOTTOMUP_MULTISHOT 
KS_EPI_PEPLAN_EARLYTE_TOPDOWN_UNDERSAMPLED 
KS_EPI_PEPLAN_EARLYTE_TOPDOWN_MULTISHOT 
KS_EPI_PEPLAN_LATETE_BOTTOMUP_UNDERSAMPLED 
KS_EPI_PEPLAN_LATETE_BOTTOMUP_MULTISHOT 
KS_EPI_PEPLAN_LATETE_TOPDOWN_UNDERSAMPLED 
KS_EPI_PEPLAN_LATETE_TOPDOWN_MULTISHOT 
KS_EPI_PEPLAN_NUMPLANS 
1930  {
1939  KS_EPI_PEPLAN_NUMPLANS /* 8 */
1940 } ks_enum_epiplans;
Definition: KSFoundation.h:1931
ks_enum_epiplans
Definition: KSFoundation.h:1930
Definition: KSFoundation.h:1938
Definition: KSFoundation.h:1932
Definition: KSFoundation.h:1933
Definition: KSFoundation.h:1935
Definition: KSFoundation.h:1937
Definition: KSFoundation.h:1934
Definition: KSFoundation.h:1939
Definition: KSFoundation.h:1936

◆ ks_enum_diffusion

KS_PHASEENCODING_PLAN Array of 16 pointers to KS_PHASEENCODING_PLANS for multi-echo use

Enumerator
KS_EPI_DIFFUSION_OFF 
KS_EPI_DIFFUSION_ON 
Definition: KSFoundation.h:1945
Definition: KSFoundation.h:1945

◆ ks_enum_imsize

Enumerator
KS_IMSIZE_NATIVE 
KS_IMSIZE_POW2 
KS_IMSIZE_MIN256 
Definition: KSFoundation.h:1948
Definition: KSFoundation.h:1948
Definition: KSFoundation.h:1948

◆ ks_enum_datadestination

Enumerator
KS_DONT_SEND 
KS_SENDTOBAM 
KS_SENDTORTP 
1949 {KS_DONT_SEND = -1, KS_SENDTOBAM = 0, KS_SENDTORTP = 1};
Definition: KSFoundation.h:1949
Definition: KSFoundation.h:1949
Definition: KSFoundation.h:1949

◆ ks_enum_accelmenu

Enumerator
KS_ACCELMENU_FRACT 
KS_ACCELMENU_INT 
Definition: KSFoundation.h:1950
Definition: KSFoundation.h:1950

◆ ks_enum_rfrole

Enumerator
KS_RF_ROLE_NOTSET 
KS_RF_ROLE_EXC 
KS_RF_ROLE_REF 
KS_RF_ROLE_CHEMSAT 
KS_RF_ROLE_SPSAT 
KS_RF_ROLE_INV 
Definition: KSFoundation.h:1953
Definition: KSFoundation.h:1953
Definition: KSFoundation.h:1953
Definition: KSFoundation.h:1953
Definition: KSFoundation.h:1953
Definition: KSFoundation.h:1953

◆ ks_enum_sincwin

Enumerator
KS_RF_SINCWIN_OFF 
KS_RF_SINCWIN_HAMMING 
KS_RF_SINCWIN_HANNING 
KS_RF_SINCWIN_BLACKMAN 
KS_RF_SINCWIN_BARTLETT 
Definition: KSFoundation.h:1954
Definition: KSFoundation.h:1954
Definition: KSFoundation.h:1954
Definition: KSFoundation.h:1954
Definition: KSFoundation.h:1954

◆ ks_enum_wavebuf

Enumerator
KS_WF_MAIN 
KS_WF_BUF1 
KS_WF_BUF2 
KS_WF_SIZE 
Definition: KSFoundation.h:1957
Definition: KSFoundation.h:1957
Definition: KSFoundation.h:1957
Definition: KSFoundation.h:1957

◆ ks_enum_smstype

Enumerator
KS_SELRF_SMS_MB 
KS_SELRF_SMS_PINS 
KS_SELRF_SMS_PINS_DANTE 
KS_SELRF_SMS_MULTI_PINS 
KS_SELRF_SMSTYPE_SIZE 
1960  {
Definition: KSFoundation.h:1963
Definition: KSFoundation.h:1965
Definition: KSFoundation.h:1964
Definition: KSFoundation.h:1961
Definition: KSFoundation.h:1962

◆ ks_enum_sms_phase_mod

Enumerator
KS_SELRF_SMS_PHAS_MOD_OFF 
KS_SELRF_SMS_PHAS_MOD_PHAS 
KS_SELRF_SMS_PHAS_MOD_AMPL 
KS_SELRF_SMS_PHAS_MOD_QUAD 
KS_SELRF_SMS_PHAS_MOD_SIZE 
1967  {
Definition: KSFoundation.h:1969
Definition: KSFoundation.h:1972
Definition: KSFoundation.h:1970
Definition: KSFoundation.h:1968
Definition: KSFoundation.h:1971

◆ anonymous enum

anonymous enum
Enumerator
KS_SEQ_COLLECTION_ALLOWNEW 
KS_SEQ_COLLECTION_LOCKED 
Definition: KSFoundation.h:1974
Definition: KSFoundation.h:1974

◆ ks_enum_grad_scaling_policy

Enumerator
KS_GRADWAVE_ABSOLUTE 
KS_GRADWAVE_RELATIVE 
Definition: KSFoundation.h:1975
Definition: KSFoundation.h:1975

Function Documentation

◆ isNaN()

int isNaN ( float  a)
58  {
59  return a != a;
60 }

◆ 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_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
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 }
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_addtraptogradrfctrl()

STATUS ks_eval_addtraptogradrfctrl ( KS_GRADRFCTRL gradrfctrl,
KS_TRAP trap 
)

*Internal use*. Adds a trapezoid (KS_TRAP) to the KS_GRADRFCTRL struct for later gradient heating calculations in ks_eval_gradrflimits()

The KS_TRAP object will be ignored if added previously

Parameters
[in,out]gradrfctrlPointer to the KS_GRADRFCTRL for the sequence
[in]trapPointer to KS_TRAP
Return values
STATUSSUCCESS or FAILURE
3773  {
3774  int i;
3775  if (trap == NULL) return SUCCESS;
3776 
3777  if (gradrfctrl == NULL) {
3778  return ks_error("%s: gradrfctrl is NULL", __FUNCTION__);
3779  }
3780 
3781  if (gradrfctrl->numtrap >= KS_MAXUNIQUE_TRAP) {
3782  return ks_error("%s: ERROR - too many unique KS_TRAP in sequence", __FUNCTION__);
3783  }
3784 
3785  /* check for previous registrations of this trap's memory address. If found, return */
3786  if (gradrfctrl->numtrap > 0) {
3787  for (i = 0; i < gradrfctrl->numtrap; i++) {
3788  if (gradrfctrl->trapptr[i] == trap)
3789  return SUCCESS;
3790  }
3791  }
3792 
3793  gradrfctrl->trapptr[gradrfctrl->numtrap++] = trap;
3794 
3795  return SUCCESS;
3796 }
int numtrap
Definition: KSFoundation.h:969
STATUS ks_error(const char *format,...)
Definition: KSFoundation_common.c:70
#define KS_MAXUNIQUE_TRAP
Definition: KSFoundation.h:188
KS_TRAP * trapptr[KS_MAXUNIQUE_TRAP]
Definition: KSFoundation.h:968

◆ ks_eval_addwavetogradrfctrl()

STATUS ks_eval_addwavetogradrfctrl ( KS_GRADRFCTRL gradrfctrl,
KS_WAVE wave 
)

*Internal use*. Adds a wave (KS_WAVE) - if it is not a part of a KS_RF object - to the KS_GRADRFCTRL struct for later gradient heating calculations in ks_eval_gradrflimits()

The KS_WAVE object will be ignored if added previously

Parameters
[in,out]gradrfctrlPointer to the KS_GRADRFCTRL for the sequence
[in]wavePointer to KS_WAVE
Return values
STATUSSUCCESS or FAILURE
3798  {
3799  int i;
3800  if (wave == NULL) return SUCCESS;
3801 
3802  if (gradrfctrl == NULL) {
3803  return ks_error("%s: gradrfctrl is NULL", __FUNCTION__);
3804  }
3805 
3806  if (gradrfctrl->numwave >= KS_MAXUNIQUE_WAVE) {
3807  return ks_error("%s: ERROR - too many unique KS_WAVE in sequence", __FUNCTION__);
3808  }
3809 
3810  /* check for previous registrations of this trap's memory address. If found, return */
3811  if (gradrfctrl->numwave > 0) {
3812  for (i = 0; i < gradrfctrl->numwave; i++) {
3813  if (gradrfctrl->waveptr[i] == wave)
3814  return SUCCESS;
3815  }
3816  }
3817 
3818  /* check also for previous waves in KS_RF (rfwave, omegawave, thetawave). If found, return */
3819  if (gradrfctrl->numrf > 0) {
3820  for (i = 0; i < gradrfctrl->numrf; i++) {
3821  if (&(gradrfctrl->rfptr[i]->rfwave) == wave || &(gradrfctrl->rfptr[i]->omegawave) == wave || &(gradrfctrl->rfptr[i]->thetawave) == wave)
3822  return SUCCESS;
3823  }
3824  }
3825 
3826  gradrfctrl->waveptr[gradrfctrl->numwave++] = wave;
3827 
3828  return SUCCESS;
3829 }
KS_RF * rfptr[KS_MAXUNIQUE_RF]
Definition: KSFoundation.h:966
STATUS ks_error(const char *format,...)
Definition: KSFoundation_common.c:70
int numrf
Definition: KSFoundation.h:967
KS_WAVE thetawave
Definition: KSFoundation.h:951
KS_WAVE * waveptr[KS_MAXUNIQUE_WAVE]
Definition: KSFoundation.h:970
KS_WAVE rfwave
Definition: KSFoundation.h:949
#define KS_MAXUNIQUE_WAVE
Definition: KSFoundation.h:189
int numwave
Definition: KSFoundation.h:971
KS_WAVE omegawave
Definition: KSFoundation.h:950

◆ ks_eval_addrftogradrfctrl()

STATUS ks_eval_addrftogradrfctrl ( KS_GRADRFCTRL gradrfctrl,
KS_RF rf 
)

*Internal use*. Adds an RF pulse to the KS_GRADRFCTRL struct for later SAR & RF power calculations in ks_eval_gradrflimits()

The RF object will be ignored if added previously

Parameters
[in,out]gradrfctrlPointer to the KS_GRADRFCTRL for the sequence
[in]rfPointer to KS_RF
Return values
STATUSSUCCESS or FAILURE
3832  {
3833  int i;
3834  if (rf == NULL) return SUCCESS;
3835 
3836  if (gradrfctrl == NULL) {
3837  return ks_error("%s: gradrfctrl is NULL", __FUNCTION__);
3838  }
3839 
3840  if (gradrfctrl->numrf >= KS_MAXUNIQUE_RF) {
3841  return ks_error("%s: ERROR - too many unique KS_RF in sequence", __FUNCTION__);
3842  }
3843 
3844  /* check for previous registrations of this rf's memory address. If found, return */
3845  if (gradrfctrl->numrf > 0) {
3846  for (i = 0; i < gradrfctrl->numrf; i++) {
3847  if (gradrfctrl->rfptr[i] == rf)
3848  return SUCCESS;
3849  }
3850  }
3851 
3852  gradrfctrl->rfptr[gradrfctrl->numrf++] = rf;
3853 
3854  return SUCCESS;
3855 }
#define KS_MAXUNIQUE_RF
Definition: KSFoundation.h:187
KS_RF * rfptr[KS_MAXUNIQUE_RF]
Definition: KSFoundation.h:966
STATUS ks_error(const char *format,...)
Definition: KSFoundation_common.c:70
int numrf
Definition: KSFoundation.h:967

◆ ks_eval_addreadtogradrfctrl()

STATUS ks_eval_addreadtogradrfctrl ( KS_GRADRFCTRL gradrfctrl,
KS_READ read 
)

*Internal use*. Adds an acquisition to the KS_GRADRFCTRL struct. Used for plotting

Parameters
[in,out]gradrfctrlPointer to the KS_GRADRFCTRL for the sequence
[in]readPointer to acquisition object.
Return values
STATUSSUCCESS or FAILURE
3857  {
3858  int i;
3859  if (read == NULL) return SUCCESS;
3860 
3861  if (gradrfctrl == NULL) {
3862  return ks_error("%s: gradrfctrl is NULL", __FUNCTION__);
3863  }
3864 
3865  if (gradrfctrl->numacq >= KS_MAXUNIQUE_READ) {
3866  return ks_error("%s: ERROR - too many readouts in sequence", __FUNCTION__);
3867  }
3868 
3869  /* check for previous registrations of the pointer. If found, return */
3870  if (gradrfctrl->numacq > 0) {
3871  for (i = 0; i < gradrfctrl->numacq; i++) {
3872  if (gradrfctrl->readptr[i] == read)
3873  return SUCCESS;
3874  }
3875  }
3876 
3877  gradrfctrl->readptr[gradrfctrl->numacq++] = read;
3878 
3879  return SUCCESS;
3880 }
STATUS ks_error(const char *format,...)
Definition: KSFoundation_common.c:70
#define KS_MAXUNIQUE_READ
Definition: KSFoundation.h:192
KS_READ * readptr[KS_MAXUNIQUE_READ]
Definition: KSFoundation.h:972
int numacq
Definition: KSFoundation.h:973

◆ 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 ( KS_READTRAP readtrap,
float  wanted_paddingarea_pre,
float  wanted_paddingarea_post 
)

◆ 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 ( KS_READTRAP 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 
)

◆ ks_eval_dixon_wang()

STATUS ks_eval_dixon_wang ( struct _dixon_dualreadtrap_s *  dual_read,
const char *const  desc,
const float  PF,
float  M 
)

◆ ks_eval_dixon_dualreadtrap()

STATUS ks_eval_dixon_dualreadtrap ( struct _dixon_dualreadtrap_s *  dual_read,
const char *const  desc,
int  flip 
)

◆ ks_verify_dualreadtrap_setup()

STATUS ks_verify_dualreadtrap_setup ( struct _dixon_dualreadtrap_s *  dual_read)

◆ ks_eval_dixon_dualreadtrap_slim()

STATUS ks_eval_dixon_dualreadtrap_slim ( struct _dixon_dualreadtrap_s *  dual_read,
const char *const  desc,
int  flip 
)

◆ ks_eval_dixon_asymtriangle_padded()

STATUS ks_eval_dixon_asymtriangle_padded ( struct _dixon_asymreadwave_s *  asym_readwave,
const char *const  desc,
const int  tas,
const int  time2center,
const float  area_pre,
const float  area_acq,
const float  fov,
const int  duration 
)

◆ ks_eval_dixon_asymspline_padded()

STATUS ks_eval_dixon_asymspline_padded ( struct _dixon_asymreadwave_s *  asym_readwave,
const char *const  desc,
const int  tas,
const int  time2center,
const int  tae,
const float  Apre,
const float  Aacq,
const float  Apost,
const float  fov,
const int  duration 
)

◆ ks_eval_dixon_asymspline()

STATUS ks_eval_dixon_asymspline ( struct _dixon_asymreadwave_s *  asym_readwave,
const char *const  desc,
const int  time2center,
const float  area,
const float  fov,
const int  duration,
const float  paddingarea 
)

◆ ks_eval_dixon_asymtriangle()

STATUS ks_eval_dixon_asymtriangle ( struct _dixon_asymreadwave_s *  asym_readwave,
const char *const  desc,
const int  ttc,
const float  area,
const float  fov,
const int  duration,
const float  paddingarea 
)

◆ ks_eval_readtrap1p()

STATUS ks_eval_readtrap1p ( KS_READTRAP readtrap,
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_readtrap_constrained() with physical gradient constraints

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

◆ 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
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_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 }
float KS_WAVEFORM[KS_MAXWAVELEN]
Definition: KSFoundation.h:262
int res
Definition: KSFoundation.h:667
KS_WAVEFORM waveform
Definition: KSFoundation.h:669

◆ 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
#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_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
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
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
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
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_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_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
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_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_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_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_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 }

◆ 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

◆ 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
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_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
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
KS_SEQ_COLLECTION seqcollection
Definition: ksepi.e:80

◆ 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
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
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_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 }
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_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++) {
5085  fprintf(fp, "%s: %dx %d us = %d\n", seqcollection->seqctrlptr[i]->description, seqcollection->seqctrlptr[i]->nseqinstances, seqcollection->seqctrlptr[i]->duration,
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
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_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 }

◆ 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
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
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
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_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_read_header_pool()

void ks_read_header_pool ( int *  exam_number,
int *  series_number,
int *  run_number 
)

◆ 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_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_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_minfov()

float ks_calc_minfov ( float  ampmax,
int  tsp 
)

Calculates the minimum readout FOV

Given a maximum allowed gradient amplitude in [G/cm] and a dwell time in [us] (see ks_calc_bw2tsp()), the minimum possible readout FOV in [mm] is calculated. This function is called internally by ks_eval_readtrap_constrained() when rampsampling is not used (.rampsampling = 0)

Parameters
[in]ampmaxThe maximum allowed gradient amplitude ([G/cm])
[in]tspDuration in [us] for each data sample in the acquisition window. Minimum: 2 [us]
Return values
minFOVMinimum FOV in the readout direction
319  {
320  float minfov;
321 
322  minfov = ((float) (2 * (1.0e3 / (tsp * 2.0)) * 1000 / GAM) * (10.0 / ampmax));
323 
324  return minfov;
325 }
float GAM

◆ ks_calc_minslthick()

float ks_calc_minslthick ( float  bw)

Calculates the minimum slice thickness [mm]

Given an RF bandwidth in [kHz], this function returns the minimum slice thickness possible given the gradient max amplitude.

Parameters
[in]bwRF bandwidth in [kHz]
Return values
minSliceThicknessMinimum slice thickness in [mm]
328  {
329  return (bw / (ks_syslimits_ampmax(loggrd) * (0.1) * GAM));
330 }
float GAM
LOG_GRAD loggrd
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

◆ ks_calc_mintsp()

int ks_calc_mintsp ( float  ampmax,
float  fov 
)

Calculates the minimum dwell time

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

Parameters
[in]ampmaxThe maximum allowed gradient amplitude ([G/cm])
[in]fovFOV in the readout direction
Return values
tspDuration in [us] for each data sample in the acquisition window
333  {
334  int mintsp;
335  int sysmintsp = 2;
336 
337  mintsp = (int) ceil(1.0e3 / (fov / ((float) (2 * 1000 / GAM) * (10.0 / ampmax)) * 2.0));
338 
339  if (mintsp < sysmintsp)
340  mintsp = mintsp;
341 
342  return mintsp;
343 }
float GAM

◆ ks_calc_fov2gradareapixel()

float ks_calc_fov2gradareapixel ( float  fov)

Calculates the gradient area needed to move one pixel in k-space

Given a readout FOV in [mm], the gradient area needed to move one pixel in a fully sampled k-space is calculated

Parameters
[in]fovFOV in the readout direction
Return values
areaGradient area in [(G/cm) * us]
346  {
347  if (fov > 0) {
348  /* G/cm * us. Area needed to move one pixel in k-space corresponding to the input FOV */
349  return (1.0e6 / (fov / 10.0 * GAM));
350  } else {
351  return (0.0);
352  }
353 }
float GAM

◆ 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()

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_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_phaseencoding_init_tgt()

void ks_phaseencoding_init_tgt ( KS_PHASEENCODING_PLAN phaseenc_plan_ptr)

Internal use for correct migration of KS_PHASEENCODING_PLAN from HOST to TGT (IPG)

Parameters
[in]phaseenc_plan_ptrPointer to KS_PHASEENCODING_PLAN
Returns
void
361  {
362 #ifdef IPG
363  if (phaseenc_plan_ptr->is_cleared_on_tgt == FALSE) {
364  phaseenc_plan_ptr->entries = NULL; /* clear memory pointer from HOST (which is garbage to the TGT) */
365  phaseenc_plan_ptr->phaser = NULL; /* clear memory pointer from HOST (which is garbage to the TGT) */
366  phaseenc_plan_ptr->zphaser = NULL; /* clear memory pointer from HOST (which is garbage to the TGT) */
367  phaseenc_plan_ptr->is_cleared_on_tgt = TRUE;
368  }
369 #else
370  (void) (phaseenc_plan_ptr);
371 #endif
372 }
int is_cleared_on_tgt
Definition: KSFoundation.h:1745
KS_PHASEENCODING_COORD * entries
Definition: KSFoundation.h:1742
const KS_PHASER * phaser
Definition: KSFoundation.h:1743
const KS_PHASER * zphaser
Definition: KSFoundation.h:1744

◆ ks_phaseencoding_get()

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

If the .ky and .kz values returned by this function is = KS_NOTSET (-1), it indicates that this echo/shot combination should have zero phase encoding and should be ignored. For 2D, .kz will always be KS_NOTSET

Parameters
[in]phaseenc_plan_ptrPointer to KS_PHASEENCODING_PLAN
[in]echoEcho index in the sequence ETL in range [0,ETL-1]
[in]shotShot index (i.e. how many times the sequence is played out per slice). For 3D, this is the combined number of shots over ky and kz
Return values
<tt>KS_PHASEENCODING_COORD</tt>
375  {
376 #ifdef IPG
377  int oktoset = ((phaseenc_plan_ptr != NULL) && phaseenc_plan_ptr->etl != KS_NOTSET) && (phaseenc_plan_ptr->is_cleared_on_tgt);
378 #else
379  int oktoset = (phaseenc_plan_ptr != NULL) && phaseenc_plan_ptr->etl != KS_NOTSET;
380 #endif
381 
382  if (!oktoset || !(phaseenc_plan_ptr->entries) ||
383  shot < 0 || shot >= phaseenc_plan_ptr->num_shots ||
384  echo < 0 || echo >= phaseenc_plan_ptr->etl) {
385 
387  return fake_coords;
388  }
389 
390  return phaseenc_plan_ptr->entries[echo + phaseenc_plan_ptr->etl * shot];
391 }
#define KS_INIT_PHASEENCODING_COORD
Definition: KSFoundation.h:219
int is_cleared_on_tgt
Definition: KSFoundation.h:1745
int num_shots
Definition: KSFoundation.h:1739
KS_PHASEENCODING_COORD * entries
Definition: KSFoundation.h:1742
#define KS_NOTSET
Definition: KSFoundation.h:103
Struct holding a 3D k-space phase encoding location (ky,kz)
Definition: KSFoundation.h:1704
int etl
Definition: KSFoundation.h:1740

◆ ks_phaseencoding_set()

void ks_phaseencoding_set ( KS_PHASEENCODING_PLAN phaseenc_plan_ptr,
int  echo,
int  shot,
int  ky,
int  kz 
)

Set [ky,kz] coordinate from KS_PHASEENCODING_PLAN for given echo and shot

If the .ky and .kz values passed to this function is = KS_NOTSET (-1), it indicates that this echo/shot combination should have zero phase encoding and should be ignored. However, ignored echo/shot combinations are not necessary since the etl*num_shots entries in .entries of KS_PHASEENCODING_PLAN are always initialized to KS_NOTSET by ks_phaseencoding_resize(). Hence, it is only necessary to explicitly set the echo/shot combination for ky/kz coordinates being acquired.

For 2D, kz (5th arg) should always be KS_NOTSET.

Parameters
[in]phaseenc_plan_ptrPointer to KS_PHASEENCODING_PLAN
[in]echoEcho index in the sequence ETL in range [0,ETL-1]
[in]shotShot index (in range [0,how many times the sequence is played out per slice - 1]). For 3D, this is the combined number of shots over ky and kz
[in]kyK-space coordinate along the first (only for 2D) phase encoding direction (integer) in range [0, phaser.numlinestoacq-1]
[in]kzK-space coordinate along the second (use KS_NOTSET for 2D) phase encoding direction (integer) in range [0, zphaser.numlinestoacq-1]
Returns
void
395  {
396 #ifdef IPG
397  int oktoset = (phaseenc_plan_ptr->etl != KS_NOTSET) && (phaseenc_plan_ptr->is_cleared_on_tgt);
398 #else
399  int oktoset = phaseenc_plan_ptr->etl != KS_NOTSET;
400 #endif
401 
402  if (!oktoset || !phaseenc_plan_ptr || !(phaseenc_plan_ptr->entries) ||
403  shot < 0 || shot >= phaseenc_plan_ptr->num_shots ||
404  echo < 0 || echo >= phaseenc_plan_ptr->etl) {
405 
406  return;
407  }
408 
409  ky = (ky < 0) ? KS_NOTSET : ky; /* KS_NOTSET = shut off phase encoding */
410  kz = (kz < 0) ? KS_NOTSET : kz;
411 
412  phaseenc_plan_ptr->entries[echo + phaseenc_plan_ptr->etl * shot].ky = ky;
413  phaseenc_plan_ptr->entries[echo + phaseenc_plan_ptr->etl * shot].kz = kz;
414 
415 }
int is_cleared_on_tgt
Definition: KSFoundation.h:1745
int num_shots
Definition: KSFoundation.h:1739
KS_PHASEENCODING_COORD * entries
Definition: KSFoundation.h:1742
#define KS_NOTSET
Definition: KSFoundation.h:103
int ky
Definition: KSFoundation.h:1705
int kz
Definition: KSFoundation.h:1706
int etl
Definition: KSFoundation.h:1740

◆ ks_phaseencoding_print()

void ks_phaseencoding_print ( const KS_PHASEENCODING_PLAN phaseenc_plan_ptr)

Print out KS_PHASEENCODING_PLAN to a text file

In SIM (WTools), a file ks_phaseencodingtable.txt will be generated in the current directory On HW (MR scanner), the same file will be located in /usr/g/mrraw

Parameters
[in]phaseenc_plan_ptrPointer to KS_PHASEENCODING_PLAN
Returns
void
418  {
420  int shot, echo;
421 #ifdef PSD_HW
422  FILE *fp = fopen("/usr/g/mrraw/ks_phaseencodingtable.txt", "w");
423 #else
424  FILE *fp = fopen("./ks_phaseencodingtable.txt", "w");
425 #endif
426 
427  if (phaseenc_plan_ptr->etl == KS_NOTSET) {
428  return; /* we haven't set it up yet */
429  }
430 
431  fprintf(fp, "numshots (vertical): %d, etl (horizontal): %d\n\n", phaseenc_plan_ptr->num_shots, phaseenc_plan_ptr->etl);
432 
433  for (shot = 0; shot < phaseenc_plan_ptr->num_shots; shot++) {
434  for (echo = 0; echo < phaseenc_plan_ptr->etl; echo++) {
435  coord = ks_phaseencoding_get(phaseenc_plan_ptr, echo, shot);
436  if (coord.ky != KS_NOTSET) {
437  fprintf(fp, "[%03d", coord.ky);
438  } else {
439  fprintf(fp, "[***");
440  }
441  if (coord.kz != KS_NOTSET) {
442  fprintf(fp, ",%03d] ", coord.kz);
443  } else {
444  fprintf(fp, ",***] ");
445  }
446  }
447  fprintf(fp,"\n");
448  }
449 
450  fclose(fp);
451 
452 }
#define KS_INIT_PHASEENCODING_COORD
Definition: KSFoundation.h:219
int num_shots
Definition: KSFoundation.h:1739
#define KS_NOTSET
Definition: KSFoundation.h:103
int ky
Definition: KSFoundation.h:1705
int kz
Definition: KSFoundation.h:1706
Struct holding a 3D k-space phase encoding location (ky,kz)
Definition: KSFoundation.h:1704
int etl
Definition: KSFoundation.h:1740
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

◆ ks_phaseencoding_resize()

STATUS ks_phaseencoding_resize ( KS_PHASEENCODING_PLAN phaseenc_plan_ptr,
int  etl,
int  num_shots 
)

Reallocate memory for KS_PHASEENCODING_PLAN entries

For every (and custom) ks_phaseencoding_generate_** functions, this function must be called before ks_phaseencoding_set() so that there is memory allocataed for the array of KS_PHASEENCODING_COORD

Parameters
[in]phaseenc_plan_ptrPointer to KS_PHASEENCODING_PLAN
[in]etlThe number of acquisition window in the pulse sequence, or echo train length (ETL)
[in]num_shotsNumber of shots (i.e. how many times the sequence is played out per slice). For 3D, shot is over both ky and kz
Returns
void
456  {
457  void *ptr = NULL;
458  int i;
459 
460  ks_phaseencoding_init_tgt(phaseenc_plan_ptr);
461 
462  ptr = realloc(phaseenc_plan_ptr->entries, etl * num_shots * sizeof(KS_PHASEENCODING_COORD));
463  if (!ptr) {
464  return ks_error("%s: failed reallocation", __FUNCTION__);
465  }
466 
467  phaseenc_plan_ptr->etl = etl;
468  phaseenc_plan_ptr->num_shots = num_shots;
469  phaseenc_plan_ptr->entries = (KS_PHASEENCODING_COORD *) ptr;
470 
471  for (i = 0; i < etl * num_shots; i++) {
472  phaseenc_plan_ptr->entries[i].ky = KS_NOTSET;
473  phaseenc_plan_ptr->entries[i].kz = KS_NOTSET;
474  }
475 
476  return SUCCESS;
477 }
int num_shots
Definition: KSFoundation.h:1739
KS_PHASEENCODING_COORD * entries
Definition: KSFoundation.h:1742
#define KS_NOTSET
Definition: KSFoundation.h:103
int ky
Definition: KSFoundation.h:1705
STATUS ks_error(const char *format,...)
Definition: KSFoundation_common.c:70
int kz
Definition: KSFoundation.h:1706
Struct holding a 3D k-space phase encoding location (ky,kz)
Definition: KSFoundation.h:1704
int etl
Definition: KSFoundation.h:1740
void ks_phaseencoding_init_tgt(KS_PHASEENCODING_PLAN *phaseenc_plan_ptr)
Internal use for correct migration of KS_PHASEENCODING_PLAN from HOST to TGT (IPG)
Definition: KSFoundation_common.c:361

◆ ks_phaseencoding_generate_simple()

STATUS ks_phaseencoding_generate_simple ( KS_PHASEENCODING_PLAN phaseenc_plan_ptr,
const char *const  desc,
KS_PHASER phaser,
KS_PHASER zphaser 
)

Generation of a KS_PHASEENCODING_PLAN for any sequence having only one echo (or same phasenc step for all echoes)

For sequences having only one echo (ETL=1) or having more echoes but where all echoes have the same ky/kz coordinate, there is no special logic necessary regarding which order to traverse the ky-kz plane over shots.

Parallel imaging in ky (and kz for 3D), with acs lines, is set up as usual using ks_eval_phaser() first before calling this function. For 2D, the phase encoding object (KS_PHASER) is then passed in as the 2nd arg to this function, with the 3rd arg being NULL. For 3D, both KS_PHASERs are passed in as 2nd and 3rd args, each with their own acceleration and resolution. For both 2D and 3D, the KS_PHASEENCODING_PLAN will be set up based on the KS_PHASER(s), but for 2D all entries.kz in the KS_PHASEENCODING_PLAN will be KS_NOTSET (-1)

Parameters
[out]phaseenc_plan_ptrPointer to KS_PHASEENCODING_PLAN
[in]descA description (text string) of the KS_PHASEENCODING_PLAN object. This description is used in the psd plot.
[in]phaserPointer to the KS_PHASER object for the first (3D) / only (2D) phase encoding direction
[in]zphaserPointer to the KS_PHASER object for the second phase encoding direction (NULL for 2D)
Returns
void
481  {
482  int etl = 1;
483  int num_shots;
484  int shot, ky, kz;
485  STATUS status;
486 
487  strncpy(phaseenc_plan_ptr->description, desc, KS_DESCRIPTION_LENGTH - 1);
488  phaseenc_plan_ptr->description[KS_DESCRIPTION_LENGTH - 1] = 0;
489 
490  /* don't allow empty description or a description with leading space */
491  if (phaseenc_plan_ptr->description == NULL || phaseenc_plan_ptr->description[0] == ' ') {
492  return ks_error("%s: description of phase encoding plan (2nd arg) cannot be NULL or leading space", __FUNCTION__);
493  }
494 
495  num_shots = (zphaser != NULL) ? (phaser->numlinestoacq * zphaser->numlinestoacq) : phaser->numlinestoacq;
496 
497  status = ks_phaseencoding_resize(phaseenc_plan_ptr, etl, num_shots);
498  if (status != SUCCESS) return status;
499 
500  shot = 0;
501  if (zphaser != NULL) { /* 3D */
502  for (kz = 0; kz < zphaser->numlinestoacq; kz++) {
503  for (ky = 0; ky < phaser->numlinestoacq; ky++) {
504  ks_phaseencoding_set(phaseenc_plan_ptr, 0, shot++, phaser->linetoacq[ky], zphaser->linetoacq[kz]);
505  }
506  }
507  phaseenc_plan_ptr->zphaser = zphaser;
508  } else { /* 2D */
509  for (ky = 0; ky < phaser->numlinestoacq; ky++) {
510  ks_phaseencoding_set(phaseenc_plan_ptr, 0, shot++, phaser->linetoacq[ky], KS_NOTSET);
511  }
512  phaseenc_plan_ptr->zphaser = NULL;
513  }
514 
515  phaseenc_plan_ptr->phaser = phaser;
516 
517  return SUCCESS;
518 }
void ks_phaseencoding_set(KS_PHASEENCODING_PLAN *phaseenc_plan_ptr, int echo, int shot, int ky, int kz)
Set [ky,kz] coordinate from KS_PHASEENCODING_PLAN for given echo and shot
Definition: KSFoundation_common.c:395
STATUS ks_phaseencoding_resize(KS_PHASEENCODING_PLAN *phaseenc_plan_ptr, int etl, int num_shots)
Reallocate memory for KS_PHASEENCODING_PLAN entries
Definition: KSFoundation_common.c:456
#define KS_NOTSET
Definition: KSFoundation.h:103
int numlinestoacq
Definition: KSFoundation.h:1684
STATUS ks_error(const char *format,...)
Definition: KSFoundation_common.c:70
int linetoacq[KS_MAX_PHASEDYN]
Definition: KSFoundation.h:1685
const KS_PHASER * phaser
Definition: KSFoundation.h:1743
#define KS_DESCRIPTION_LENGTH
Definition: KSFoundation.h:185
KS_DESCRIPTION description
Definition: KSFoundation.h:1741
const KS_PHASER * zphaser
Definition: KSFoundation.h:1744

◆ ks_phaseencoding_generate_simple_ellipse()

STATUS ks_phaseencoding_generate_simple_ellipse ( KS_PHASEENCODING_PLAN phaseenc_plan_ptr,
const char *const  desc,
KS_PHASER phaser,
KS_PHASER zphaser 
)

Generation of a KS_PHASEENCODING_PLAN for any sequence having only one echo (or same phasenc step for all echoes) - ellipse version

For sequences having only one echo (ETL=1) or having more echoes but where all echoes have the same ky/kz coordinate, there is no special logic necessary regarding which order to traverse the ky-kz plane over shots.

Parallel imaging in ky (and kz for 3D), with acs lines, is set up as usual using ks_eval_phaser() first before calling this function. For 2D, the phase encoding object (KS_PHASER) is then passed in as the 2nd arg to this function, with the 3rd arg being NULL. For 3D, both KS_PHASERs are passed in as 2nd and 3rd args, each with their own acceleration and resolution. For both 2D and 3D, the KS_PHASEENCODING_PLAN will be set up based on the KS_PHASER(s), but for 2D all entries.kz in the KS_PHASEENCODING_PLAN will be KS_NOTSET (-1). This function will create an elliptical k-space in ky/kz. For rectangular k-space, use ks_phaseencoding_generate_simple.

Parameters
[out]phaseenc_plan_ptrPointer to KS_PHASEENCODING_PLAN
[in]descA description (text string) of the KS_PHASEENCODING_PLAN object. This description is used in the psd plot.
[in]phaserPointer to the KS_PHASER object for the first (3D) / only (2D) phase encoding direction
[in]zphaserPointer to the KS_PHASER object for the second phase encoding direction (NULL for 2D)
Returns
void
522  {
523  int etl = 1;
524  int ky, kz, i;
525  float radius_ky, radius_kz, center_ky, center_kz;
526  float x1, x2;
527  STATUS status;
528  int inellipse; /* Number of ky,kz points in the ellipse */
529 
530  strncpy(phaseenc_plan_ptr->description, desc, KS_DESCRIPTION_LENGTH - 1);
531  phaseenc_plan_ptr->description[KS_DESCRIPTION_LENGTH - 1] = 0;
532 
533  /* don't allow empty description or a description with leading space */
534  if (phaseenc_plan_ptr->description == NULL || phaseenc_plan_ptr->description[0] == ' ') {
535  return ks_error("%s: description of phase encoding plan (2nd arg) cannot be NULL or leading space", __FUNCTION__);
536  }
537 
538  if (zphaser == NULL) { /* We can't do ellipse k-space coverage (ky,kz) in 2D */
539  return ks_phaseencoding_generate_simple(phaseenc_plan_ptr, desc, phaser, NULL);
540  }
541 
542 
543  phaseenc_plan_ptr->phaser = phaser;
544  radius_ky = phaser->res/2.0;
545  center_ky = phaser->res/2.0 - 0.5;
546 
547  phaseenc_plan_ptr->zphaser = zphaser;
548  radius_kz = zphaser->res/2.0;
549  center_kz = zphaser->res/2.0 - 0.5;
550 
551  for (i = 0; i < 2; i++) { /* i = 0: check how many. i = 1: set up coords */
552 
553  if (i == 1) {
554  status = ks_phaseencoding_resize(phaseenc_plan_ptr, etl, inellipse);
555  if (status != SUCCESS) return status;
556  }
557  inellipse = 0;
558 
559  for (kz = 0; kz < zphaser->numlinestoacq; kz++) {
560  for (ky = 0; ky < phaser->numlinestoacq; ky++) {
561  x1 = (phaser->linetoacq[ky] - center_ky) / (float) radius_ky;
562  x2 = (zphaser->linetoacq[kz] - center_kz) / (float) radius_kz;
563  if ((x1 * x1 + x2 * x2) <= 1.0) {
564  if (i == 1) {
565  ks_phaseencoding_set(phaseenc_plan_ptr, 0, inellipse, phaser->linetoacq[ky], zphaser->linetoacq[kz]);
566  }
567  inellipse++;
568  }
569  } /* ky */
570  } /* kz */
571 
572  } /* i */
573 
574  return SUCCESS;
575 }
void ks_phaseencoding_set(KS_PHASEENCODING_PLAN *phaseenc_plan_ptr, int echo, int shot, int ky, int kz)
Set [ky,kz] coordinate from KS_PHASEENCODING_PLAN for given echo and shot
Definition: KSFoundation_common.c:395
STATUS ks_phaseencoding_resize(KS_PHASEENCODING_PLAN *phaseenc_plan_ptr, int etl, int num_shots)
Reallocate memory for KS_PHASEENCODING_PLAN entries
Definition: KSFoundation_common.c:456
int res
Definition: KSFoundation.h:1678
int numlinestoacq
Definition: KSFoundation.h:1684
STATUS ks_error(const char *format,...)
Definition: KSFoundation_common.c:70
int linetoacq[KS_MAX_PHASEDYN]
Definition: KSFoundation.h:1685
const KS_PHASER * phaser
Definition: KSFoundation.h:1743
#define KS_DESCRIPTION_LENGTH
Definition: KSFoundation.h:185
KS_DESCRIPTION description
Definition: KSFoundation.h:1741
const KS_PHASER * zphaser
Definition: KSFoundation.h:1744
STATUS ks_phaseencoding_generate_simple(KS_PHASEENCODING_PLAN *phaseenc_plan_ptr, const char *const desc, KS_PHASER *phaser, KS_PHASER *zphaser)
Generation of a KS_PHASEENCODING_PLAN for any sequence having only one echo (or same phasenc step for...
Definition: KSFoundation_common.c:481

◆ ks_fse_calcecho()

STATUS ks_fse_calcecho ( double *  bestecho,
double *  optecho,
int *  nacqlines_to_kspacecenter,
KS_PHASER pe,
ks_enum_pf_earlylate_te  pf_direction,
int  TE,
int  etl,
int  esp 
)

Calculates the ETL index corresponding the desired TE given a KS_PHASER object and echo spacing

Given the KS_PHASER object used in the FSE train (containing res and partial ky Fourier info), the desired effective echo time (TE [us]), the echo train length (etl) and the echo spacing between consecutive echoes (esp [us]), this function calculates two echo indices:

  • bestecho: is the 0-based index corresponding to the echo in the FSE train that should be placed in the center of k-space, i.e. the echo that most closely matches the desired TE. If bestecho is an integer value (although it is in double format), the data acquired for this echo should straddle the k-space center. bestecho can also be an interger + 0.5, indicating that there are two echo indices that should be placed around the k-space center. For example, floor(bestecho) corresponds to the echo index that should be placed just below the k-space center and ceil(bestecho) corresponds to the echo index that should be placed just above the k-space center. I.e. when bestecho = *.5, no single echo index will straddle the k-space center, and the k-space line located at ky position +0.5 will come from data acquired from echo #[floor(bestecho)] and ky position -0.5 correspondingly from echo #[ceil(bestecho)].
  • optecho: is the 0-based index corresponding to an ideal TE choice that would allow the data from the echoes in the FSE train to be placed out linearly each time. Doing so will reduce ghosting and T2 blurring. Hence, optecho ignores the input TE value here, but the calling function can use this information to determine k-space acquisition order, but also to suggest a TE (optecho * esp) that would result in optimal image quality for the chosen ETL.
Parameters
[out]bestechoPointer to the echo number in the train that should be placed at the center of k-space given the current ETL and ESP
[out]optechoPointer to the optimal echo number in the train that would allow k-space to be sampled linearly (to reduce T2-blurring and ghosting)
[out]nacqlines_to_kspacecenterPointer to a an integer with the number of k-space lines until center
[in]pePointer to a KS_PHASER
[in]pf_directionShould be either KS_PF_EARLY_TE or KS_PF_LATE_TE (applies to partial Fourier in ky to set if k-space center should be acquired early or late in the EPI train. Has no effect for full ky Fourier)
[in]TE(Echo Time in [us])
[in]etl(EchoTrain Length)
[in]esp(Echo Spacing in [us])
Return values
STATUSSUCCESS or FAILURE
580  {
581  int numshots, i;
582  int nacqlines_fullside, nacqlines_pfside;
583  double numshots_double;
584 
585  if (etl < 1) {
586  return ks_error("%s: ETL (arg 5) must be at least 1", __FUNCTION__);
587  }
588  if (pe->numlinestoacq < 1) {
589  return ks_error("%s: field '.numlinestoacq' in 3rd arg must be at least 1", __FUNCTION__);
590  }
591  if (etl > pe->numlinestoacq) {
592  return ks_error("%s: ETL (arg 5) cannot exceed number of lines to acquire", __FUNCTION__);
593  }
594  if (esp <= 0) {
595  return ks_error("%s: ESP (arg 6) must be larger than 1", __FUNCTION__);
596  }
597  if (pe->nover < 0) {
598  return ks_error("%s: Only top half of k-space supported for pFourier", __FUNCTION__);
599  }
600 
601  /* Number of shots */
602  numshots = CEIL_DIV(pe->numlinestoacq, etl);
603  numshots_double = (double) pe->numlinestoacq / (double) etl;
604 
605  /* index 'i' will stop incrementing as we pass the center of k-space (i=0 is top row of k-space).
606  Note that 'i' will be much less that res/2 (after break) even for full Fourier when
607  we have R > 1 and acslines */
608  for (i = 0; i < pe->numlinestoacq - 1; i++) {
609  if (((pe->res - 1) / 2.0 - pe->linetoacq[i]) < 0)
610  break;
611  }
612 
613  nacqlines_fullside = i;
614  nacqlines_pfside = pe->numlinestoacq - i; /* which is equal or almost equal to nacqlines_fullside for full Fourier */
615 
616  if (pf_direction == KS_PF_EARLY_TE) {
617  *nacqlines_to_kspacecenter = nacqlines_pfside;
618  } else {
619  *nacqlines_to_kspacecenter = nacqlines_fullside;
620  }
621 
622 
623  if (etl % 2) {
624  /* etl is odd. The optimal echo will straddle around the center of k-space */
625 
626  if (optecho != NULL)
627  *optecho = ceil((((double) *nacqlines_to_kspacecenter + numshots_double / 2.0) / numshots_double) - 1e-5); /* integer value since ETL is odd */
628 
629  if (bestecho != NULL) {
630  *bestecho = floor((double) TE / (double) esp + 0.5); /* integer */
631  if (*bestecho < 1)
632  *bestecho = 1;
633  if (*bestecho > etl)
634  *bestecho = etl;
635  }
636 
637  } else {
638  /* etl is even. The optimal two echoes will be placed above and below k-space center, respectively */
639 
640  if (optecho != NULL)
641  *optecho = ceil((double) *nacqlines_to_kspacecenter / numshots_double) + 0.5; /* half-integer value since ETL is even */
642 
643  if (bestecho != NULL) {
644  if (etl == 2) {
645  /* special case for ETL = 2 */
646  if (TE < 1.25 * esp)
647  *bestecho = 1; /* 1st echo straddling around k-space center */
648  else if (TE < 1.75 * esp)
649  *bestecho = 1.5; /* 1st/end echo below/above k-space center */
650  else
651  *bestecho = 2; /* 2nd echo straddling around k-space center */
652  } else {
653  *bestecho = floor((double) TE / (double) esp) + 0.5; /* half-integer */
654  if (*bestecho <= 1.5)
655  *bestecho = 1; /* minTE, back to integer 'bestecho' for center-out encoding */
656  if (*bestecho >= (etl - 0.5))
657  *bestecho = etl; /* max TE, back to integer 'bestecho' for out-center encoding */
658  }
659  }
660 
661  }
662 
663  if (optecho != NULL) {
664  /* Partial Fourier MinTE case, regardless if ETL is even or odd */
665  if ((pe->nover > 0) && (*nacqlines_to_kspacecenter <= numshots / 2))
666  *optecho = 1;
667 
668  /* Partial Fourier MaxTE case, regardless if ETL is even or odd */
669  if ((pe->nover < 0) && (*nacqlines_to_kspacecenter >= (pe->linetoacq[i] - numshots / 2)))
670  *optecho = etl;
671  }
672 
673  return SUCCESS;
674 
675 } /* ks_fse_calcecho() */
int res
Definition: KSFoundation.h:1678
int numlinestoacq
Definition: KSFoundation.h:1684
STATUS ks_error(const char *format,...)
Definition: KSFoundation_common.c:70
int linetoacq[KS_MAX_PHASEDYN]
Definition: KSFoundation.h:1685
int nover
Definition: KSFoundation.h:1679
Definition: KSFoundation.h:1899

◆ ks_phaseencoding_generate_2Dfse()

STATUS ks_phaseencoding_generate_2Dfse ( KS_PHASEENCODING_PLAN phaseenc_plan_ptr,
const char *const  desc,
KS_PHASER phaser,
KS_PHASER zphaser,
ks_enum_pf_earlylate_te  pf_direction,
int  TE,
int  etl,
int  esp 
)

2DFSE: Makes a KS_PHASEENCODING_PLAN depending on etl, desired TE, and echo spacing (esp)

This function is only executed on TGT and will return early when called on HOST.

The phase encoding sorting is depending on many factors. First, the desired TE will dictate which one (or two) of the echoes in the FSE train (of length etl) that should be placed in the center. This is determined based on the properties of the KS_PHASER object (phase encoding gradients in the FSE train), TE and esp by calling ksfse_calc_echo().

Second, the sorting of the echoes will first of all depend on whether bestecho is equal to optecho. If it is, then the etl echoes can be placed out linearly in k-space (from the bottom->top of k-space, or vice versa). This will produce the best image quality with minimal T2-blurring and FSE ghosting.

As partial ky Fourier is allowed, this function first checks on which side (top or bottom half) of k-space that has more room for the echo indices that are either before or after the bestecho index corresponding to the desired TE. The goal with this is to try to sweep k-space as linearly as possible in as much of the center portion of k-space as possible when bestecho != optecho.

When bestecho != optecho, there will be data from either the first or last echo indices that won't fit in k-space if placed next to a neighboring index. If this data would be placed on the other size of k-space (that still needs lines to be filled in), where e.g. echo index #0 would be placed next to echo index #(etl-1), strong ghosting will occur due to the much strong signal from echo #0 compared to #(etl-1). With an echo train length of etl, k-space is filled in numshots = ceil(numlinestoacq / etl. Using half of the numshots lines for each echo index a linear sweep of the center half of k-space can is layed out. To avoid FSE ghosting, the numshots/2 remaining lines for each early or late echo index are placed such as neighboring ky lines will never have a change in echo index by more than one. For example if etl = 8, esp = 10 ms and TE = 25, then bestecho = 1.5 and optecho = 4.5. We avoid to sort the echoes in the following k-space order for ghosting reasons: 7-6-0-1*2-3-4-5 (* = k-space center) since there will be a strong discontinuity between 6 and 0. Instead we use groups of numshots/2 per echo index (and get two groups per echo index in k-space): 5-4-3-2-1-0-0-1*2-3-4-5-6-7-6-5

Parameters
[out]phaseenc_plan_ptrPointer to KS_PHASEENCODING_PLAN
[in]descA description (text string) of the KS_PHASEENCODING_PLAN object. This description is used in the psd plot.
[in]phaserPointer to a KS_PHASER
[in]zphaserPointer to a KS_PHASER (3D) or NULL (2D)
[in]pf_directionShould be either KS_PF_EARLY_TE or KS_PF_LATE_TE (applies to partial Fourier in ky to set if k-space center should be acquired early or late in the EPI train. Has no effect for full ky Fourier)
[in]TE(Echo Time in [us])
[in]etl(EchoTrain Length)
[in]esp(Echo Spacing in [us])
Return values
STATUSSUCCESS or FAILURE

Grouping shots for each of the ETL echoes in k-space: We don't place all shots for each echo together in k-space since we need to make a z-shaped pattern to avoid ghosting. Instead we let half of the shots be one group (of size 'cgroupsize') that we place in the central ~50% of k-space, which allows us to get a linear sweep in this central half of k-space using all 'etl' echoes, regardless of the chosen effective TE. The actual center-ness depends on TE, and the remaining of k-space is filled using the echoes belonging to the latter 'numshots-cgroupsize' number of shots. At the edges of k-space, numshots*etl seldom match up to the chosen opyres, but numshots*etl is always >= opyres thanks to the CEIL_DIV at the beginning of this function. The surplus echoes we don't need are numshots*etl - opyres (for full Fourier and no ARC), and as they are already tagged as -1 = KS_NOTSET in the beginning of this function, they will be placed in baseline view (0) in the ksfse_scan_acqloop()

679  {
680  int i, j, ky, kz;
681  int numshots, numkz;
682  double bestecho, optecho;
683  int nacqlines_to_kspacecenter;
684  int cgroupsize, halfgroupsize;
685  int cstart, cshift;
686  STATUS status;
687 
688  strncpy(phaseenc_plan_ptr->description, desc, KS_DESCRIPTION_LENGTH - 1);
689  phaseenc_plan_ptr->description[KS_DESCRIPTION_LENGTH - 1] = 0;
690 
691  /* don't allow empty description or a description with leading space */
692  if (phaseenc_plan_ptr->description == NULL || phaseenc_plan_ptr->description[0] == ' ') {
693  return ks_error("%s: description of phase encoding plan (2nd arg) cannot be NULL or leading space", __FUNCTION__);
694  }
695 
696  if (etl < 1) {
697  return SUCCESS; /* we haven't set it up yet */
698  }
699  if (phaser->numlinestoacq < 2) {
700  return ks_error("%s: field '.numlinestoacq' in 2nd arg must be at least 2", __FUNCTION__);
701  }
702  if (etl > phaser->numlinestoacq) {
703  return ks_error("%s: ETL cannot exceed number of lines to acquire", __FUNCTION__);
704  }
705  if (phaser->nover < 0) {
706  return ks_error("%s: Only top half of k-space supported for pFourier", __FUNCTION__);
707  }
708 
709  phaseenc_plan_ptr->phaser = phaser;
710  phaseenc_plan_ptr->zphaser = zphaser;
711 
712  /* Number of shots (2D) */
713  numshots = CEIL_DIV(phaser->numlinestoacq, etl);
714 
715  /* Number of kz */
716  numkz = (zphaser != NULL) ? zphaser->numlinestoacq : 1;
717 
718  /* allocate KS_PHASEENCODING_PLAN table (all entries will be initialized to KS_NOTSET) */
719  status = ks_phaseencoding_resize(phaseenc_plan_ptr, etl, numshots * numkz);
720  if (status != SUCCESS) return status;
721 
722 
723  for (kz = 0; kz < numkz; kz++) {
724 
725  if (etl == 1) {
726 
727  /* Special case for ETL = 1. Just acquire the data in order */
728  for (ky = 0; ky < phaser->numlinestoacq; ky++) {
729  ks_phaseencoding_set(phaseenc_plan_ptr,
730  0, ky + kz * phaser->numlinestoacq,
731  phaser->linetoacq[ky], (zphaser != NULL) ? zphaser->linetoacq[kz] : KS_NOTSET);
732  }
733 
734  } else {
735 
736  /* ETL > 1 (this is a long else-statement) */
737 
738  /* get the best echo number for the given TE/ETL/ESP, and also the optimal echo number that would allow for a linear k-space sweep */
739  status = ks_fse_calcecho(&bestecho, &optecho, &nacqlines_to_kspacecenter, phaser, pf_direction, TE, etl, esp);
740  if (status != SUCCESS) return status;
741 
742 
743  if (areSame(bestecho, optecho)) {
744  /* we can place the echoes linearly in k-space in groups of 'numshots' per echo given the current combination of TE, ETL and ESP */
745  cgroupsize = numshots;
746  halfgroupsize = (int) floor((float) phaser->numlinestoacq / (etl * 2.0) + 0.5);
747  } else {
757  cgroupsize = (int) ceil((float) phaser->numlinestoacq / (etl * 2.0)); /* ~numshots/2 lines per etl in the central 50% of k-space */
758  halfgroupsize = (int) floor((float) phaser->numlinestoacq / (etl * 4.0) + 0.5);
759  }
760 
761  /* if bestecho == optecho: Place out all echoes
762  if bestecho != optecho: Place out the center half of lines */
763  if (areSame(bestecho,1)) /* minTE */
764  cshift = cgroupsize;
765  else if (areSame(bestecho, etl)) /* maxTE */
766  cshift = 0;
767  else
768  cshift = halfgroupsize; /* non-extreme TE */
769  cstart = (int)((nacqlines_to_kspacecenter + cshift) - cgroupsize * bestecho);
770  if (pf_direction == KS_PF_EARLY_TE)
771  cstart = (phaser->numlinestoacq - 1) - cstart;
772  ky = cstart;
773  for (i = 0; i < etl; i++) {
774  for (j = 0; j < cgroupsize; j++) {
775  if (ky >= 0 && ky < phaser->numlinestoacq) {
776  ks_phaseencoding_set(phaseenc_plan_ptr, i, j + kz * numshots, phaser->linetoacq[ky], (zphaser != NULL) ? zphaser->linetoacq[kz] : KS_NOTSET);
777  }
778  if (pf_direction == KS_PF_EARLY_TE)
779  ky--;
780  else
781  ky++;
782  }
783  }
784 
785  if (!areSame(bestecho, optecho)) {
786  /* remaining 'numshots-cgroupsize' number of shots for late echoes */
787  i = etl - 1;
788  while (i >= 0) {
789  for (j = cgroupsize; j < numshots; j++) {
790  if (ky >= 0 && ky < phaser->numlinestoacq) {
791  ks_phaseencoding_set(phaseenc_plan_ptr, i, j + kz * numshots, phaser->linetoacq[ky], (zphaser != NULL) ? zphaser->linetoacq[kz] : KS_NOTSET);
792  }
793  if (pf_direction == KS_PF_EARLY_TE)
794  ky--;
795  else
796  ky++;
797  } /* for j */
798  i--;
799  }
800 
801  /* remaining 'numshots-cgroupsize' number of shots for early echoes */
802  if (pf_direction == KS_PF_EARLY_TE)
803  ky = cstart + 1;
804  else
805  ky = cstart - 1;
806  i = 0;
807  while (i < etl) {
808  for (j = cgroupsize; j < numshots; j++) {
809  if (ky >= 0 && ky < phaser->numlinestoacq) {
810  ks_phaseencoding_set(phaseenc_plan_ptr, i, j + kz * numshots, phaser->linetoacq[ky], (zphaser != NULL) ? zphaser->linetoacq[kz] : KS_NOTSET);
811  }
812  if (pf_direction == KS_PF_EARLY_TE)
813  ky++;
814  else
815  ky--;
816  } /* for j */
817  i++;
818  }
819  } /* bestecho != optecho */
820 
821  } /* etl > 1 */
822 
823  } /* numkz */
824 
825 
826  return SUCCESS;
827 
828 
829 } /* ks_phaseencoding_generate_2Dfse() */
void ks_phaseencoding_set(KS_PHASEENCODING_PLAN *phaseenc_plan_ptr, int echo, int shot, int ky, int kz)
Set [ky,kz] coordinate from KS_PHASEENCODING_PLAN for given echo and shot
Definition: KSFoundation_common.c:395
STATUS ks_phaseencoding_resize(KS_PHASEENCODING_PLAN *phaseenc_plan_ptr, int etl, int num_shots)
Reallocate memory for KS_PHASEENCODING_PLAN entries
Definition: KSFoundation_common.c:456
#define areSame(a, b)
Definition: KSFoundation.h:119
#define KS_NOTSET
Definition: KSFoundation.h:103
int numlinestoacq
Definition: KSFoundation.h:1684
STATUS ks_error(const char *format,...)
Definition: KSFoundation_common.c:70
int linetoacq[KS_MAX_PHASEDYN]
Definition: KSFoundation.h:1685
const KS_PHASER * phaser
Definition: KSFoundation.h:1743
#define KS_DESCRIPTION_LENGTH
Definition: KSFoundation.h:185
STATUS ks_fse_calcecho(double *bestecho, double *optecho, int *nacqlines_to_kspacecenter, KS_PHASER *pe, ks_enum_pf_earlylate_te pf_direction, int TE, int etl, int esp)
Calculates the ETL index corresponding the desired TE given a KS_PHASER object and echo spacing...
Definition: KSFoundation_common.c:580
KS_DESCRIPTION description
Definition: KSFoundation.h:1741
const KS_PHASER * zphaser
Definition: KSFoundation.h:1744
int nover
Definition: KSFoundation.h:1679
Definition: KSFoundation.h:1899

◆ ks_phaseencoding_generate_epi()

STATUS ks_phaseencoding_generate_epi ( KS_PHASEENCODING_PLAN phaseenc_plan_ptr,
const char *const  desc,
const KS_EPI epitrain,
const ks_enum_epiblipsign  blipsign,
const ks_enum_pf_earlylate_te  pf_direction,
const int  numileavestoacq,
ks_enum_sweep_order  sweep_order,
int  numsegments,
const int  caipi_delta 
)

EPI: Makes a KS_PHASEENCODING_PLAN suitable for 2D or 3D epi

The etl is the number of readouts in the EPI train, derived from the phaser. The number of ky shots is also given by the phaser, while the number of kz shots is given by zphaser. Any partial fourier is handled by the phasers, and acceleration and acs lines in the z direction is handled by zphaser. For 3D EPI, the number of shots in the phase encoding plan will be the product of the number of shots in the ky direction and the number of kz lines to acquire.

Each shot will be played out in a single kz plane. The shot order will be linear in ky and kz. The ky shots will be in the inner loop, unless kzshotsfirst is TRUE.

Parameters
[out]phaseenc_plan_ptrPointer to the phase encoding plan
[in]descA description (text string) of the KS_PHASEENCODING_PLAN object. This description is used in the psd plot.
[in]epitrainPointer to a KS_EPI
[in]blipsignShould be either KS_EPI_POSBLIPS or KS_EPI_NEGBLIPS. Determines distortion direction.
[in]pf_directionShould be either KS_PF_EARLY_TE or KS_PF_LATE_TE (applies to partial Fourier in ky to set if k-space center should be acquired early or late in the EPI train. Has no effect for full ky Fourier)
[in]numileavestoacqNumber of ky interleaves to play. Must be within 1-numInterleaves and an integer factor of numInterleaves. Set =1 for single-shot EPI. Set to phaser.R for full multi-shot EPI.
[in]sweep_orderSweep order for shots as defined in KSFoundation.h (ks_enum_sweep_order). Has no effect on distortions.
[in]numsegmentsNumber of kz encodes to play
[in]caipi_deltaUse CAIPIRINHA sampling pattern (affects 3D epi only)
Return values
STATUSSUCCESS or FAILURE
833  {
834 
835  int numileaves = epitrain->blipphaser.R;
836 
837  if (epitrain->blipphaser.numlinestoacq < 2 && (numileaves != epitrain->blipphaser.res)) {
838  return ks_error("%s: field '.numlinestoacq' of epitrain->blipphaser must be at least 2, not %d", __FUNCTION__, epitrain->blipphaser.numlinestoacq);
839  }
840 
841  strncpy(phaseenc_plan_ptr->description, desc, KS_DESCRIPTION_LENGTH - 1);
842  phaseenc_plan_ptr->description[KS_DESCRIPTION_LENGTH - 1] = 0;
843 
844  /* don't allow empty description or a description with leading space */
845  if (phaseenc_plan_ptr->description == NULL || phaseenc_plan_ptr->description[0] == ' ') {
846  return ks_error("%s: description of phase encoding plan (2nd arg) cannot be NULL or leading space", __FUNCTION__);
847  }
848 
849  int readout, shot, interleaf, kyview, kzview, kzind;
850  int etl, numfullsegments, numshotsforsegment, totalnumshots, i;
851  int shift;
852  STATUS status;
853 
854  /* Number of shots (2D) */
855  if (numileavestoacq < 1 || numileavestoacq > numileaves || (numileaves % numileavestoacq != 0)) {
856  return ks_error("%s: numileavestoacq (=%d) must be in range 1-blipphaser.R and an integer factor of blipphaser.R (=%d)", __FUNCTION__, numileavestoacq, numileaves);
857  }
858 
859  /* Number of segments (3D) */
860  if (epitrain->zphaser.res != KS_NOTSET && (numsegments < 1 || numsegments > epitrain->zphaser.numlinestoacq)) {
861  return ks_error("%s: numsegments (=%d) must be in range 1-zphaser.numlinestoacq (=%d)", __FUNCTION__, numsegments, epitrain->zphaser.numlinestoacq);
862  }
863 
864  /* Number of readouts per EPI train (etl) */
865  etl = epitrain->blipphaser.numlinestoacq;
866 
867  /* Check number of kz segments and how many should be fully sampled (controlled by zphaser.nacslines) */
868  if (epitrain->zphaser.res == KS_NOTSET) { /* 2D epi */
869  numsegments = 1;
870  numfullsegments = (numileavestoacq == numileaves);
871  } else { /* 3D epi */
872  if (epitrain->zphaser.R == 1) {
873  numfullsegments = epitrain->zphaser.nacslines; /* special interpretation for zR=1 */
874  } else { /* zR > 1 */
875  if (epitrain->zphaser.nacslines > 0) {
876  numfullsegments = 1; /* initialize */
877  for (kzind = 1; kzind < numsegments; kzind++) {
878  if (abs(epitrain->zphaser.linetoacq[kzind]-epitrain->zphaser.linetoacq[kzind-1])==1) {
879  numfullsegments++;
880  }
881  }
882  } else { /* no kz "acs lines" */
883  numfullsegments = 0;
884  }
885  }
886  }
887 
888  /* allocate KS_PHASEENCODING_PLAN table (all entries will be initialized to KS_NOTSET) */
889  totalnumshots = numfullsegments * numileaves + (numsegments-numfullsegments) * numileavestoacq;
890  status = ks_phaseencoding_resize(phaseenc_plan_ptr, etl, totalnumshots);
891 
892  phaseenc_plan_ptr->phaser = &epitrain->blipphaser;
893  phaseenc_plan_ptr->zphaser = &epitrain->zphaser;
894 
895  if (status != SUCCESS) return status;
896 
897  shot = 0; /* initialize */
898  int ileaves[numileaves]; /* interleaf indices */
899 
900  for (kzind = 0; kzind < numsegments; kzind++) {
901  kzview = (epitrain->zphaser.numlinestoacq > 0) ? epitrain->zphaser.linetoacq[kzind] : KS_NOTSET;
902 
903  if (kzview != KS_NOTSET && abs(kzview*2 - (epitrain->zphaser.res-1)) <= numfullsegments) {
904  numshotsforsegment = numileaves;
905  } else {
906  numshotsforsegment = numileavestoacq;
907  }
908 
909  if (sweep_order == KS_SWEEP_ORDER_TOP_DOWN) {
910  for (i = 0; i < numshotsforsegment; i++) {
911  ileaves[i] = i;
912  }
913  } else if (sweep_order == KS_SWEEP_ORDER_BOTTOM_UP) {
914  for (i = 0; i < numshotsforsegment; i++) {
915  ileaves[numshotsforsegment - i - 1] = i;
916  }
917  } else if (sweep_order == KS_SWEEP_ORDER_OUTSIDE_IN) {
918  for (i = 0; i < numshotsforsegment; i++) {
919  ileaves[i] = (i % 2) ? numshotsforsegment - 1 - i/2 : i/2;
920  }
921  } else if (sweep_order == KS_SWEEP_ORDER_CENTER_OUT) {
922  for (i = 0; i < numshotsforsegment; i++) {
923  ileaves[numshotsforsegment - i - 1] = (i % 2) ? numshotsforsegment - 1 - i/2 : i/2;
924  }
925  }
926  for (i = 0; i < numshotsforsegment; i++) {
927  interleaf = ileaves[i];
928  for (readout = 0; readout < etl; readout++) {
929  shift = interleaf * (numileaves / numshotsforsegment); /* interleaf shift */
930  shift = (shift + (kzview/epitrain->zphaser.R) * caipi_delta) % numileaves; /* CAIPIRINHA shift */
931  if ((epitrain->blipphaser.nover != 0) && (((pf_direction == KS_PF_LATE_TE) && (blipsign == KS_EPI_POSBLIPS)) || ((pf_direction == KS_PF_EARLY_TE) && (blipsign == KS_EPI_NEGBLIPS))) ) {
932  /* shift kspace area to lower half */
933  shift += epitrain->blipphaser.res - (epitrain->blipphaser.res/2 + epitrain->blipphaser.nover);
934  }
935  if (blipsign == KS_EPI_NEGBLIPS) {
936  kyview = epitrain->blipphaser.linetoacq[readout] + shift; /* admittedly, .linetoacq[] has lost its role a bit for EPI with all shifting we do */
937  } else if (blipsign == KS_EPI_POSBLIPS) {
938  kyview = epitrain->blipphaser.linetoacq[(etl - 1) - readout] + shift; /* admittedly, .linetoacq[] has lost its role a bit for EPI with all shifting we do */
939  } else if (blipsign == KS_EPI_NOBLIPS) {
940  kyview = KS_NOTSET;
941  kzview = KS_NOTSET;
942  } else {
943  return ks_error("%s: blipsign must be KS_EPI_POSBLIPS or KS_EPI_NEGBLIPS, not %d", __FUNCTION__, blipsign);
944  }
945  ks_phaseencoding_set(phaseenc_plan_ptr, readout, shot, kyview, kzview);
946  } /* readout */
947  shot++;
948  } /* interleaf */
949  } /* kzind */
950 
951  return SUCCESS;
952 
953 } /* ks_phaseencoding_generate_epi() */
void ks_phaseencoding_set(KS_PHASEENCODING_PLAN *phaseenc_plan_ptr, int echo, int shot, int ky, int kz)
Set [ky,kz] coordinate from KS_PHASEENCODING_PLAN for given echo and shot
Definition: KSFoundation_common.c:395
int R
Definition: KSFoundation.h:1680
STATUS ks_phaseencoding_resize(KS_PHASEENCODING_PLAN *phaseenc_plan_ptr, int etl, int num_shots)
Reallocate memory for KS_PHASEENCODING_PLAN entries
Definition: KSFoundation_common.c:456
int res
Definition: KSFoundation.h:1678
int nacslines
Definition: KSFoundation.h:1681
Definition: KSFoundation.h:1904
#define KS_NOTSET
Definition: KSFoundation.h:103
KS_PHASER blipphaser
Definition: KSFoundation.h:1840
int numlinestoacq
Definition: KSFoundation.h:1684
STATUS ks_error(const char *format,...)
Definition: KSFoundation_common.c:70
KS_PHASER zphaser
Definition: KSFoundation.h:1841
int linetoacq[KS_MAX_PHASEDYN]
Definition: KSFoundation.h:1685
const KS_PHASER * phaser
Definition: KSFoundation.h:1743
#define KS_DESCRIPTION_LENGTH
Definition: KSFoundation.h:185
Definition: KSFoundation.h:1905
KS_DESCRIPTION description
Definition: KSFoundation.h:1741
Definition: KSFoundation.h:1899
const KS_PHASER * zphaser
Definition: KSFoundation.h:1744
Definition: KSFoundation.h:1902
Definition: KSFoundation.h:1903
Definition: KSFoundation.h:1929
int nover
Definition: KSFoundation.h:1679
Definition: KSFoundation.h:1929
Definition: KSFoundation.h:1929
Definition: KSFoundation.h:1899

◆ ks_phaseencoding_generate_allepiplans()

STATUS ks_phaseencoding_generate_allepiplans ( KS_PHASEENCODING_ALLEPIPLANS  phaseenc_plans,
const char *const  desc,
const KS_EPI epitrain,
const int  caipi_delta 
)

EPI: Makes all 8 KS_PHASEENCODING_PLANs possible for 2D or 3D epi

Parameters
[out]phaseenc_plansPointer to the phase encoding plan array (KS_PHASEENCODING_ALLEPIPLANS containing KS_EPI_PEPLAN_NUMPLANS elements)
[in]descA description (text string) of the KS_PHASEENCODING_PLAN object. This description is used in the psd plot.
[in]epitrainPointer to a KS_EPI
[in]caipi_deltaUse CAIPIRINHA sampling pattern (affects 3D epi only)
Return values
STATUSSUCCESS or FAILURE
959  {
960  ks_enum_epiblipsign kspace_direction;
961  ks_enum_pf_earlylate_te pf_direction;
962  int numileavestoacq, p;
963  KS_DESCRIPTION tmpdesc;
964  STATUS status;
965  int is3Dscan = epitrain->zphaser.res > 1;
966  int numileaves = epitrain->blipphaser.R;
967 
968  for (p = 0; p < KS_EPI_PEPLAN_NUMPLANS; p++) {
969 
970  switch (p) {
972  pf_direction = KS_PF_EARLY_TE;
973  kspace_direction = KS_EPI_POSBLIPS;
974  numileavestoacq = (is3Dscan) ? (numileaves / epitrain->R_3d) : numileaves;
975  sprintf(tmpdesc, "%s:mShot/EarlyTE/blip+", desc);
976  break;
978  pf_direction = KS_PF_EARLY_TE;
979  kspace_direction = KS_EPI_POSBLIPS;
980  numileavestoacq = 1;
981  sprintf(tmpdesc, "%s:Undersampled/EarlyTE/blip+", desc);
982  break;
984  pf_direction = KS_PF_EARLY_TE;
985  kspace_direction = KS_EPI_NEGBLIPS;
986  numileavestoacq = (is3Dscan) ? (numileaves / epitrain->R_3d) : numileaves;
987  sprintf(tmpdesc, "%s:mShot/EarlyTE/blip-", desc);
988  break;
990  pf_direction = KS_PF_EARLY_TE;
991  kspace_direction = KS_EPI_NEGBLIPS;
992  numileavestoacq = 1;
993  sprintf(tmpdesc, "%s:Undersampled/EarlyTE/blip-", desc);
994  break;
996  pf_direction = KS_PF_LATE_TE;
997  kspace_direction = KS_EPI_POSBLIPS;
998  numileavestoacq = (is3Dscan) ? (numileaves / epitrain->R_3d) : numileaves;
999  sprintf(tmpdesc, "%s:mShot/LateTE/blip+", desc);
1000  break;
1002  pf_direction = KS_PF_LATE_TE;
1003  kspace_direction = KS_EPI_POSBLIPS;
1004  numileavestoacq = 1;
1005  sprintf(tmpdesc, "%s:Undersampled/LateTE/blip+", desc);
1006  break;
1008  pf_direction = KS_PF_LATE_TE;
1009  kspace_direction = KS_EPI_NEGBLIPS;
1010  numileavestoacq = (is3Dscan) ? (numileaves / epitrain->R_3d) : numileaves;
1011  sprintf(tmpdesc, "%s:mShot/LateTE/blip-", desc);
1012  break;
1014  pf_direction = KS_PF_LATE_TE;
1015  kspace_direction = KS_EPI_NEGBLIPS;
1016  numileavestoacq = 1;
1017  sprintf(tmpdesc, "%s:Undersampled/LateTE/blip-", desc);
1018  break;
1019  default:
1020  return ks_error("%s:Error, we should not have arrived here", __FUNCTION__);
1021  break;
1022  }
1023 
1024  status = ks_phaseencoding_generate_epi(&phaseenc_plans[p], tmpdesc,
1025  epitrain,
1026  kspace_direction,
1027  pf_direction,
1028  numileavestoacq,
1030  epitrain->zphaser.numlinestoacq,
1031  caipi_delta);
1032  if (status != SUCCESS) return status;
1033 
1034  }
1035 
1036  return SUCCESS;
1037 
1038 } /* ks_phaseencoding_generate_allepiplans() */
STATUS ks_phaseencoding_generate_epi(KS_PHASEENCODING_PLAN *phaseenc_plan_ptr, const char *const desc, const KS_EPI *epitrain, const ks_enum_epiblipsign blipsign, const ks_enum_pf_earlylate_te pf_direction, const int numileavestoacq, ks_enum_sweep_order sweep_order, int numsegments, const int caipi_delta)
EPI: Makes a KS_PHASEENCODING_PLAN suitable for 2D or 3D epi
Definition: KSFoundation_common.c:833
ks_enum_pf_earlylate_te
Definition: KSFoundation.h:1899
int R_3d
Definition: KSFoundation.h:1842
Definition: KSFoundation.h:1931
int R
Definition: KSFoundation.h:1680
int res
Definition: KSFoundation.h:1678
Definition: KSFoundation.h:1938
Definition: KSFoundation.h:1932
KS_PHASER blipphaser
Definition: KSFoundation.h:1840
int numlinestoacq
Definition: KSFoundation.h:1684
Definition: KSFoundation.h:1933
STATUS ks_error(const char *format,...)
Definition: KSFoundation_common.c:70
KS_PHASER zphaser
Definition: KSFoundation.h:1841
Definition: KSFoundation.h:1935
Definition: KSFoundation.h:1899
Definition: KSFoundation.h:1937
Definition: KSFoundation.h:1902
Definition: KSFoundation.h:1929
Definition: KSFoundation.h:1934
ks_enum_epiblipsign
Definition: KSFoundation.h:1929
Definition: KSFoundation.h:1939
char KS_DESCRIPTION[KS_DESCRIPTION_LENGTH]
Definition: KSFoundation.h:261
Definition: KSFoundation.h:1929
Definition: KSFoundation.h:1899
Definition: KSFoundation.h:1936

◆ ks_phaseencoding_allepiplans_setechoes()

STATUS ks_phaseencoding_allepiplans_setechoes ( KS_PHASEENCODING_PLAN_16PTRS  peplan_echoes,
KS_PHASEENCODING_ALLEPIPLANS  allepiplans,
int  nechoes,
int  multishot_flag,
int  min1stTE,
int  mirrorTE,
ks_enum_epiblipsign  blipsign 
)

EPI: Makes a pointer to an entry in KS_PHASEENCODING_ALLEPIPLANS for each echo and store it in an array for scan loop usage

Parameters
[out]peplan_echoesPointers to the phase encoding plan array to use for each EPI train in the sequence (max 16 EPI trains per sequence playout)
[in]allepiplansAll possible EPI plans for current EPI train (with given read.res and blipphaser.res/.nover)
[in]nechoesNumber of EPI train (echoes) in sequence (max 16)
[in]multishot_flagMultishot EPI or underampled EPI (both controlled by epitrain.blipphaser.R)
[in]min1stTEFor partial ky Fourier (.blipphaser.nover > 0), should 1st EPI train pass k-space center early or late. Has no effect for full ky Fourier
[in]mirrorTEShould lateTE/earlyTE alternate between EPI trains (as when an RF refocusing pulse is placed in-between trains)
[in]blipsignPositive (KS_EPI_POSBLIPS) or negative (KS_EPI_NEGBLIPS) to control k-space direction and hence the geometric distortion direction
Return values
STATUSSUCCESS or FAILURE
1043  {
1044 /*
1045  Assumes KSFoundation.h has the ks_enum_epiplans enum in the following ORDER:
1046  KS_EPI_PEPLAN_EARLYTE_BOTTOMUP_UNDERSAMPLED, // 0
1047  KS_EPI_PEPLAN_EARLYTE_BOTTOMUP_MULTISHOT, // 1
1048  KS_EPI_PEPLAN_EARLYTE_TOPDOWN_UNDERSAMPLED, // 2
1049  KS_EPI_PEPLAN_EARLYTE_TOPDOWN_MULTISHOT, // 3
1050  KS_EPI_PEPLAN_LATETE_BOTTOMUP_UNDERSAMPLED, // 4
1051  KS_EPI_PEPLAN_LATETE_BOTTOMUP_MULTISHOT, // 5
1052  KS_EPI_PEPLAN_LATETE_TOPDOWN_UNDERSAMPLED, // 6
1053  KS_EPI_PEPLAN_LATETE_TOPDOWN_MULTISHOT, // 7
1054  KS_EPI_PEPLAN_NUMPLANS // 8
1055 */
1056 
1057  int echo, whichplan;
1058 
1059  if (peplan_echoes == NULL) {
1060  return ks_error("%s: arg 1 (output) is NULL", __FUNCTION__);
1061  }
1062  if (allepiplans == NULL) {
1063  return ks_error("%s: arg 2 (input) is NULL", __FUNCTION__);
1064  }
1065  if (nechoes < 1 || nechoes > 16) {
1066  return ks_error("%s: nechoes (%d, arg 3) must be in range [1,16]", __FUNCTION__, nechoes);
1067  }
1068  if ( !(multishot_flag == 0 || multishot_flag == 1) ) {
1069  return ks_error("%s: multishot_flag (%d, arg 4) must be 0 or 1", __FUNCTION__, multishot_flag);
1070  }
1071  if ( !(min1stTE == 0 || min1stTE == 1) ) {
1072  return ks_error("%s: min1stTE (%d, arg 5) must be 0 or 1", __FUNCTION__, min1stTE);
1073  }
1074  if ( !(mirrorTE == 0 || mirrorTE == 1) ) {
1075  return ks_error("%s: mirrorTE (%d, arg 6) must be 0 or 1", __FUNCTION__, mirrorTE);
1076  }
1077  if ( !(blipsign == KS_EPI_POSBLIPS || blipsign == KS_EPI_NEGBLIPS || blipsign == KS_EPI_NOBLIPS) ) {
1078  return ks_error("%s: blipsign (%d, arg 7) must be -1, 0 or 1", __FUNCTION__, blipsign);
1079  }
1080 
1081  for (echo = 0; echo < 16; echo++) {
1082  peplan_echoes[echo] = NULL; /* reset all pointers in case #EPI trains has been reduced */
1083  }
1084 
1085  for (echo = 0; echo < nechoes; echo ++) {
1086  if (mirrorTE) { /* with 'mirrorTE', lateTE and earlyTE are alternating across echoes */
1087  whichplan = (min1stTE) ? ((echo % 2) * 4) : (((echo % 2) == 0) * 4); /* 0 or 4 */
1088  } else {
1089  whichplan = (min1stTE) ? 0 : 4;
1090  }
1091  whichplan += (blipsign == KS_EPI_NEGBLIPS) ? 2 : 0;
1092  whichplan += multishot_flag > 0;
1093 
1094  peplan_echoes[echo] = &allepiplans[whichplan];
1095  }
1096 
1097  return SUCCESS;
1098 
1099 } /* ks_phaseencoding_allepiplans_setechoes() */
STATUS ks_error(const char *format,...)
Definition: KSFoundation_common.c:70
Definition: KSFoundation.h:1929
Definition: KSFoundation.h:1929
Definition: KSFoundation.h:1929

◆ 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
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
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_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_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 }

◆ 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
int nslices_per_pass
Definition: KSFoundation.h:1353
int npasses
Definition: KSFoundation.h:1352
int nslices
Definition: KSFoundation.h:1351

◆ 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 }

◆ 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 }

◆ 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 }

◆ 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
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_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 }
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_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 */
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
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

◆ 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 */
Definition: KSFoundation.h:1953
Definition: KSFoundation.h:1953
Definition: KSFoundation.h:1953
Definition: KSFoundation.h:1953
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
Definition: KSFoundation.h:1953

◆ 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_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 */
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
Definition: KSFoundation.h:1953
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

◆ 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
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_error()

STATUS ks_error ( const char *  format,
  ... 
)

Common error message function for HOST and TGT

  • On the MR-scanner, this function will append error messages with a timestamp to the file /usr/g/mrraw/ksf_errors.txt in addition to sending an epic_error(), which will show the error at the bottom of the UI
  • In simluation, the error message will be seen both in Evaltool and in the main WTools window
Parameters
[in]formatStandard C printf input arguments
Return values
STATUSReturns FAILURE

◆ ks_dbg()

STATUS STATUS ks_dbg ( const char *  format,
  ... 
)

Common debug message function for HOST and TGT

  • On the MR-scanner, this function will append error messages with a timestamp to the file /usr/g/mrraw/ksf_debug.txt
Parameters
[in]formatStandard C printf input arguments
Return values
STATUSReturns SUCCESS

◆ existfile()

STATUS STATUS STATUS existfile ( const char *  fname)
175  {
176  FILE *file;
177  if ((file = fopen(fname, "r"))) {
178  fclose(file);
179  return SUCCESS;
180  }
181  return FAILURE;
182 }

◆ ks_syslimits_hasICEhardware()

int ks_syslimits_hasICEhardware ( )

Check for ICE Hardware (e.g. RX27+ on SIGNA Premier)

Return values
int1: ICE 0: MGD
186  {
187  int isice = FALSE;
188 #if EPIC_RELEASE >= 27
189  isice = cfssctype;
190 #endif
191 
192  return isice;
193 
194 }
int cfssctype

◆ ks_dbg_reset()

void ks_dbg_reset ( )

Clear debug file content

119  {
120  FILE *fp;
121 
122 /* if on MR scanner (HOST and TGT/IPG, HW=hardware=MRscanner) */
123 #ifdef PSD_HW
124  fp = fopen("/usr/g/mrraw/ks_debug.txt", "w");
125 #else
126  fp = fopen("./ks_debug.txt", "w");
127 #endif
128 
129 fclose(fp);
130 
131 }

◆ ks_syslimits_ampmax()

float ks_syslimits_ampmax ( LOG_GRAD  loggrd)

Returns the maximum gradient amplitude that can be used on all gradient boards simultaneously

Since the waveform design (using ks_eval_***() functions) is separated from the placement of waveforms (using ks_pg_***() functions), the maximum gradient limit to use for gradient design must account for the least capable gradient board given the current slice angulation. ks_syslimits_ampmax() assumes that all gradients may be on simultaneously

The return value from this function can be passed in as ampmax to all ks_eval***_constrained() functions

Parameters
[in]loggrdGE's LOG_GRAD struct, dependent on the gradient system and current slice angulation
Return values
gradmaxMaximum gradient amplitude ([G/cm]) that can be used on all boards simultaneously
201  {
202  return (FMin(3, loggrd.tx_xyz, loggrd.ty_xyz, loggrd.tz_xyz));
203 }
LOG_GRAD loggrd

◆ ks_syslimits_ampmax2()

float ks_syslimits_ampmax2 ( LOG_GRAD  loggrd)

Returns the maximum gradient amplitude that can be used on two gradient boards simultaneously

Since the waveform design (using ks_eval_***() functions) is separated from the placement of waveforms (using ks_pg_***() functions), the maximum gradient limit to use for gradient design must account for the least capable gradient board given the current slice angulation. ks_syslimits_ampmax2() assumes that gradients are played out on at most one more board at the same time as the current trapezoid

The return value from this function can be passed in as ampmax to all ks_eval***_constrained() functions

Parameters
[in]loggrdGE's LOG_GRAD struct, dependent on the gradient system and current slice angulation
Return values
gradmaxMaximum gradient amplitude ([G/cm]) that can be used on 2 boards simultaneously
205  {
206  return (FMin(6, loggrd.tx_xy, loggrd.tx_xz, loggrd.ty_xy, loggrd.ty_yz, loggrd.tz_xz, loggrd.tz_yz));
207 }
LOG_GRAD loggrd

◆ ks_syslimits_ampmax1()

float ks_syslimits_ampmax1 ( LOG_GRAD  loggrd)

Returns the maximum gradient amplitude that can be used on one gradient board at a time

Since the waveform design (using ks_eval_***() functions) is separated from the placement of waveforms (using ks_pg_***() functions), the maximum gradient limit to use for gradient design must account for the least capable gradient board given the current slice angulation. ks_syslimits_ampmax1() assumes that no other gradient is played out on another board at the same time as the current trapezoid

The return value from this function can be passed in as ampmax to all ks_eval***_constrained() functions

Parameters
[in]loggrdGE's LOG_GRAD struct, dependent on the gradient system and current slice angulation
Return values
gradmaxMaximum gradient amplitude ([G/cm]) that can be used on one board at a time
209  {
210  return (FMin(3, loggrd.tx, loggrd.ty, loggrd.tz));
211 }
LOG_GRAD loggrd

◆ ks_syslimits_ampmax1p()

float ks_syslimits_ampmax1p ( LOG_GRAD  loggrd)

Returns the maximum gradient amplitude on the physical gradient axes

Parameters
[in]loggrdGE's LOG_GRAD struct, dependent on the gradient system and current slice angulation
Return values
gradmaxMaximum gradient amplitude ([G/cm]) as defined by the physical gradients (lowest of physical X, Y, Z)
214  {
215  return (FMin(3, loggrd.xfs, loggrd.yfs, loggrd.zfs));
216 }
LOG_GRAD loggrd

◆ ks_syslimits_ramptimemax()

int ks_syslimits_ramptimemax ( LOG_GRAD  loggrd)

Returns the minimum ramp time to get from zero to full gradient scale

Parameters
[in]loggrdGE's LOG_GRAD struct, dependent on the gradient system and current slice angulation
Return values
ramptimeRamptime in [us] to get from zero to full gradient scale
234  {
235  /* ramptime at full gradient scale */
236  return IMax(3, loggrd.xrt, loggrd.yrt, loggrd.zrt);
237 }
LOG_GRAD loggrd

◆ ks_syslimits_slewrate()

float ks_syslimits_slewrate ( LOG_GRAD  loggrd)

Returns the maximum slewrate that can be used on all gradient boards simultaneously

The return value from this function is the ratio of ks_syslimits_ampmax() and ks_syslimits_ramptimemax() and can be passed in as slewrate to all ks_eval***_constrained() functions

Parameters
[in]loggrdGE's LOG_GRAD struct, dependent on the gradient system and current slice angulation
Return values
slewrateMaximum slewrate ([(G/cm) / us]) that can be used on all boards simultaneously
248  {
250 }
LOG_GRAD loggrd
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
int ks_syslimits_ramptimemax(LOG_GRAD loggrd)
Returns the minimum ramp time to get from zero to full gradient scale
Definition: KSFoundation_common.c:234

◆ ks_syslimits_slewrate2()

float ks_syslimits_slewrate2 ( LOG_GRAD  loggrd)

Returns the maximum slewrate that can be used on two gradient boards simultaneously

The return value from this function is the ratio of ks_syslimits_ampmax2() and ks_syslimits_ramptimemax() and can be passed in as slewrate to all ks_eval***_constrained() functions

Parameters
[in]loggrdGE's LOG_GRAD struct, dependent on the gradient system and current slice angulation
Return values
slewrateMaximum slewrate ([(G/cm) / us]) that can be used on all boards simultaneously
252  {
254 }
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
LOG_GRAD loggrd
int ks_syslimits_ramptimemax(LOG_GRAD loggrd)
Returns the minimum ramp time to get from zero to full gradient scale
Definition: KSFoundation_common.c:234

◆ ks_syslimits_slewrate1()

float ks_syslimits_slewrate1 ( LOG_GRAD  loggrd)

Returns the maximum slewrate that can be used on one gradient board at a time

The return value from this function is the ratio of ks_syslimits_ampmax1() and ks_syslimits_ramptimemax() and can be passed in as slewrate to all ks_eval***_constrained() functions

Parameters
[in]loggrdGE's LOG_GRAD struct, dependent on the gradient system and current slice angulation
Return values
slewrateMaximum slewrate ([(G/cm) / us]) that can be used on one board at at time
256  {
258 }
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
LOG_GRAD loggrd
int ks_syslimits_ramptimemax(LOG_GRAD loggrd)
Returns the minimum ramp time to get from zero to full gradient scale
Definition: KSFoundation_common.c:234

◆ ks_syslimits_slewrate1p()

float ks_syslimits_slewrate1p ( LOG_GRAD  loggrd)

Returns the maximum gradient amplitude on the physical gradient axes

Parameters
[in]loggrdGE's LOG_GRAD struct, dependent on the gradient system and current slice angulation
Return values
gradmaxMaximum slewrate ([(G/cm) / us]) that can be used on one board at at time as defined by the physical gradients (lowest of physical X, Y, Z)
261  {
263 }
LOG_GRAD loggrd
float ks_syslimits_ampmax1p(LOG_GRAD loggrd)
Returns the maximum gradient amplitude on the physical gradient axes
Definition: KSFoundation_common.c:214
int ks_syslimits_ramptimemax(LOG_GRAD loggrd)
Returns the minimum ramp time to get from zero to full gradient scale
Definition: KSFoundation_common.c:234

◆ ks_syslimits_gradtarget()

float ks_syslimits_gradtarget ( LOG_GRAD  loggrd,
int  board 
)

Returns the maximum target amplitude for a board (internal use)

For gradient boards, the field .tx, .ty or .tz is returned. For non-gradient boards, 1.0 is returned. This function is used internally by ks_pg_***() functions and there should be no need to call it directly.

Parameters
[in]loggrdGE's LOG_GRAD struct, dependent on the gradient system and current slice angulation
[in]boardThe board on which the current trapezoid or waveform is to be played out on
Return values
gradampMaximum target amplitude for a board
220  {
221  switch (board) {
222  case XGRAD:
223  return loggrd.tx;
224  case YGRAD:
225  return loggrd.ty;
226  case ZGRAD:
227  return loggrd.tz;
228  default:
229  return 1.0;
230  }
231 }
LOG_GRAD loggrd

◆ ks_syslimits_ampmax_phys()

float ks_syslimits_ampmax_phys ( )

Returns the integer phase (not in use)

All phase values in KSFoundation are in [degrees], including flip angles, RF/receive phases and THETA waveforms. This function is therefore currently not used. See instead ks_phase_degrees2hw()

Parameters
[in]radphasePhase in radians
Return values
intphaseInteger phase to use on THETA board on hardware
197  {
198  return FMin(3, phygrd.xfs, phygrd.yfs, phygrd.zfs);
199 }
PHYS_GRAD phygrd

◆ ks_syslimits_ramptimemax_phys()

int ks_syslimits_ramptimemax_phys ( )
239  {
240  /* ramptime at full gradient scale */
241  return IMax(3, phygrd.xrt, phygrd.yrt, phygrd.zrt);
242 }
PHYS_GRAD phygrd

◆ ks_syslimits_slewrate_phys()

float ks_syslimits_slewrate_phys ( )
244  {
246 }
int ks_syslimits_ramptimemax_phys()
Definition: KSFoundation_common.c:239
float ks_syslimits_ampmax_phys()
Returns the integer phase (not in use)
Definition: KSFoundation_common.c:197

◆ ks_phase_radians2hw()

short ks_phase_radians2hw ( float  radphase)
266  {
267  short iphase;
268  UINT uiphase;
269  double dphase = (double) phase;
270  double twopi = (double) 2.0 * PI;
271 
272  /* Resolve angle to lie within range ( -2*pi <= dphase < 2*pi ) */
273  dphase = fmod(dphase, twopi);
274 
275  /* Find the equivalent positive angle */
276  if (dphase < 0.0)
277  dphase = twopi + dphase;
278 
279  /* Convert the angle to exciter DDS phase representation where only the
280  upper 14 bits of 16 are used */
281  uiphase = (UINT) (dphase * 16384.0 / twopi + 0.5);
282  if (uiphase == 16384)
283  uiphase = 0;
284 
285  /* left shift the result by 2 bits */
286  iphase = (short) (uiphase << 2);
287 
288  return iphase;
289 }

◆ ks_phase_degrees2hw()

short ks_phase_degrees2hw ( float  degphase)

Returns the integer phase (internal use)

All phase values in KSFoundation are in [degrees], including flip angles, RF/receive phases and THETA waveforms. This function is used internally by another internal function ks_wave2iwave(), which is in turn called by ks_pg_wave()

Parameters
[in]degphasePhase in degrees
Return values
intphaseInteger phase to use on THETA board on hardware
291  {
292 
293  return ks_phase_radians2hw(phase * PI / 180.0);
294 }
short ks_phase_radians2hw(float phase)
Definition: KSFoundation_common.c:266

◆ ks_calc_selgradamp()

float ks_calc_selgradamp ( float  rfbw,
float  slthick 
)

Returns the gradient amplitude in [G/cm] necessary for slice selection (internal use)

Given an RF bandwidth in [Hz] and a slice thickness in [mm], this function calculates the gradient strength in [G/cm] necessary to produce a slice with the given slice thickness

This function is used by ks_eval_seltrap(), ks_eval_selrf_constrained() and ks_scan_selrf_setfreqphase()

Parameters
[in]rfbwThe bandwidth of the RF pulse to use with the gradient
[in]slthickThe desired slice thickness in [mm]
Return values
gradampGradient amplitude to use [G/cm]
309  {
310 
311  if (slthick > 0 && rfbw > 0)
312  return (rfbw / (GAM * slthick / 10.0));
313  else
314  return 0.0; /* return zero amp if requested slice thickness is <= 0 */
315 }
float GAM

◆ ks_wave_res()

int ks_wave_res ( const KS_WAVE wave)

Returns the number of samples in a KS_WAVE sequence object

Parameters
[in]waveKS_WAVE object
Return values
resResolution (i.e. number of samples) in the KS_WAVE object
1107  {
1108 
1109  if (wave == NULL) {
1110  ks_error("ks_wave_res: wave is NULL");
1111  return KS_NOTSET;
1112  }
1113  if (wave->description == NULL || wave->description[0] == ' ') {
1114  ks_error("ks_wave_res: wave description not initialized or has leading space");
1115  return KS_NOTSET;
1116  }
1117  if (wave->res < 2 || wave->res > KS_MAXWAVELEN) {
1118  ks_error("ks_wave_res(%s): 'res' (%d) is out of valid range [2, %d]", wave->description, wave->res, KS_MAXWAVELEN);
1119  return KS_NOTSET;
1120  }
1121  if (wave->res % 2) {
1122  ks_error("ks_wave_res(%s): 'res' (%d) must be even", wave->description, wave->res);
1123  return KS_NOTSET;
1124  }
1125 
1126  return wave->res;
1127 }
#define KS_NOTSET
Definition: KSFoundation.h:103
STATUS ks_error(const char *format,...)
Definition: KSFoundation_common.c:70
KS_DESCRIPTION description
Definition: KSFoundation.h:666
int res
Definition: KSFoundation.h:667
#define KS_MAXWAVELEN
Definition: KSFoundation.h:186

◆ ks_waveform_max()

float ks_waveform_max ( const KS_WAVEFORM  waveform,
int  res 
)

Returns the maximum value in a KS_WAVEFORM

Parameters
[in]waveformKS_WAVEFORM (float array)
[in]resResolution (i.e. number of samples) in the KS_WAVEFORM
Return values
maxvalMaximum value
1130  {
1131  float maxv = -1.0e38;
1132  int i;
1133 
1134  if (waveform == NULL) {
1135  ks_error("ks_waveform_max: wave is NULL");
1136  return maxv;
1137  }
1138  if (res <= 0) {
1139  return maxv;
1140  }
1141 
1142  for (i = 0; i < res; i++) {
1143  if (waveform[i] > maxv)
1144  maxv = waveform[i];
1145  }
1146 
1147  return maxv;
1148 }
STATUS ks_error(const char *format,...)
Definition: KSFoundation_common.c:70

◆ ks_wave_max()

float ks_wave_max ( const KS_WAVE wave)

Returns the maximum value in a KS_WAVE sequence object

Parameters
[in]waveKS_WAVE object
Return values
maxvalMaximum value
1151  {
1152  return ks_waveform_max(wave->waveform, wave->res);
1153 }
float ks_waveform_max(const KS_WAVEFORM waveform, int res)
Returns the maximum value in a KS_WAVEFORM
Definition: KSFoundation_common.c:1130
int res
Definition: KSFoundation.h:667
KS_WAVEFORM waveform
Definition: KSFoundation.h:669

◆ ks_waveform_min()

float ks_waveform_min ( const KS_WAVEFORM  waveform,
int  res 
)

Returns the minimum value in a KS_WAVEFORM

Parameters
[in]waveformKS_WAVEFORM (float array)
[in]resResolution (i.e. number of samples) in the KS_WAVEFORM
Return values
minvalMinimum value
1156  {
1157  float minv = 1.0e38;
1158  int i;
1159 
1160  if (waveform == NULL) {
1161  ks_error("ks_waveform_min: wave is NULL");
1162  return minv;
1163  }
1164  if (res <= 0) {
1165  return minv;
1166  }
1167 
1168  for (i = 0; i < res; i++) {
1169  if (waveform[i] < minv)
1170  minv = waveform[i];
1171  }
1172 
1173  return minv;
1174 }
STATUS ks_error(const char *format,...)
Definition: KSFoundation_common.c:70

◆ ks_wave_min()

float ks_wave_min ( const KS_WAVE wave)

Returns the minimum value in a KS_WAVE sequence object

Parameters
[in]waveKS_WAVE object
Return values
minvalMinimum value
1177  {
1178  return ks_waveform_min(wave->waveform, wave->res);
1179 }
int res
Definition: KSFoundation.h:667
float ks_waveform_min(const KS_WAVEFORM waveform, int res)
Returns the minimum value in a KS_WAVEFORM
Definition: KSFoundation_common.c:1156
KS_WAVEFORM waveform
Definition: KSFoundation.h:669

◆ ks_waveform_absmax()

float ks_waveform_absmax ( const KS_WAVEFORM  waveform,
int  res 
)

Returns the maximum absolute value in a KS_WAVEFORM

Parameters
[in]waveformKS_WAVEFORM (float array)
[in]resResolution (i.e. number of samples) in the KS_WAVEFORM
Return values
absmaxvalMaximum absolute value
1203  {
1204  float maxv = -1.0e38;
1205  int i;
1206 
1207  if (waveform == NULL) {
1208  ks_error("ks_waveform_absmax: wave is NULL");
1209  return maxv;
1210  }
1211  if (res <= 0) {
1212  return maxv;
1213  }
1214 
1215  for (i = 0; i < res; i++) {
1216  if (fabs(waveform[i]) > maxv)
1217  maxv = fabs(waveform[i]);
1218  }
1219 
1220  return maxv;
1221 }
STATUS ks_error(const char *format,...)
Definition: KSFoundation_common.c:70

◆ ks_waveform_maxslew()

float ks_waveform_maxslew ( const KS_WAVEFORM  waveform,
int  res,
int  duration 
)

Returns the maximum absolute value in a KS_WAVEFORM

Parameters
[in]waveformKS_WAVEFORM
[in]resResolution (i.e. number of samples) in the KS_WAVEFORM
[in]durationDuration of the waveform in us
Return values
absmaxvalMaximum absolute value
1228  {
1229  float maxslew = -1.0e38;
1230  int i;
1231 
1232  if (waveform == NULL) {
1233  ks_error("ks_waveform_absmax: wave is NULL");
1234  return maxslew;
1235  }
1236  if (res <= 0) {
1237  return maxslew;
1238  }
1239  float tmp = 0;
1240  for (i = 0; i < res-1; i++) {
1241  tmp = fabs(waveform[i] - waveform[i+1]);
1242  if (tmp > maxslew)
1243  maxslew = tmp;
1244  }
1245  maxslew = maxslew*res/duration;
1246  return maxslew;
1247 }
STATUS ks_error(const char *format,...)
Definition: KSFoundation_common.c:70

◆ ks_wave_maxslew()

float ks_wave_maxslew ( const KS_WAVE wave)

Returns the maximum absolute value in a KS_WAVE

Parameters
[in]waveKS_WAVE
Return values
absmaxvalMaximum absolute value
1250  {
1251  return ks_waveform_maxslew(wave->waveform, wave->res, wave->duration);
1252 }
float ks_waveform_maxslew(const KS_WAVEFORM waveform, int res, int duration)
Returns the maximum absolute value in a KS_WAVEFORM
Definition: KSFoundation_common.c:1228
int res
Definition: KSFoundation.h:667
KS_WAVEFORM waveform
Definition: KSFoundation.h:669
int duration
Definition: KSFoundation.h:668

◆ ks_iwave_absmax()

short ks_iwave_absmax ( const KS_IWAVE  waveform,
int  res 
)

Returns the max amp of a KS_IWAVE between 0 and res

Parameters
[in]waveformKS_WAVE object
[in]resCheck up to this many samples
Return values
maximumamplitude of the waveform
1182  {
1183  short maxv = 0;
1184  int i;
1185 
1186  if (waveform == NULL) {
1187  ks_error("%s: wave is NULL", __FUNCTION__);
1188  return maxv;
1189  }
1190  if (res <= 0) {
1191  return maxv;
1192  }
1193 
1194  for (i = 0; i < res; i++) {
1195  if (abs(waveform[i]) > maxv)
1196  maxv = abs(waveform[i]);
1197  }
1198 
1199  return maxv;
1200 }
STATUS ks_error(const char *format,...)
Definition: KSFoundation_common.c:70

◆ ks_wave_absmax()

float ks_wave_absmax ( const KS_WAVE wave)

Returns the maximum absolute value in a KS_WAVE sequence object

Parameters
[in]waveKS_WAVE object
Return values
absmaxvalMaximum absolute value
1224  {
1225  return ks_waveform_absmax(wave->waveform, wave->res);
1226 }
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

◆ ks_waveform_area()

float ks_waveform_area ( const KS_WAVEFORM  waveform,
int  start,
int  end,
int  dwelltime 
)

Returns the area of a KS_WAVEFORM over a specified interval given in [us]

Parameters
[in]waveformKS_WAVEFORM (float array)
[in]start[us] Start position of area calculation in [us] (0 = start of waveform)
[in]end[us] End position of area calculation in [us] (res * dwelltime = end of waveform)
[in]dwelltime[us] of each waveform point (duration/res)
Return values
areaThe area of the KS_WAVEFORM over the specified interval in [(G/cm) * us]
1254  {
1255  int i;
1256  double area = 0;
1257  int start_indx = CEIL_DIV(start, dwelltime);
1258  int end_indx = end / dwelltime;
1259 
1260  if (waveform == NULL) {
1261  ks_error("%s: wave is NULL", __FUNCTION__);
1262  }
1263  if (start_indx < 0) {
1264  ks_error("%s: start must be >= 0", __FUNCTION__);
1265  }
1266  if (end_indx > KS_MAXWAVELEN) {
1267  ks_error("%s: end cannot exceed %d", __FUNCTION__, KS_MAXWAVELEN);
1268  }
1269 
1270  for (i = start_indx; i < end_indx; i++) {
1271  area += waveform[i] * dwelltime;
1272  }
1273 
1274  /* add potential partial dwell time due to CEIL_DIV, when start [us] is not divisible by dwelltime */
1275  if ((start_indx * dwelltime) > start && start_indx > 0) {
1276  area += waveform[start_indx - 1] * (((double) start_indx * dwelltime) - (double) start);
1277  }
1278 
1279  /* add potential partial dwell time due to (floor) integer division, when end [us] is not divisible by dwelltime */
1280  if ((end_indx * dwelltime) < end && (end_indx < (KS_MAXWAVELEN - 1))) {
1281  area += waveform[end_indx + 1] * ((double) end - ((double) end_indx * dwelltime));
1282  }
1283 
1284  return ((float) area);
1285 }
STATUS ks_error(const char *format,...)
Definition: KSFoundation_common.c:70
#define KS_MAXWAVELEN
Definition: KSFoundation.h:186

◆ ks_wave_area()

float ks_wave_area ( const KS_WAVE wave,
int  start,
int  end 
)

Returns the area of a KS_WAVE object over a specified interval given in [us]

Parameters
[in]waveKS_WAVE object
[in]start[us] Start position of area calculation in [us] (0 = start of waveform)
[in]end[us] End position of area calculation in us
Return values
areaThe area of the KS_WAVE object over the specified interval in [(G/cm) * us]
1287  {
1288  int dwelltime = wave->duration / wave->res;
1289  return ks_waveform_area(wave->waveform, start, end, dwelltime);
1290 }
float ks_waveform_area(const KS_WAVEFORM waveform, int start, int end, int dwelltime)
Returns the area of a KS_WAVEFORM over a specified interval given in [us]
Definition: KSFoundation_common.c:1254
int res
Definition: KSFoundation.h:667
KS_WAVEFORM waveform
Definition: KSFoundation.h:669
int duration
Definition: KSFoundation.h:668

◆ ks_waveform_sum()

float ks_waveform_sum ( const KS_WAVEFORM  waveform,
int  res 
)

Returns the sum of a KS_WAVEFORM

Parameters
[in]waveformKS_WAVEFORM (float array)
[in]resResolution (i.e. number of samples) in the KS_WAVEFORM
Return values
sumThe sum of the KS_WAVEFORM
1293  {
1294  int i;
1295  double sum = 0;
1296 
1297  if (waveform == NULL) {
1298  ks_error("ks_waveform_sum: wave is NULL");
1299  }
1300  if (res <= 0) {
1301  ks_error("ks_waveform_sum: res must be positive");
1302  }
1303 
1304  for (i = 0; i < res; i++) {
1305  sum += waveform[i];
1306  }
1307 
1308  return (float)sum;
1309 }
STATUS ks_error(const char *format,...)
Definition: KSFoundation_common.c:70

◆ ks_wave_sum()

float ks_wave_sum ( const KS_WAVE wave)

Returns the sum of a KS_WAVE sequence object

Parameters
[in]waveKS_WAVE object
Return values
sumThe sum of the KS_WAVEFORM
1311  {
1312  return ks_waveform_sum(wave->waveform, wave->res);
1313 }
float ks_waveform_sum(const KS_WAVEFORM waveform, int res)
Returns the sum of a KS_WAVEFORM
Definition: KSFoundation_common.c:1293
int res
Definition: KSFoundation.h:667
KS_WAVEFORM waveform
Definition: KSFoundation.h:669

◆ ks_waveform_norm()

float ks_waveform_norm ( const KS_WAVEFORM  waveform,
int  res 
)

Returns the 2-norm of a KS_WAVEFORM

Parameters
[in]waveformKS_WAVEFORM (float array)
[in]resResolution (i.e. number of samples) in the KS_WAVEFORM
Return values
norm2The 2-norm of a KS_WAVEFORM
1316  {
1317  int i;
1318  double sum = 0;
1319 
1320  if (waveform == NULL) {
1321  ks_error("ks_waveform_norm: wave is NULL");
1322  }
1323  if (res <= 0) {
1324  ks_error("ks_waveform_norm: res must be positive");
1325  }
1326 
1327  for (i = 0; i < res; i++) {
1328  sum += pow(fabs(waveform[i]), 2.0);
1329  }
1330 
1331  return (float)sqrt(sum);
1332 }
STATUS ks_error(const char *format,...)
Definition: KSFoundation_common.c:70

◆ ks_wave_norm()

float ks_wave_norm ( const KS_WAVE wave)

Returns the 2-norm of a KS_WAVE

Parameters
[in]waveKS_WAVE object
Return values
norm2The 2-norm of a KS_WAVE
1334  {
1335  return ks_waveform_norm(wave->waveform, wave->res);
1336 }
float ks_waveform_norm(const KS_WAVEFORM waveform, int res)
Returns the 2-norm of a KS_WAVEFORM
Definition: KSFoundation_common.c:1316
int res
Definition: KSFoundation.h:667
KS_WAVEFORM waveform
Definition: KSFoundation.h:669

◆ ks_waveform_cumsum()

void ks_waveform_cumsum ( KS_WAVEFORM  cumsumwaveform,
const KS_WAVEFORM  waveform,
int  res 
)

Calculates a KS_WAVEFORM with the cumulative sum (i.e. integral) of a KS_WAVEFORM

Parameters
[out]cumsumwaveformKS_WAVEFORM (float array)
[in]waveformKS_WAVEFORM (float array)
[in]resResolution (i.e. number of samples) in the KS_WAVEFORM
1339  {
1340  int i, j;
1341 
1342  for (j = 0; j < res; j++) {
1343 
1344  double sum = 0;
1345  for (i = 0; i < j; i++) {
1346  sum += waveform[i];
1347  }
1348 
1349  cumsumwaveform[j] = (float)sum;
1350  }
1351 }

◆ ks_wave_cumsum()

void ks_wave_cumsum ( KS_WAVE cumsumwave,
const KS_WAVE wave 
)

Calculates a KS_WAVE with the cumulative sum (i.e. integral) of a KS_WAVE

Parameters
[out]cumsumwaveKS_WAVE object
[in]waveKS_WAVE
1353  {
1354  int res;
1355  res = (cumsumwave->res > wave->res) ? wave->res : cumsumwave->res; /* smallest of the two */
1356  ks_waveform_cumsum(cumsumwave->waveform, wave->waveform, res);
1357 }
void ks_waveform_cumsum(KS_WAVEFORM cumsumwaveform, const KS_WAVEFORM waveform, int res)
Calculates a KS_WAVEFORM with the cumulative sum (i.e. integral) of a KS_WAVEFORM
Definition: KSFoundation_common.c:1339
int res
Definition: KSFoundation.h:667
KS_WAVEFORM waveform
Definition: KSFoundation.h:669

◆ ks_waveform_multiply()

void ks_waveform_multiply ( KS_WAVEFORM  waveform_mod,
const KS_WAVEFORM  waveform,
int  res 
)

In-place multiplication of one KS_WAVEFORM with another KS_WAVEFORM

Multiplication of waveform a (arg 1) with waveform b (arg 2) as: a *= b

Parameters
[in,out]waveform_modKS_WAVE object
[in]waveformKS_WAVEFORM (float array)
[in]resResolution (i.e. number of samples) in the KS_WAVEFORM
1360  {
1361  int i;
1362 
1363  for (i = 0; i < res; i++) {
1364  waveform_mod[i] *= waveform[i];
1365  }
1366 }

◆ ks_wave_multiply()

void ks_wave_multiply ( KS_WAVE wave_mod,
const KS_WAVE wave 
)

In-place multiplication of one KS_WAVE with another KS_WAVE

Multiplication of waveform a (arg 1) with waveform b (arg 2) as: a *= b

If the duration of the two KS_WAVE objects have different resolution, the shorter KS_WAVE will be multiplied with the first part of the longer KS_WAVE

Parameters
[in,out]wave_modKS_WAVE object
[in]waveKS_WAVE object
1368  {
1369  int res;
1370  res = (wave_mod->res > wave->res) ? wave->res : wave_mod->res; /* smallest of the two */
1371  ks_waveform_multiply(wave_mod->waveform, wave->waveform, res);
1372 }
void ks_waveform_multiply(KS_WAVEFORM waveform_mod, const KS_WAVEFORM waveform, int res)
In-place multiplication of one KS_WAVEFORM with another KS_WAVEFORM
Definition: KSFoundation_common.c:1360
int res
Definition: KSFoundation.h:667
KS_WAVEFORM waveform
Definition: KSFoundation.h:669

◆ ks_waveform_add()

void ks_waveform_add ( KS_WAVEFORM  waveform_mod,
const KS_WAVEFORM  waveform,
int  res 
)

In-place addition of one KS_WAVEFORM with another KS_WAVEFORM

Addition of waveform a (arg 1) with waveform b (arg 2) as: a += b

Parameters
[in,out]waveform_modKS_WAVEFORM (float array)
[in]waveformKS_WAVEFORM (float array)
[in]resResolution (i.e. number of samples) in the KS_WAVEFORM
1375  {
1376  int i;
1377 
1378  for (i = 0; i < res; i++) {
1379  waveform_mod[i] += waveform[i];
1380  }
1381 }

◆ ks_wave_add()

void ks_wave_add ( KS_WAVE wave_mod,
const KS_WAVE wave 
)

In-place addition of one KS_WAVE with another KS_WAVE

Addition of waveform a (arg 1) with waveform b (arg 2) as: a *= b

If the duration of the two KS_WAVE objects have different resolution, the shorter KS_WAVE will be added to the first part of the longer KS_WAVE

Parameters
[in,out]wave_modKS_WAVE object
[in]waveKS_WAVE object
1383  {
1384  int res;
1385  res = (wave_mod->res > wave->res) ? wave->res : wave_mod->res; /* smallest of the two */
1386  ks_waveform_add(wave_mod->waveform, wave->waveform, res);
1387 }
void ks_waveform_add(KS_WAVEFORM waveform_mod, const KS_WAVEFORM waveform, int res)
In-place addition of one KS_WAVEFORM with another KS_WAVEFORM
Definition: KSFoundation_common.c:1375
int res
Definition: KSFoundation.h:667
KS_WAVEFORM waveform
Definition: KSFoundation.h:669

◆ ks_waveform_multiplyval()

void ks_waveform_multiplyval ( KS_WAVEFORM  waveform,
float  val,
int  res 
)

In-place scalar multiplication of a KS_WAVEFORM

The values in a KS_WAVEFORM are multiplied with a scalar value val

Parameters
[in,out]waveformKS_WAVEFORM (float array)
[in]valFloating point value to multiply with
[in]resResolution (i.e. number of samples) in the KS_WAVEFORM
1390  {
1391  int i;
1392 
1393  for (i = 0; i < res; i++) {
1394  waveform[i] *= val;
1395  }
1396 }

◆ ks_wave_multiplyval()

void ks_wave_multiplyval ( KS_WAVE wave,
float  val 
)

In-place scalar multiplication of a KS_WAVE

The waveform values in a KS_WAVE sequence object (.waveform[]) are multiplied with a scalar value val

Parameters
[in,out]waveKS_WAVE object
[in]valFloating point value to multiply with
1398  {
1399  ks_waveform_multiplyval(wave->waveform, val, wave->res);
1400 }
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 res
Definition: KSFoundation.h:667
KS_WAVEFORM waveform
Definition: KSFoundation.h:669

◆ ks_waveform_addval()

void ks_waveform_addval ( KS_WAVEFORM  waveform,
float  val,
int  res 
)

In-place scalar addition of a KS_WAVEFORM

The values in a KS_WAVEFORM are added with a scalar value val

Parameters
[in,out]waveformKS_WAVEFORM (float array)
[in]valFloating point value to add
[in]resResolution (i.e. number of samples) in the KS_WAVEFORM
1403  {
1404  int i;
1405 
1406  for (i = 0; i < res; i++) {
1407  waveform[i] += val;
1408  }
1409 }

◆ ks_wave_addval()

void ks_wave_addval ( KS_WAVE wave,
float  val 
)

In-place scalar addition of a KS_WAVE

The waveform values in a KS_WAVE sequence object (.waveform[]) are added with a scalar value val

Parameters
[in,out]waveKS_WAVE object
[in]valFloating point value to add
1411  {
1412  ks_waveform_addval(wave->waveform, val, wave->res);
1413 }
void ks_waveform_addval(KS_WAVEFORM waveform, float val, int res)
In-place scalar addition of a KS_WAVEFORM
Definition: KSFoundation_common.c:1403
int res
Definition: KSFoundation.h:667
KS_WAVEFORM waveform
Definition: KSFoundation.h:669

◆ ks_waveform2iwave()

STATUS ks_waveform2iwave ( KS_IWAVE  iwave,
const KS_WAVEFORM  waveform,
int  res,
int  board 
)

(Internal use) Conversion of a KS_WAVEFORM to a short int array for use on hardware

This function is used internally by ks_pg_wave() and there should be no need to call this function directly.

For all boards except THETA, this function auto-scales the (float) values in KS_WAVEFORM so that the maximum absolute value becomes +/- 32766. The output short int (KS_IWAVE) array is to be copied to hardware. For a KS_WAVEFORM on the THETA board, the necessary phase wraps and scaling to short int format is performed using ks_phase_degrees2hw(). GE's requirement of only having even numbers in the waveform except for a final odd value (end-of-waveform) is also taken care of here

Note that the preservation of the physical units are done in ks_pg_wave() by setting the correct instruction amplitude to multiply this auto-scaled waveform with in the sequence's hardware memory

Parameters
[out]iwaveKS_IWAVE (short int array)
[in]waveformKS_WAVEFORM
[in]resResolution (i.e. number of samples) in the KS_WAVEFORM
[in]boardOne of XGRAD, YGRAD, ZGRAD, RHO, THETA, OMEGA
Return values
STATUSSUCCESS or FAILURE
1418  {
1419  int i;
1420 
1421  if (board != THETA) {
1422  float absmax = ks_waveform_absmax(waveform, res);
1423  for (i = 0; i < res; i++) {
1424  iwave[i] = ((short) (max_pg_wamp * waveform[i] / absmax)) & ~WEOS_BIT;
1425  }
1426  } else {
1427  for (i = 0; i < res; i++) {
1428  iwave[i] = ((short) ks_phase_degrees2hw(waveform[i])) & ~WEOS_BIT;
1429  }
1430  }
1431 
1432  /* set end-of-waveform flag */
1433  iwave[res - 1] |= WEOS_BIT;
1434 
1435  return SUCCESS;
1436 
1437 } /* ks_wave2iwave() */
float ks_waveform_absmax(const KS_WAVEFORM waveform, int res)
Returns the maximum absolute value in a KS_WAVEFORM
Definition: KSFoundation_common.c:1203
short ks_phase_degrees2hw(float phase)
Returns the integer phase (internal use)
Definition: KSFoundation_common.c:291

◆ ks_wave2iwave()

STATUS ks_wave2iwave ( KS_IWAVE  iwave,
const KS_WAVE wave,
int  board 
)

(Internal use) Conversion of waveform content in a KS_WAVE sequence object to a short int array for use on hardware

This function is used internally by ks_pg_wave() and there should be no need to call this function directly.

For all boards except THETA, this function auto-scales the (float) values in the .waveform[] field so that the maximum absolute value becomes +/- 32766. The output short int (KS_IWAVE) array is to be copied to hardware. For a KS_WAVE on the THETA board, the necessary phase wraps and scaling to short int format is performed using ks_phase_degrees2hw(). GE's requirement of only having even numbers in the waveform except for a final odd value (end-of-waveform) is also taken care of here

Note that the preservation of the physical units are done in ks_pg_wave() by setting the correct instruction amplitude to multiply this auto-scaled waveform with in the sequence's hardware memory

Parameters
[out]iwaveKS_IWAVE (short int array)
[in]waveKS_WAVE
[in]boardOne of XGRAD, YGRAD, ZGRAD, RHO, THETA, OMEGA
Return values
STATUSSUCCESS or FAILURE
1440  {
1441  return ks_waveform2iwave(iwave, wave->waveform, wave->res, board);
1442 }
STATUS ks_waveform2iwave(KS_IWAVE iwave, const KS_WAVEFORM waveform, int res, int board)
(Internal use) Conversion of a KS_WAVEFORM to a short int array for use on hardware
Definition: KSFoundation_common.c:1418
int res
Definition: KSFoundation.h:667
KS_WAVEFORM waveform
Definition: KSFoundation.h:669

◆ ks_pg_echossp()

WF_PULSE* ks_pg_echossp ( WF_PULSE *  echo,
const char *  suffix 
)

Returns the pointer to XTR or RBA for an echo WF_PULSE (internal use)

This function is used internally by ks_pg_read() to get pointers to the XTR and RBA SSP packets for an acquisition window

Parameters
[in]echoPointer to an echo (WF_PULSE)
[in]suffixString being "xtr", "rba", or empty
Return values
wfptrPointer to a WF_PULSE
1458  {
1459 
1460  if (!strcmp(suffix, "xtr")) {
1461  return echo->assoc_pulse;
1462  } else if (!strcmp(suffix, "rba")) {
1463  return echo->assoc_pulse->assoc_pulse;
1464  } else {
1465  return echo;
1466  }
1467 
1468 }

◆ ks_pg_trap()

STATUS ks_pg_trap ( KS_TRAP trap,
KS_SEQLOC  loc,
KS_SEQ_CONTROL ctrl 
)

Places a KS_TRAP sequence object on a board at some position in the pulse sequence

This function should be called in the @pg section to place out a KS_TRAP sequence object that creates a static trapezoid in the pulse sequence. The same KS_TRAP object can be freely placed on XGRAD, YGRAD and ZGRAD as many times as desired, by only changing the second argument each time (to avoid waveform overlaps). The second argument is a KS_SEQLOC struct with three fields:

  • .board: Choose between XGRAD, YGRAD, ZGRAD (or OMEGA)
  • .pos: Absolute time in [us] of the start of the attack ramp of the trapezoid
  • .ampscale: A factor that must be in range [-1.0,1.0] that is multiplied with .amp to yield a per-instance (i.e. per-placement) amplitude ([G/cm]). The simplest is to use 1.0, which makes the KS_TRAP object to be placed out on the board as initially designed using ks_eval_trap(). For the OMEGA board, this factor must be 1.0.

Regardless of the order placed out in the @pg section, and on which boards (XGRAD, YGRAD, ZGRAD) the KS_TRAP object is placed, the instance number of the KS_TRAP is automatically sorted in time. If two instances of one KS_TRAP occur at the same time (.pos) on different boards, XGRAD comes before YGRAD, which comes before ZGRAD. These abstract, sorted, instance numbers are used by ks_scan_***() functions in scan() to refer to a specific instance of a KS_TRAP object in the sequence

If the .duration field in the KS_TRAP object is 0, ks_pg_trap() will return SUCCESS and quietly ignore placing it out. This is a part of the mechanism that setting the .duration field to 0 in cveval() should eliminate its existance in both timing calulations and in the pulse sequence generation in @pg

When a trapezoid is placed on OMEGA, neither the designed amplitude (the .amp field in KS_TRAP) nor the .ampscale (in KS_SEQLOC) has an effect on the final frequency modulation. Instead the run-time amplitude of a KS_TRAP object on the OMEGA board is controlled in scan() via a function called ks_scan_omegatrap_hz()

GENERAL NOTE: It is a requirement that the function in the @pg section containing all ks_pg_***() calls is also called exactly once in cveval() after the calls to the corresponding ks_eval_***() functions that design the KS_*** sequence objects. Each ks_pg_***() will throw an error if this has not been done.

Parameters
[in,out]trapPointer to a KS_TRAP sequence object
[in]locKS_SEQLOC struct to specify when and where to place the KS_TRAP
[in,out]ctrlPointer to the KS_SEQ_CONTROL struct corresponding to the sequence module for this KS_TRAP
Return values
STATUSSUCCESS or FAILURE
1475  {
1476  float boardmax;
1477  char boardc[8] = "xyz---o";
1478 
1479  boardmax = ks_syslimits_gradtarget(loggrd, loc.board); /* returns 1.0 for OMEGA board */
1480 
1481  /* checking for NULL pointers */
1482  if (trap == NULL) {
1483  return ks_error("ks_pg_trap: First argument is NULL");
1484  }
1485  /* duration of 0 should not throw an error as this is a sign of that it has not been set up
1486  or has manually been disabled this way */
1487  if (trap->duration == 0) {
1488  return SUCCESS;
1489  }
1490 
1491  if (trap->description == NULL || trap->description[0] == 0 || trap->description[0] == ' ') {
1492  return ks_error("ks_pg_trap: Invalid description ('%s')", trap->description);
1493  }
1494 
1495 #ifdef IPG
1496  /* TGT: Don't place anything on hardware if ctrl->duration = 0.
1497  This is in concert with:
1498  - not performing createseq() in KS_SEQLENGTH if seqctrl.duration = 0
1499  - not playing sequence modules in scan() using ks_scan_playsequence() if seqctrl.duration = 0
1500  */
1501  if (ctrl->duration == 0) {
1502  return ks_error("%s(%s): Refusing to place trap since seqctrl.duration = 0 (indicating `%s` not to be played in scan())", __FUNCTION__, trap->description, ctrl->description);
1503  }
1504 #endif
1505 
1506  /* input validation */
1507  if ((trap->ramptime % 4) || (trap->plateautime % 4) || (trap->duration % 4)) {
1508  ks_error("ks_pg_trap(%s): All timing parameters must be divisible by 4", trap->description);
1509  return FAILURE;
1510  }
1511 
1512  if ((trap->ramptime < 4) || (trap->plateautime < 4) || (trap->duration < 4)) {
1513  ks_error("ks_pg_trap(%s): All timing parameters must be at least 4 us", trap->description);
1514  return FAILURE;
1515  }
1516 
1517  if (isNaN(loc.ampscale)) {
1518  return ks_error("ks_pg_trap(%s): loc.ampscale NaN", trap->description);
1519  }
1520 
1521  if (loc.pos < GRAD_UPDATE_TIME || loc.pos > 4e6) {
1522  ks_error("ks_pg_trap(%s): Invalid position %d, it must be between 4us and 4s", trap->description, loc.pos);
1523  return FAILURE;
1524  }
1525 
1526  if (loc.board == OMEGA && !areSame(loc.ampscale,1.0)) {
1527  return ks_error("ks_pg_trap(%s): ampscale must be +1.0 for OMEGA", trap->description);
1528  }
1529 
1530  if (loc.ampscale < -1.0 || loc.ampscale > 1.0) {
1531  ks_error("ks_pg_trap(%s): Invalid ampscale %g, it must be between -1.0 and 1.0", trap->description, loc.ampscale);
1532  return FAILURE;
1533  }
1534 
1535  if (loc.board != XGRAD && loc.board != YGRAD && loc.board != ZGRAD && loc.board != OMEGA) {
1536  ks_error("ks_pg_trap(%s): Invalid board code %d, it must be %d (XGRAD), %d (YGRAD), %d (ZGRAD) or %d (OMEGA)", trap->description, loc.board, XGRAD, YGRAD, ZGRAD, OMEGA);
1537  return FAILURE;
1538  }
1539 
1540  if (fabs(trap->amp * loc.ampscale) > boardmax) {
1541  ks_error("ks_pg_trap(%s): trapezoid amplitude %g exceeds maximum (%g) for board %c", trap->description, trap->amp * loc.ampscale, boardmax, boardc[loc.board]);
1542  return FAILURE;
1543  }
1544 
1545  loc.pos = RUP_GRD(loc.pos);
1546 
1547 #ifdef IPG
1548  {
1549 
1550  int i;
1551 
1552  char tmpstr[KS_DESCRIPTION_LENGTH + 7];
1553  tmpstr[KS_DESCRIPTION_LENGTH + 7 - 1] = 0;
1554 
1555  if (trap->base.ngenerated >= trap->base.ninst) {
1556  ks_error("ks_pg_trap @tgt(%s): the number of instances generated on the target exceeds the number of ones generated on the host", trap->description);
1557  return FAILURE;
1558  }
1559 
1560  if (!ctrl->gradrf.is_cleared_on_tgt) /* Reset gradrfctrl on tgt to remove dead pointers copied from host. */
1561  {
1563  }
1564 
1565  /* TODO: consider freeing memory if ngenerated is zero -- EA */
1566 
1567  if (trap->wfpulse == NULL) {
1568  WF_PULSE wfInit = INITPULSE;
1569 
1570  trap->wfpulse = (WF_PULSE **) AllocNode(8 * sizeof(WF_PULSE *)); /* X, Y, Z, (OMEGA = 6). Not applicable to other boards */
1571 
1572  for (i = XGRAD; i <= OMEGA; i++) {
1573 
1574  if (i == XGRAD || i == YGRAD || i == ZGRAD || i == OMEGA) {
1575 
1576  trap->wfpulse[i] = (WF_PULSE *) AllocNode(3 * sizeof(WF_PULSE)); /* attack, plateau, decay */
1577 
1578  /* init pulse structure */
1579  trap->wfpulse[i][G_ATTACK] = wfInit;
1580  trap->wfpulse[i][G_PLATEAU] = wfInit;
1581  trap->wfpulse[i][G_DECAY] = wfInit;
1582 
1583  /* Name for WF_PULSEs */
1584  sprintf(tmpstr, "%s.%ca", trap->description, boardc[i]); pulsename(&(trap->wfpulse[i][G_ATTACK]), tmpstr);
1585  sprintf(tmpstr, "%s.%c", trap->description, boardc[i]); pulsename(&(trap->wfpulse[i][G_PLATEAU]), tmpstr);
1586  sprintf(tmpstr, "%s.%cd", trap->description, boardc[i]); pulsename(&(trap->wfpulse[i][G_DECAY]), tmpstr);
1587 
1588  createramp(&(trap->wfpulse[i][G_ATTACK]), (WF_PROCESSOR) i, trap->ramptime, 0, max_pg_wamp,
1589  maxGradRes * (trap->ramptime / GRAD_UPDATE_TIME), 1.0);
1590  createconst(&(trap->wfpulse[i][G_PLATEAU]), (WF_PROCESSOR) i, trap->plateautime, max_pg_wamp);
1591  createramp(&(trap->wfpulse[i][G_DECAY]), (WF_PROCESSOR) i, trap->ramptime, max_pg_wamp, 0,
1592  maxGradRes * (trap->ramptime / GRAD_UPDATE_TIME), 1.0);
1593 
1594  linkpulses(3, &(trap->wfpulse[i][G_PLATEAU]), &(trap->wfpulse[i][G_ATTACK]), &(trap->wfpulse[i][G_DECAY]));
1595 
1596  } else {
1597 
1598  trap->wfpulse[i] = NULL;
1599 
1600  }
1601 
1602  } /* per board */
1603 
1604  } /* wfpulse == NULL */
1605 
1606 
1607  /* allocate the array of WFINSTANCE's if needed */
1608  if (!trap->wfi) {
1609  trap->wfi = (KS_WFINSTANCE *) AllocNode(trap->base.ninst * sizeof(KS_WFINSTANCE));
1610  }
1611 
1612  /* Set instruction amplitudes at the proper position and board */
1613  {
1614  const int corrected_pos = loc.pos + (loc.board == OMEGA)*psd_rf_wait;
1615  /* creating new instructions */
1616  createinstr(&(trap->wfpulse[loc.board][G_ATTACK]),
1617  corrected_pos,
1618  trap->ramptime,
1619  (int)(trap->amp * loc.ampscale * MAX_PG_IAMP / boardmax));
1620  createinstr(&(trap->wfpulse[loc.board][G_PLATEAU]),
1621  corrected_pos + trap->ramptime,
1622  trap->plateautime,
1623  (int)(trap->amp * loc.ampscale * MAX_PG_IAMP / boardmax));
1624  createinstr(&(trap->wfpulse[loc.board][G_DECAY]),
1625  corrected_pos + trap->ramptime + trap->plateautime,
1626  trap->ramptime,
1627  (int)(trap->amp * loc.ampscale * MAX_PG_IAMP / boardmax));
1628  }
1629 
1630  /* initializing the corresponding KS_WFINSTANCE */
1631  trap->wfi[trap->base.ngenerated].boardinstance = trap->wfpulse[loc.board][G_PLATEAU].ninsts - 1; /* zero based */
1632  trap->wfi[trap->base.ngenerated].wf = &(trap->wfpulse[loc.board][G_PLATEAU]);
1633  trap->wfi[trap->base.ngenerated].loc = loc;
1634 
1635  trap->base.ngenerated++;
1636 
1637  /* sort after the last instance is generated */
1638  if (trap->base.ngenerated == trap->base.ninst) {
1639  ks_sort_wfi_by_timeboard(trap->wfi, trap->base.ninst);
1640  }
1641  }
1642 #else /* TGT(IPG) or HOST */
1643  {
1644  /* HOST */
1645 
1646  trap->locs[trap->base.ninst] = loc;
1647 
1648  /* update instance counter on host only. This counter is used to allocate the right amount of WFINSTANCEs on IPG */
1649  trap->base.ninst++;
1650 
1651  /* for trap, we also need to keep track of #instances per X, Y, Z for gradient heating calculations.
1652  As this needs to be done in HOST before the IPG (target), we can't use the wfi[...].boardinstance or .loc information above for this */
1653  if (loc.board == XGRAD || loc.board == YGRAD || loc.board == ZGRAD)
1654  trap->gradnum[loc.board]++;
1655  }
1656 
1657 #endif /* IPG */
1658  /* register this KS_TRAP for later heating calculations */
1659  {
1660  STATUS status = ks_eval_addtraptogradrfctrl(&ctrl->gradrf, trap);
1661  if (status != SUCCESS) return status;
1662  }
1663  return SUCCESS;
1664 
1665 } /* ks_pg_trap */
STATUS ks_eval_addtraptogradrfctrl(KS_GRADRFCTRL *gradrfctrl, KS_TRAP *trap)
*Internal use*. Adds a trapezoid (KS_TRAP) to the KS_GRADRFCTRL struct for later gradient heating cal...
Definition: KSFoundation_common.c:3773
void ks_sort_wfi_by_timeboard(KS_WFINSTANCE *a, int nitems)
Sort WF_INSTANCEs in time, then board
Definition: KSFoundation_common.c:3283
int plateautime
Definition: KSFoundation.h:584
int boardinstance
Definition: KSFoundation.h:399
#define areSame(a, b)
Definition: KSFoundation.h:119
KS_BASE base
Definition: KSFoundation.h:579
int pos
Definition: KSFoundation.h:389
(Internal use) Structure being a part of various sequence objects to sort them in time across boards...
Definition: KSFoundation.h:398
int ninst
Definition: KSFoundation.h:409
STATUS ks_error(const char *format,...)
Definition: KSFoundation_common.c:70
int board
Definition: KSFoundation.h:388
int duration
Definition: KSFoundation.h:1135
float ks_syslimits_gradtarget(LOG_GRAD loggrd, int board)
Returns the maximum target amplitude for a board (internal use)
Definition: KSFoundation_common.c:220
float ampscale
Definition: KSFoundation.h:390
Definition: KSFoundation.h:1910
KS_GRADRFCTRL gradrf
Definition: KSFoundation.h:1143
KS_WFINSTANCE * wfi
Definition: KSFoundation.h:591
int isNaN(float a)
Definition: KSFoundation_common.c:58
#define KS_DESCRIPTION_LENGTH
Definition: KSFoundation.h:185
int maxGradRes
KS_SEQLOC loc
Definition: KSFoundation.h:401
LOG_GRAD loggrd
int is_cleared_on_tgt
Definition: KSFoundation.h:974
float amp
Definition: KSFoundation.h:581
KS_SEQLOC locs[KS_MAXINSTANCES]
Definition: KSFoundation.h:589
WF_PULSE * wf
Definition: KSFoundation.h:400
KS_DESCRIPTION description
Definition: KSFoundation.h:580
void ks_tgt_reset_gradrfctrl(KS_GRADRFCTRL *gradrfctrl)
Definition: KSFoundation_tgt.c:1481
KS_DESCRIPTION description
Definition: KSFoundation.h:1141
int ngenerated
Definition: KSFoundation.h:410
Definition: KSFoundation.h:1910
WF_PULSE ** wfpulse
Definition: KSFoundation.h:590
int duration
Definition: KSFoundation.h:585
int psd_rf_wait
int gradnum[3]
Definition: KSFoundation.h:586
Definition: KSFoundation.h:1910
int ramptime
Definition: KSFoundation.h:583

◆ ks_pg_read()

STATUS ks_pg_read ( KS_READ read,
int  pos,
KS_SEQ_CONTROL ctrl 
)

Places a KS_READ sequence object at some position in the pulse sequence

This function should be called in the @pg section to place out a KS_READ sequence object that creates an acquisition window in the pulse sequence. The same KS_READ object can be freely placed as many times as desired, by only changing the second argument each time (to avoid overlaps). The second argument is an integer with the absolute time in [us] of the start of the acquisition. System gradient delays are accounted for in ks_pg_read() by internally adjusting the position by adding the system variable psd_grd_wait to the position value. This assures the gradients boards and acquisition window are in sync on the MR system.

Regardless of the order the acquisition windows are placed out in the @pg section, the instance number of the KS_READ is automatically sorted in time

If the .duration field in the KS_READ object is 0, ks_pg_read() will return SUCCESS and quietly ignore placing it out. This is a part of the mechanism that setting the .duration field to 0 in cveval() should eliminate its existance in both timing calulations and in the pulse sequence generation in @pg

GENERAL NOTE: It is a requirement that the function in the @pg section containing all ks_pg_***() calls is also called exactly once in cveval() after the calls to the corresponding ks_eval_***() functions that design the KS_*** sequence objects. Each ks_pg_***() will throw an error if this has not been done.

Parameters
[in,out]readPointer to a KS_READ sequence object
[in]posAbsolute time in [us] when to start data acquisition
[in,out]ctrlPointer to the KS_SEQ_CONTROL struct corresponding to the sequence module for this KS_READ
Return values
STATUSSUCCESS or FAILURE
1688  {
1689 #ifdef IPG
1690  int acqwin_start;
1691  int placementindx;
1692  long dab_pos;
1693  char tmpstr[KS_DESCRIPTION_LENGTH + 7];
1694 #endif
1695  STATUS status;
1696 
1697  /* duration of 0 should not throw an error as this is a sign of that it has not been set up or has manually been disabled this way */
1698  if (read->duration == 0) {
1699  return SUCCESS;
1700  }
1701 
1702 #ifdef IPG
1703  {
1704  tmpstr[KS_DESCRIPTION_LENGTH + 7 - 1] = 0;
1705 
1706  if (read->base.ngenerated >= read->base.ninst) {
1707  ks_error("ks_pg_read @tgt(%s): the number of instances generated on the target exceeds the number of ones generated on the host", read->description);
1708  return FAILURE;
1709  }
1710 
1711  if (!ctrl->gradrf.is_cleared_on_tgt) { /* Reset gradrfctrl on tgt to remove dead pointers copied from host. */
1713  }
1714 
1715  if (read->echo == NULL) {
1716  /* this relies on that we have run the sequence in cveval() (i.e. on host) before to get proper grad.ninst */
1717  read->echo = (WF_PULSE *) AllocNode(read->base.ninst * sizeof(WF_PULSE));
1718  }
1719 
1720  placementindx = read->base.ngenerated; /* not sorted in time, but in placement order */
1721 
1722  acqwin_start = pos + psd_grd_wait;
1723 
1724  if (placementindx == 0 || read->filt.tdaq > 200) {
1725  dab_pos = DEFAULTPOS; /* results in 200 us before RBA (DEFAULTPOS is defined as 0. See: /ESE_.../psd/include/private/pulsegen.h) */
1726  } else {
1727  /* For small matrix sizes, the acq window is so short that the DAB packet
1728  for current lobe may collide with the RBA belonging to the previous lobe.
1729  We are not allowed to decrease the DAB-RBA distance below 200us. Instead,
1730  let's put it right after the end of the previous XTR pulse.
1731  With the default XTR-RBA offset of 143us, and since the RBA-RBA distance
1732  is equal to read->grad.duration, we will get a DAB-RBA offset of
1733  acqwin_duration + 143us - 17us (XTR duration)
1734  */
1735  dab_pos = pbegallssp(ks_pg_echossp(&read->echo[placementindx - 1], "xtr"), 0) + 32 /* LONG_DAB_length = 31 */;
1736  if (dab_pos + 200 > acqwin_start)
1737  dab_pos = DEFAULTPOS; /* fallback to default pos. Perhaps the readouts were not placed out in time order in the PSD ? */
1738  }
1739 
1740  /* name the ACQ pulse */
1741  sprintf(tmpstr, "%s%03d", read->description, placementindx); pulsename(&(read->echo[placementindx]), tmpstr);
1742 
1743  if (read->filt.fslot == KS_NOTSET) {
1744  /* add 'return' before ks_error once we've got an answer from GE regarding acqq and setrfltrs() */
1745  ks_error("ks_pg_read @tgt(%s): WARNING no filter slot assigned. First run setfilter(&read.filt)", read->description);
1746  }
1747 
1748  /* place the ACQ pulse */
1749  acqq_longdab(&(read->echo[placementindx]),
1750  (long) acqwin_start,
1751  (long) dab_pos, /* DAB */
1752  (long) DEFAULTPOS, /* XTR */
1753  read->filt.fslot /* filter slot */);
1754 
1755  read->base.ngenerated++;
1756 
1757  /* sort after the last instance is generated */
1758  if (read->base.ngenerated == read->base.ninst) {
1759  ks_sort_wfp_by_time(read->echo, read->base.ninst);
1760  }
1761 
1762  /* Associate this echo with a global filter slot number
1763  This number is generated by the GE function setfilter() in e.g. predownload(), before this function is called.
1764  For main sequences, there is also the wrapper function GEReq_predownload_setfilter() */
1765  if (read->filt.fslot >= 0 && read->filt.fslot <= MAX_FILTER_SLOT) {
1766  setrfltrs((int) read->filt.fslot, &(read->echo[placementindx]));
1767  }
1768 
1769  }
1770 #else /* IPG */
1771  {
1772  read->pos[read->base.ninst] = pos;
1773  read->base.ninst++; /* update acq counter on host */
1774  }
1775 #endif /* IPG */
1776 
1777  status = ks_eval_addreadtogradrfctrl(&ctrl->gradrf, read);
1778  if (status != SUCCESS) return status;
1779  return SUCCESS;
1780 }
WF_PULSE * echo
Definition: KSFoundation.h:738
void ks_sort_wfp_by_time(WF_PULSE *a, int nitems)
Sort WF_PULSEs in time
Definition: KSFoundation_common.c:3292
#define KS_NOTSET
Definition: KSFoundation.h:103
int ninst
Definition: KSFoundation.h:409
STATUS ks_error(const char *format,...)
Definition: KSFoundation_common.c:70
STATUS ks_eval_addreadtogradrfctrl(KS_GRADRFCTRL *gradrfctrl, KS_READ *read)
*Internal use*. Adds an acquisition to the KS_GRADRFCTRL struct. Used for plotting
Definition: KSFoundation_common.c:3857
KS_BASE base
Definition: KSFoundation.h:731
KS_GRADRFCTRL gradrf
Definition: KSFoundation.h:1143
KS_DESCRIPTION description
Definition: KSFoundation.h:732
#define KS_DESCRIPTION_LENGTH
Definition: KSFoundation.h:185
int is_cleared_on_tgt
Definition: KSFoundation.h:974
int duration
Definition: KSFoundation.h:733
WF_PULSE * ks_pg_echossp(WF_PULSE *echo, const char *suffix)
Returns the pointer to XTR or RBA for an echo WF_PULSE (internal use)
Definition: KSFoundation_common.c:1458
STATUS acqq_longdab(WF_PULSE_ADDR pulse, LONG pos_ref, LONG dab_ref, LONG xtr_ref, LONG fslot_value)
Definition: KSFoundation_tgt.c:1510
FILTER_INFO filt
Definition: KSFoundation.h:735
int psd_grd_wait
void ks_tgt_reset_gradrfctrl(KS_GRADRFCTRL *gradrfctrl)
Definition: KSFoundation_tgt.c:1481
int ngenerated
Definition: KSFoundation.h:410
int pos[KS_MAXUNIQUE_READ]
Definition: KSFoundation.h:736

◆ ks_pg_phaser()

STATUS ks_pg_phaser ( KS_PHASER phaser,
KS_SEQLOC  loc,
KS_SEQ_CONTROL ctrl 
)

Places a KS_PHASER sequence object on a board at some position in the pulse sequence

This function should be called in the @pg section to place out a KS_PHASER sequence object that creates a dynamic trapezoid for phase encoding. For 3D applications, two different KS_PHASER objects should have been designed by calling ks_eval_phaser() on each KS_PHASER as FOV and resolution typically differ for the two phase encoding directions. The same KS_PHASER object can be freely placed on XGRAD, YGRAD and ZGRAD as many times as desired, by only changing the second argument each time (to avoid waveform overlaps). For Cartesian applications, phase encoding gradients such as KS_PHASER are typically placed out on YGRAD (phase encoding axis), for 3D also on ZGRAD. For 3D, the KS_PHASER object may embed a slice rephaser (static trapezoid) by setting .areaoffset to a non-zero value (see ks_eval_phaser()). The second argument is a KS_SEQLOC struct with three fields:

Regardless of the order placed out in the @pg section, and on which boards (XGRAD, YGRAD, ZGRAD) the KS_PHASER object is placed, the instance number of the KS_PHASER is automatically sorted in time. If two instances of one KS_PHASER occur at the same time (.pos) on different boards, XGRAD comes before YGRAD, which comes before ZGRAD. These abstract, sorted, instance numbers are used by ks_scan_***() functions in scan() to refer to a specific instance of a KS_PHASER object in the sequence

If the .duration field in the KS_PHASER object is 0, ks_pg_phaser() will return SUCCESS and quietly ignore placing it out. This is a part of the mechanism that setting the .duration field to 0 in cveval() should eliminate its existance in both timing calulations and in the pulse sequence generation in @pg

GENERAL NOTE: It is a requirement that the function in the @pg section containing all ks_pg_***() calls is also called exactly once in cveval() after the calls to the corresponding ks_eval_***() functions that design the KS_*** sequence objects. Each ks_pg_***() will throw an error if this has not been done.

Parameters
[in,out]phaserPointer to a KS_PHASER sequence object
[in]locKS_SEQLOC struct to specify when and where to place the KS_PHASER
[in,out]ctrlPointer to the KS_SEQ_CONTROL struct corresponding to the sequence module for this KS_PHASER
Return values
STATUSSUCCESS or FAILURE
1669  {
1670 
1671 
1672  /* loc.ampscale != 1.0 is not allowed */
1673  if (!areSame(loc.ampscale,1.0)) {
1674  return ks_error("ks_pg_phaser(%s): field 'ampscale' in 2nd arg must be 1.0", phaser->grad.description);
1675  }
1676 
1677  /* loc.board must be one of XGRAD, YGRAD, ZGRAD */
1678  if (loc.board != XGRAD && loc.board != YGRAD && loc.board != ZGRAD) {
1679  return ks_error("ks_pg_phaser(%s): field 'board' in 2nd arg must be XGRAD, YGRAD, or ZGRAD", phaser->grad.description);
1680  }
1681 
1682  return ks_pg_trap(&phaser->grad, loc, ctrl);
1683 
1684 }
#define areSame(a, b)
Definition: KSFoundation.h:119
KS_TRAP grad
Definition: KSFoundation.h:1676
STATUS ks_error(const char *format,...)
Definition: KSFoundation_common.c:70
int board
Definition: KSFoundation.h:388
float ampscale
Definition: KSFoundation.h:390
KS_DESCRIPTION description
Definition: KSFoundation.h:580
STATUS ks_pg_trap(KS_TRAP *trap, KS_SEQLOC loc, KS_SEQ_CONTROL *ctrl)
Places a KS_TRAP sequence object on a board at some position in the pulse sequence
Definition: KSFoundation_common.c:1475

◆ ks_pg_readtrap()

STATUS ks_pg_readtrap ( KS_READTRAP readtrap,
KS_SEQLOC  loc,
KS_SEQ_CONTROL ctrl 
)

Places a KS_READTRAP sequence object on a board at some position in the pulse sequence

This function should be called in the @pg section to place out a KS_READTRAP sequence object consisting of a trapezoid (KS_TRAP) and an acquisition window (KS_READ). The same KS_READTRAP object can be freely placed on XGRAD, YGRAD and ZGRAD as many times as desired, by only changing the second argument each time (to avoid waveform overlaps). For Cartesian applications, readouts such as KS_READTRAP are typically placed on XGRAD (frequency encoding axis). The second argument is a KS_SEQLOC struct with three fields:

  • .board: Choose between XGRAD, YGRAD, ZGRAD
  • .pos: Absolute time in [us] of the start of the attack ramp of the readout trapezoid
  • .ampscale: For KS_READTRAP objects, this must be either +1.0 or -1.0 so that the FOV is not altered. Negative amplitudes will automatically be taken into account in ks_scan_offsetfov() to create the necessary frequency offset

Regardless of the order placed out in the @pg section, and on which boards (XGRAD, YGRAD, ZGRAD) the KS_READTRAP object is placed, the instance number of the KS_READTRAP is automatically sorted in time. If two instances of one KS_READTRAP occur at the same time (.pos) on different boards, XGRAD comes before YGRAD, which comes before ZGRAD. These abstract, sorted, instance numbers are used by ks_scan_***() functions in scan() to refer to a specific instance of a KS_READTRAP object in the sequence.

If the .duration field in the KS_READTRAP object is 0, ks_pg_readtrap() will return SUCCESS and quietly ignore placing it out. This is a part of the mechanism that setting the .duration field to 0 in cveval() should eliminate its existance in both timing calulations and in the pulse sequence generation in @pg

GENERAL NOTE: It is a requirement that the function in the @pg section containing all ks_pg_***() calls is also called exactly once in cveval() after the calls to the corresponding ks_eval_***() functions that design the KS_*** sequence objects. Each ks_pg_***() will throw an error if this has not been done.

Parameters
[in,out]readtrapPointer to a KS_READTRAP sequence object
[in]locKS_SEQLOC struct to specify when and where to place the KS_READTRAP
[in,out]ctrlPointer to the KS_SEQ_CONTROL struct corresponding to the sequence module for this KS_READTRAP
Return values
STATUSSUCCESS or FAILURE
1785  {
1786  KS_SEQLOC omegaloc;
1787 
1788  /* checking for NULL pointers */
1789  if (readtrap == NULL) {
1790  return ks_error("%s: First argument is NULL", __FUNCTION__);
1791  }
1792  /* duration of 0 should not throw an error as this is a sign of that it has not been set up
1793  or has manually been disabled this way */
1794  if (readtrap->grad.duration == 0) {
1795  return SUCCESS;
1796  }
1797  if (readtrap->grad.description == NULL || readtrap->grad.description[0] == 0 || readtrap->grad.description[0] == ' ') {
1798  return ks_error("%s: Invalid description ('%s')", __FUNCTION__, readtrap->grad.description);
1799  }
1800 
1801 #ifdef IPG
1802  /* TGT (IPG): Don't place anything on hardware if ctrl->duration = 0.
1803  This is in concert with:
1804  - not performing createseq() in KS_SEQLENGTH if seqctrl.duration = 0
1805  - not playing sequence modules in scan() using ks_scan_playsequence() if seqctrl.duration = 0
1806  */
1807  if (ctrl->duration == 0) {
1808  return ks_error("%s(%s): Refusing to place readtrap since seqctrl.duration = 0 (indicating `%s` not to be played in scan())", __FUNCTION__, readtrap->grad.description, ctrl->description);
1809  }
1810 #endif
1811 
1812  if (!areSame(loc.ampscale, 1.0) && !areSame(loc.ampscale, -1.0)) {
1813  ks_error("%s(%s): .ampscale %g must be exactly +1 or -1 for readout gradients", __FUNCTION__, readtrap->grad.description, loc.ampscale);
1814  return FAILURE;
1815  }
1816 
1817  /*** acq ***/
1818  if (ks_pg_read(&readtrap->acq, loc.pos + readtrap->acqdelay, ctrl) == FAILURE)
1819  return FAILURE;
1820 
1821 
1822  /*** trapezoid ***/
1823  if (ks_pg_trap(&readtrap->grad, loc, ctrl) == FAILURE)
1824  return FAILURE;
1825 
1826  /* also place the omega wave form for rampsampled cases */
1827  if (readtrap->omega.duration > 0) {
1828  /* omega trapezoid matching the readout lobe to allow for FOV offsets in freq dir. with ramp sampling */
1829  omegaloc.board = OMEGA;
1830  omegaloc.pos = loc.pos;
1831  omegaloc.ampscale = 1.0; /* just unity value for conformance. It does not matter during scanning, since ks_scan_omegahz() ignores this value */
1832  if (ks_pg_trap(&readtrap->omega, omegaloc, ctrl) == FAILURE)
1833  return FAILURE;
1834  }
1835 
1836 
1837  return SUCCESS;
1838 
1839 
1840 } /* ks_pg_readtrap */
STATUS ks_pg_read(KS_READ *read, int pos, KS_SEQ_CONTROL *ctrl)
Places a KS_READ sequence object at some position in the pulse sequence
Definition: KSFoundation_common.c:1688
#define areSame(a, b)
Definition: KSFoundation.h:119
int pos
Definition: KSFoundation.h:389
STATUS ks_error(const char *format,...)
Definition: KSFoundation_common.c:70
int board
Definition: KSFoundation.h:388
int duration
Definition: KSFoundation.h:1135
KS_TRAP grad
Definition: KSFoundation.h:1586
float ampscale
Definition: KSFoundation.h:390
KS_TRAP omega
Definition: KSFoundation.h:1587
typedef struct used as argument to ks_pg_*** functions to control where and when to place a sequence ...
Definition: KSFoundation.h:387
KS_DESCRIPTION description
Definition: KSFoundation.h:580
KS_DESCRIPTION description
Definition: KSFoundation.h:1141
KS_READ acq
Definition: KSFoundation.h:1574
int duration
Definition: KSFoundation.h:585
STATUS ks_pg_trap(KS_TRAP *trap, KS_SEQLOC loc, KS_SEQ_CONTROL *ctrl)
Places a KS_TRAP sequence object on a board at some position in the pulse sequence
Definition: KSFoundation_common.c:1475
int acqdelay
Definition: KSFoundation.h:1580

◆ ks_pg_wave()

STATUS ks_pg_wave ( KS_WAVE wave,
KS_SEQLOC  loc,
KS_SEQ_CONTROL ctrl 
)

Places a KS_WAVE sequence object on a board at some position in the pulse sequence

This function should be called in the @pg section to place out a KS_WAVE sequence object that creates an arbitrary waveform in the pulse sequence. The same KS_WAVE object can be placed on any board as many times as desired, by only changing the second argument each time (to avoid waveform overlaps). However, the RHO (RF) board should be avoided as this bypasses RF scaling and SAR calculations, providing no control over the flip angle. For RF waveforms, always use KS_RF (or KS_SELRF). The second argument is a KS_SEQLOC struct with three fields:

  • .board: Choose between XGRAD, YGRAD, ZGRAD, OMEGA, THETA
  • .pos: Absolute time in [us] of the start of the waveform. For RF, OMEGA and THETA, pos_rf_wait will be added to this time to take system delays into account
  • .ampscale: For gradient boards, this factor must be in range [-1.0,1.0] that is multiplied with the waveform to yield a per-instance (i.e. per-placement) amplitude ([G/cm]). For THETA, only values of +1.0 and -1.0 is allowed, and for OMEGA only 1.0 is allowed

The KS_WAVE sequence object contains a KS_WAVEFORM (float[KS_MAXWAVELEN]) to hold the waveform. The physical unit of the waveform in the KS_WAVE object depends the board on which it is placed using ks_pg_wave(). For the

  • gradient boards (XGRAD, YGRAD, ZGRAD), the unit is [G/cm]
  • THETA board, the unit is [degrees]
  • RF (RHO) and OMEGA boards, the units are arbitrary (c.f. ks_eval_rf() and ks_eval_wave()). Don't call ks_pg_wave() directly to place a KS_WAVE on the RF board

When a waveform is placed on OMEGA, neither the waveform amplitude nor the .ampscale (in KS_SEQLOC) affects the final frequency modulation. Instead the run-time amplitude of a KS_WAVE object on the OMEGA board is controlled in scan() via a function called ks_scan_omegawave_hz(), where the largest value in the field .waveform[] in the KS_WAVE will correspond to the value in [Hz] provided.

If the .duration field in the KS_WAVE object is 0, ks_pg_wave() will return SUCCESS and quietly ignore placing it out. This is a part of the mechanism that setting the .duration field to 0 in cveval() should eliminate its existance in both timing calulations and in the pulse sequence generation in @pg

All KS_WAVE objects are double buffered in ks_pg_wave() so that waveforms can be replaced in run-time (scan()) using ks_scan_wave2hardware()

Regardless of the order placed out in the @pg section, and on which boards (XGRAD, YGRAD, ZGRAD) the KS_WAVE object is placed, the instance number of the KS_WAVE is automatically sorted in time. If two instances of one KS_WAVE occur at the same time (.pos) on different boards, XGRAD comes before YGRAD, which comes before ZGRAD. These abstract, sorted, instance numbers are used by ks_scan_***() functions in scan() to refer to a specific instance of a KS_WAVE object in the sequence.

GENERAL NOTE: It is a requirement that the function in the @pg section containing all ks_pg_***() calls is also called exactly once in cveval() after the calls to the corresponding ks_eval_***() functions that design the KS_*** sequence objects. Each ks_pg_***() will throw an error if this has not been done.

Parameters
[in,out]wavePointer to a KS_WAVE sequence object
[in]locKS_SEQLOC struct to specify when and where to place the KS_WAVE
[in,out]ctrlPointer to the KS_SEQ_CONTROL struct corresponding to the sequence module for this KS_WAVE
Return values
STATUSSUCCESS or FAILURE
1849  {
1850  float boardmax;
1851  char boardc[8] = "xyzrrto"; /* x,y,z,rho1,rho2,theta,omega */
1852  float waveamp = ks_wave_absmax(wave); /* maximum amplitude in wave (in its own units, like e.g. [G/cm] or [degrees]) */
1853 
1854  boardmax = ks_syslimits_gradtarget(loggrd, loc.board); /* returns 1.0 for non-gradients boards */
1855 
1856  /* checking for NULL pointers */
1857  if (wave == NULL) {
1858  return ks_error("ks_pg_wave: First argument is NULL");
1859  }
1860  /* duration of 0 should not throw an error as this is a sign of that it has not been set up
1861  or has manually been disabled this way */
1862  if (wave->duration == 0) {
1863  return SUCCESS;
1864  }
1865  if (wave->description == NULL || wave->description[0] == 0 || wave->description[0] == ' ') {
1866  return ks_error("ks_pg_wave: Invalid description ('%s')", wave->description);
1867  }
1868 
1869 #ifdef IPG
1870  /* TGT: Don't place anything on hardware if ctrl->duration = 0.
1871  This is in concert with:
1872  - not performing createseq() in KS_SEQLENGTH if seqctrl.duration = 0
1873  - not playing sequence modules in scan() using ks_scan_playsequence() if seqctrl.duration = 0
1874  */
1875  if (ctrl->duration == 0) {
1876  return ks_error("%s(%s): Refusing to place wave since seqctrl.duration = 0 (indicating `%s` not to be played in scan())", __FUNCTION__, wave->description, ctrl->description);
1877  }
1878 
1879  if (!ctrl->gradrf.is_cleared_on_tgt) /* Reset gradrfctrl on tgt to remove dead pointers copied from host. */
1880  {
1882  }
1883 
1884 #endif
1885 
1886  /* input validation */
1887  if (wave->duration % 4) {
1888  return ks_error("ks_pg_wave(%s): field 'duration' (%d) must be divisible by 4", wave->description, wave->duration);
1889  }
1890  if (wave->res == 0) {
1891  return ks_error("ks_pg_wave(%s): field 'res' cannot be 0", wave->description);
1892  }
1893  if (wave->res % 2) {
1894  return ks_error("ks_pg_wave(%s): field 'res' (%d) must be even ", wave->description, wave->res);
1895  }
1896  if (wave->duration % wave->res) {
1897  return ks_error("ks_pg_wave(%s): field 'duration' (%d) must be divisible by 'res' %d", wave->description, wave->duration, wave->res);
1898  }
1899  if (loc.board == XGRAD || loc.board == YGRAD || loc.board == ZGRAD) {
1900  if ((wave->duration / wave->res) % GRAD_UPDATE_TIME) {
1901  return ks_error("ks_pg_wave(%s): field 'duration' (%d) must a multiple of 4 times 'res' (%d) for gradient boards", wave->description, wave->duration, wave->res);
1902  }
1903  if (wave->gradwave_units == KS_GRADWAVE_ABSOLUTE && !areSame(loc.ampscale,1.0)) {
1904  return ks_error("ks_pg_wave(%s): ampscale must be +1.0 for gradwave with absolute units", wave->description);
1905  }
1906  } else {
1907  if ((wave->duration / wave->res) % RF_UPDATE_TIME) {
1908  return ks_error("ks_pg_wave(%s): field 'duration' (%d) must a multiple of 2 times 'res' (%d) for non-gradient boards", wave->description, wave->duration, wave->res);
1909  }
1910  }
1911 
1912  if (isNaN(loc.ampscale)) {
1913  return ks_error("ks_pg_wave(%s): loc.ampscale NaN", wave->description);
1914  }
1915  if (loc.pos < GRAD_UPDATE_TIME || loc.pos > 4e6) {
1916  return ks_error("ks_pg_wave(%s): Invalid position %d, it must be between 4us and 4s", wave->description, loc.pos);
1917  }
1918  if (loc.board == OMEGA && !areSame(loc.ampscale,1.0)) {
1919  return ks_error("ks_pg_wave(%s): ampscale must be +1.0 for OMEGA", wave->description);
1920  }
1921  if (loc.board == THETA && !areSame(loc.ampscale,1.0) && !areSame(loc.ampscale,-1.0)) {
1922  return ks_error("ks_pg_wave(%s): ampscale must be +1.0 or -1.0 for THETA", wave->description);
1923  }
1924  if (loc.ampscale < -1.0 || loc.ampscale > 1.0) {
1925  return ks_error("ks_pg_wave(%s): Invalid ampscale %g, it must be between -1.0 and 1.0", wave->description, loc.ampscale);
1926  }
1927  if (fabs(waveamp * loc.ampscale) > boardmax && loc.board != THETA) {
1928  return ks_error("ks_pg_wave(%s): wave amplitude (%.2f) exceeds maximum (%g) for board %c", wave->description, waveamp * loc.ampscale, boardmax, boardc[loc.board]);
1929  }
1930 
1931  loc.pos = RUP_GRD(loc.pos);
1932 
1933 
1934 #ifdef HOST_TGT
1935 
1936  /* HOST */
1937 
1938  wave->locs[wave->base.ninst] = loc;
1939 
1940  /* update instance counter on host only. This counter is used to allocate the right amount of WFINSTANCEs on IPG */
1941  wave->base.ninst++;
1942 
1943  /* for wave, we also need to keep track of #instances per X, Y, Z for gradient heating calculations.
1944  As this needs to be done in HOST before the IPG (target), we can't use the wfi[...].boardinstance or .loc information above for this */
1945  if (loc.board == XGRAD || loc.board == YGRAD || loc.board == ZGRAD) {
1946  wave->gradnum[loc.board]++;
1947  }
1948 
1949 #else
1950 
1951  {
1952 
1953  int i;
1954  int wave_ptr, isrf, iamp;
1955  char tmpstr[KS_DESCRIPTION_LENGTH + 7];
1956 
1957  if (wave->base.ngenerated >= wave->base.ninst) {
1958  ks_error("ks_pg_wave @tgt(%s): the number of instances generated on the target (%d) exceeds the number of ones generated on the host (%d)", wave->description, wave->base.ngenerated + 1, wave->base.ninst);
1959  return FAILURE;
1960  }
1961 
1962  /* first call on TGT, allocate WF_PULSE * array where each element corresponds to one board */
1963  if (wave->wfpulse == NULL) {
1964  wave->wfpulse = (WF_PULSE **) AllocNode(7 * sizeof(WF_PULSE *)); /* WF pointers for all boards (XGRAD->OMEGA) */
1965  } /* wfpulse == NULL */
1966 
1967 
1968  /* first call on TGT using this loc.board, make waveform memory (double buffered) */
1969  if (wave->wfpulse[loc.board] == NULL) {
1970  WF_PULSE wfInit = INITPULSE;
1971  KS_IWAVE iwave;
1972 
1973  wave->wfpulse[loc.board] = (WF_PULSE *) AllocNode(KS_WF_SIZE * sizeof(WF_PULSE)); /* KS_WF_SIZE (3) to allow for double buffering */
1974 
1975  for (i = KS_WF_MAIN; i < KS_WF_SIZE; i++)
1976  wave->wfpulse[loc.board][i] = wfInit;
1977 
1978  sprintf(tmpstr, "%s.%c", wave->description, boardc[loc.board]); pulsename(&(wave->wfpulse[loc.board][KS_WF_MAIN]), tmpstr);
1979  sprintf(tmpstr, "%s_buf1.%c", wave->description, boardc[loc.board]); pulsename(&(wave->wfpulse[loc.board][KS_WF_BUF1]), tmpstr);
1980  sprintf(tmpstr, "%s_buf2.%c", wave->description, boardc[loc.board]); pulsename(&(wave->wfpulse[loc.board][KS_WF_BUF2]), tmpstr);
1981 
1982  /* wave -> short int wave for hardware use. All waveforms but THETA are autoscaled to +/- max_pg_wamp (32766)*/
1983  if (ks_wave2iwave(iwave, wave, loc.board) == FAILURE)
1984  return FAILURE;
1985 
1986  /* reserve hardware memory to allow for double buffering */
1987  for (i = KS_WF_MAIN; i <= KS_WF_BUF2; i++) {
1988  createreserve(&wave->wfpulse[loc.board][i], (WF_PROCESSOR) loc.board, wave->res);
1989  wave->wfpulse[loc.board][i].type = TYPEXTERNAL;
1990  movewaveimm(iwave, &wave->wfpulse[loc.board][i], (int) 0, wave->res, TOHARDWARE);
1991  }
1992 
1993  } /* wfpulse[loc.board] == NULL */
1994 
1995 
1996  /* create instruction at the current position */
1997  isrf = loc.board == RHO || loc.board == RHO2 || loc.board == THETA || loc.board == OMEGA;
1998 
1999  if (loc.board == THETA)
2000  iamp = (int)(loc.ampscale * MAX_PG_IAMP);
2001  else
2002  iamp = (int)((waveamp * loc.ampscale / boardmax) * MAX_PG_IAMP);
2003 
2004  createinstr(&wave->wfpulse[loc.board][KS_WF_MAIN],
2005  (long) (loc.pos + (psd_rf_wait * isrf)),
2006  (long) wave->duration,
2007  (long) iamp);
2008 
2009  /* WF instance bookkeeping */
2010  if (wave->wfi == NULL) {
2011  wave->wfi = (KS_WFINSTANCE *) AllocNode(wave->base.ninst * sizeof(KS_WFINSTANCE));
2012  }
2013  wave->wfi[wave->base.ngenerated].boardinstance = wave->wfpulse[loc.board][KS_WF_MAIN].ninsts - 1;
2014  wave->wfi[wave->base.ngenerated].wf = wave->wfpulse[loc.board];
2015  wave->wfi[wave->base.ngenerated].loc = loc;
2016 
2017  /* After instructions have been set, set the wfpulse[KS_WF_MAIN] to the first waveform buffer (_BUF1) */
2018  getwave(&wave_ptr, &(wave->wfpulse[loc.board][KS_WF_BUF1]));
2019  setwave(wave_ptr, &(wave->wfpulse[loc.board][KS_WF_MAIN]), wave->wfi[wave->base.ngenerated].boardinstance);
2020 
2021  wave->base.ngenerated++;
2022 
2023  /* sort after the last instance is generated */
2024  if (wave->base.ngenerated == wave->base.ninst) {
2025  ks_sort_wfi_by_timeboard(wave->wfi, wave->base.ninst);
2026  }
2027 
2028  }
2029 
2030 #endif /* IPG or HOST */
2031 
2032  /* register this KS_WAVE for later heating calculations */
2033  {
2034  STATUS status = ks_eval_addwavetogradrfctrl(&ctrl->gradrf, wave);
2035  if (status != SUCCESS) return status;
2036  }
2037 
2038  return SUCCESS;
2039 
2040 } /* ks_pg_wave */
Definition: KSFoundation.h:1975
void ks_sort_wfi_by_timeboard(KS_WFINSTANCE *a, int nitems)
Sort WF_INSTANCEs in time, then board
Definition: KSFoundation_common.c:3283
short KS_IWAVE[KS_MAXWAVELEN]
Definition: KSFoundation.h:263
int boardinstance
Definition: KSFoundation.h:399
#define areSame(a, b)
Definition: KSFoundation.h:119
float ks_wave_absmax(const KS_WAVE *wave)
Returns the maximum absolute value in a KS_WAVE sequence object
Definition: KSFoundation_common.c:1224
Definition: KSFoundation.h:1957
int pos
Definition: KSFoundation.h:389
KS_SEQLOC locs[KS_MAXINSTANCES]
Definition: KSFoundation.h:672
(Internal use) Structure being a part of various sequence objects to sort them in time across boards...
Definition: KSFoundation.h:398
int ninst
Definition: KSFoundation.h:409
int gradwave_units
Definition: KSFoundation.h:671
STATUS ks_error(const char *format,...)
Definition: KSFoundation_common.c:70
int board
Definition: KSFoundation.h:388
int duration
Definition: KSFoundation.h:1135
float ks_syslimits_gradtarget(LOG_GRAD loggrd, int board)
Returns the maximum target amplitude for a board (internal use)
Definition: KSFoundation_common.c:220
float ampscale
Definition: KSFoundation.h:390
KS_GRADRFCTRL gradrf
Definition: KSFoundation.h:1143
int isNaN(float a)
Definition: KSFoundation_common.c:58
KS_WFINSTANCE * wfi
Definition: KSFoundation.h:674
#define KS_DESCRIPTION_LENGTH
Definition: KSFoundation.h:185
int gradnum[3]
Definition: KSFoundation.h:670
KS_SEQLOC loc
Definition: KSFoundation.h:401
KS_DESCRIPTION description
Definition: KSFoundation.h:666
LOG_GRAD loggrd
int is_cleared_on_tgt
Definition: KSFoundation.h:974
STATUS ks_eval_addwavetogradrfctrl(KS_GRADRFCTRL *gradrfctrl, KS_WAVE *wave)
*Internal use*. Adds a wave (KS_WAVE) - if it is not a part of a KS_RF object - to the KS_GRADRFCTRL ...
Definition: KSFoundation_common.c:3798
WF_PULSE ** wfpulse
Definition: KSFoundation.h:673
KS_BASE base
Definition: KSFoundation.h:665
WF_PULSE * wf
Definition: KSFoundation.h:400
Definition: KSFoundation.h:1957
void ks_tgt_reset_gradrfctrl(KS_GRADRFCTRL *gradrfctrl)
Definition: KSFoundation_tgt.c:1481
STATUS ks_wave2iwave(KS_IWAVE iwave, const KS_WAVE *wave, int board)
(Internal use) Conversion of waveform content in a KS_WAVE sequence object to a short int array for u...
Definition: KSFoundation_common.c:1440
KS_DESCRIPTION description
Definition: KSFoundation.h:1141
int ngenerated
Definition: KSFoundation.h:410
int psd_rf_wait
Definition: KSFoundation.h:1957
int res
Definition: KSFoundation.h:667
int duration
Definition: KSFoundation.h:668
Definition: KSFoundation.h:1957

◆ ks_pg_rf()

STATUS ks_pg_rf ( KS_RF rf,
KS_SEQLOC  loc,
KS_SEQ_CONTROL ctrl 
)

Places a KS_RF sequence object on a board at some position in the pulse sequence

This function should be called in the @pg section to place out a KS_RF sequence object that creates an RF pulse in the pulse sequence. The same KS_RF object can be placed out as many times as desired, by only changing the second argument each time (to avoid waveform overlaps). The second argument is a KS_SEQLOC struct with three fields:

  • .board: Ignored. .rfwave will always be placed on RHO. If the resolution of .omegawave is non-zero it will be placed on OMEGA, and correspondingly for .thetawave on THETA
  • .pos: Absolute time in [us] of the start of the waveform
  • .ampscale: A factor that must be in range [-1.0,1.0] that is multiplied with .amp to yield a per-instance (i.e. per-placement) amplitude. For KS_RF objects this will cause the flip angle to be reduced on a per-instance basis. This can be used to generate a variable flip-angle train of RF pulses using a single KS_RF object. An .ampscale of -1.0 is equivalent to adding a 180 degree RF phase shift to the RF pulse

If the rfwave.duration field in the KS_RF object is 0, ks_pg_rf() will return SUCCESS and quietly ignore placing it out. This is a part of the mechanism that setting the .duration field to 0 in cveval() should eliminate its existance in both timing calulations and in the pulse sequence generation in @pg

As ks_pg_rf() internally calls ks_pg_wave() for its KS_WAVEs, double buffering will be used for all hardware waveforms, allowing waveforms to be replaced in run-time (scan()) using ks_scan_wave2hardware()

Regardless of the order placed out in the @pg section the KS_RF object is placed, the instance number of the KS_RF is automatically sorted in time. These abstract, sorted, instance numbers are used by ks_scan_***() functions in scan() to refer to a specific instance of a KS_RF object in the sequence.

GENERAL NOTE: It is a requirement that the function in the @pg section containing all ks_pg_***() calls is also called exactly once in cveval() after the calls to the corresponding ks_eval_***() functions that design the KS_*** sequence objects. Each ks_pg_***() will throw an error if this has not been done.

When ks_pg_rf() is run in cveval() (on HOST), the field .rfpulse.activity that was first set to 0 by ks_eval_rf() is now set to PSD_APS2_ON + PSD_MPS2_ON + PSD_SCAN_ON by ks_pg_rf(). This triggers the RF functions used in ks_eval_gradrflimits() to include this KS_RF object in RF scaling and SAR calculations. Each time ks_pg_rf() is called, the field .rfpulse.num is incremented after being first set to 0 by ks_eval_rf(). Hence, it is crucial that the sequence generating function (c.f. KS_SEQ_CONTROL) containing all ks_pg_***() calls is executed between the ks_eval_rf() setup call and the call to ks_eval_gradrflimits()

Parameters
[in,out]rfPointer to a KS_RF sequence object
[in]locKS_SEQLOC struct to specify when to place the KS_RF
[in,out]ctrlPointer to the KS_SEQ_CONTROL struct corresponding to the sequence module for this KS_RF
Return values
STATUSSUCCESS or FAILURE
2049  {
2050 
2051  /* input validation */
2052  if (rf == NULL) {
2053  return ks_error("%s: First argument is NULL", __FUNCTION__);
2054  }
2055 
2056  /* duration of 0 should not throw an error as this is a sign of that it has not been set up
2057  or has manually been disabled this way */
2058  if (rf->rfwave.duration == 0) {
2059  return SUCCESS;
2060  }
2061  if (rf->rfwave.description == NULL || rf->rfwave.description[0] == 0 || rf->rfwave.description[0] == ' ') {
2062  return ks_error("ks_pg_rf: Invalid description ('%s')", rf->rfwave.description);
2063  }
2064 
2065 #ifdef IPG
2066  /* TGT: Don't place anything on hardware if ctrl->duration = 0.
2067  This is in concert with:
2068  - not performing createseq() in KS_SEQLENGTH if seqctrl.duration = 0
2069  - not playing sequence modules in scan() using ks_scan_playsequence() if seqctrl.duration = 0
2070  */
2071  if (ctrl->duration == 0) {
2072  return ks_error("%s(%s): Refusing to place RF since seqctrl.duration = 0 (indicating `%s` not to be played in scan())", __FUNCTION__, rf->rfwave.description, ctrl->description);
2073  }
2074  /* TGT: make sure (the RF pulses in) the sequence module were a part of the seqcollection when GEReq_eval_rfscaling() was called */
2075  if (ctrl->rfscalingdone == FALSE && ctrl->duration > 0) {
2076  return ks_error("%s(%s): Missing RF scaling for sequence module with non-zero duration. Add `%s` to seqcollection *before* calling GEReq_eval_rfscaling(seqcollection)", __FUNCTION__, rf->rfwave.description, ctrl->description);
2077  }
2078 
2079  if (!ctrl->gradrf.is_cleared_on_tgt) /* Reset gradrfctrl on tgt to remove dead pointers copied from host. */
2080  {
2082  }
2083 
2084 #endif
2085 
2086  loc.pos = RUP_RF(loc.pos);
2087 
2088  RFEnvelopeWaveformGeneratorCheck(rf->rfwave.description, (WF_PROCESSOR) ks_rhoboard);
2089 
2090  if (rf->omegawave.res) {
2091  if (rf->omegawave.duration != rf->rfwave.duration) {
2092  ks_error("ks_pg_rf (%s): The duration of the omega waveform must be equal to the RF duration", rf->rfwave.description);
2093  return FAILURE;
2094  }
2095  }
2096  if (rf->thetawave.res) {
2097  if (rf->thetawave.duration != rf->rfwave.duration) {
2098  ks_error("ks_pg_rf (%s): The duration of the theta waveform must be equal to the RF duration", rf->rfwave.description);
2099  return FAILURE;
2100  }
2101  }
2102 
2103  /* Set ctrl->momentstart if this is an excitation pulse */
2104  if (rf->role == KS_RF_ROLE_EXC) {
2105  ctrl->momentstart = RUP_GRD(loc.pos + rf->start2iso); /* RUP_GRD: make sure it is on a 4us raster too for gradient timing calcs */
2106  }
2107 
2108  /* register this KS_RF for later heating calculations - and do this *before* calls to ks_pg_wave() below (in this function) to avoid double registrations (c.f. ks_eval_addwavetogradrfctrl()).
2109  N.B.: While RF/sar calcs (ks_eval_rfscaling()) are not affected by this, registering the rf->rfwave both as a part of the RF object and as a separate wave in ctrl->gradrf would otherwise lead to problems for
2110  ks_gnuplot_seqmodule(). This would cause the RF pulse to show up twice in the plot / pdf / image.
2111  ks_eval_addwavetogradrfctrl() is called in ks_pg_wave() and checks if this wave also belongs to a parent registered RF pulse in which case the wave will not be registered again */
2112  {
2113  STATUS status = ks_eval_addrftogradrfctrl(&ctrl->gradrf, rf);
2114  if (status != SUCCESS) return status;
2115  }
2116 
2117 #ifdef HOST_TGT
2118 
2119  /* update rfpulse.num counter on host only. We can use this ninst variable as info
2120  for RF heating calculations */
2121  rf->rfpulse.num++;
2122 
2123  /* now that we have shown that we are really going to use this pulse, set its
2124  activity flag to SCAN, APS2, MPS2 */
2125  rf->rfpulse.activity = PSD_APS2_ON + PSD_MPS2_ON + PSD_SCAN_ON;
2126 
2127 #else
2128 
2129  if (areSame(rf->rfpulse.num, 0)) {
2130  ks_error("ks_pg_rf @tgt(%s): pulsegen was never run on HOST (rf->rfpulse.num = 0)", rf->rfwave.description);
2131  return FAILURE;
2132  }
2133 
2134  if (rf->rfpulse.activity == 0) {
2135  ks_error("ks_pg_rf @tgt(%s): RF pulse with zero activity", rf->rfwave.description);
2136  return FAILURE;
2137  }
2138 
2139  /* The values in rf->rfwave.waveform[] are relative (unit-less). The actual amplitude of the RF wave
2140  is only controlled by rf->amp (which in turn is set by GE's setScale() and indirectly by peakB1()).
2141  Prepare RF waveform with correct amplitude on TGT side that can be used with ks_pg_wave() to give the right
2142  RF amplitude, after we have done RF scaling on HOST and hence have a final value for rf->amp.
2143  The units of this RF waveform, before and after calling ks_wave_multiplywal(), are relative (not [G])
2144  due to GE's RF scaling process involving e.g. xmtaddScan.
2145  Look in GERequired.e:GEReq_eval_gradrflimits() for more info on how peakB1() and setScale() are used. */
2146 
2147  ks_wave_multiplyval(&rf->rfwave, rf->amp / ks_wave_absmax(&rf->rfwave));
2148 
2149 #endif
2150 
2151 
2152 
2153  /* place the RF wave */
2154  loc.board = RHO;
2155  if (ks_pg_wave(&rf->rfwave, loc, ctrl) == FAILURE)
2156  return FAILURE;
2157 
2158 #ifdef IPG
2159  /* add SSP RF bits that handles the frequency control and blanking/unblanking the receiver */
2160  addrfbits(&rf->rfwave.wfpulse[RHO][KS_WF_MAIN], 0, loc.pos + psd_rf_wait, rf->rfwave.duration);
2161  /* set RF frequency offset */
2162  setfrequency((int)(rf->cf_offset / TARDIS_FREQ_RES), &rf->rfwave.wfpulse[RHO][KS_WF_MAIN], INSTRALL);
2163 #endif
2164 
2165  /* set ampscale to 1. If you want to scale the omega or theata board, do it seperatly */
2166  loc.ampscale = 1;
2167 
2168  /* place the OMEGA wave */
2169  loc.board = OMEGA;
2170  if (rf->omegawave.res) {
2171  if (ks_pg_wave(&rf->omegawave, loc, ctrl) == FAILURE)
2172  return FAILURE;
2173  }
2174 
2175 #ifdef IPG
2176  if (rf->omegawave.res) {
2177  /* add SSP RF bits that handles the frequency control and blanking/unblanking the receiver */
2178  addrfbits(&rf->omegawave.wfpulse[OMEGA][KS_WF_MAIN], 0, loc.pos + psd_rf_wait, rf->omegawave.duration);
2179  }
2180 #endif
2181 
2182  /* place the THETA wave */
2183  loc.board = THETA;
2184  if (rf->thetawave.res) {
2185  if (ks_pg_wave(&rf->thetawave, loc, ctrl) == FAILURE)
2186  return FAILURE;
2187  }
2188 
2189 #ifdef IPG
2190  if (rf->thetawave.res) {
2191  /* add SSP RF bits that handles the frequency control and blanking/unblanking the receiver */
2192  addrfbits(&rf->thetawave.wfpulse[THETA][KS_WF_MAIN], 0, loc.pos + psd_rf_wait, rf->thetawave.duration);
2193  }
2194 #endif
2195 
2196  return SUCCESS;
2197 
2198 } /* ks_pg_rf */
STATUS ks_pg_wave(KS_WAVE *wave, KS_SEQLOC loc, KS_SEQ_CONTROL *ctrl)
Places a KS_WAVE sequence object on a board at some position in the pulse sequence
Definition: KSFoundation_common.c:1849
float cf_offset
Definition: KSFoundation.h:942
int start2iso
Definition: KSFoundation.h:944
#define areSame(a, b)
Definition: KSFoundation.h:119
float ks_wave_absmax(const KS_WAVE *wave)
Returns the maximum absolute value in a KS_WAVE sequence object
Definition: KSFoundation_common.c:1224
int pos
Definition: KSFoundation.h:389
int role
Definition: KSFoundation.h:939
int ks_rhoboard
Definition: KSFoundation_common.c:42
RF_PULSE rfpulse
Definition: KSFoundation.h:948
int rfscalingdone
Definition: KSFoundation.h:1139
int momentstart
Definition: KSFoundation.h:1136
STATUS ks_error(const char *format,...)
Definition: KSFoundation_common.c:70
int board
Definition: KSFoundation.h:388
int duration
Definition: KSFoundation.h:1135
Definition: KSFoundation.h:1953
float ampscale
Definition: KSFoundation.h:390
KS_GRADRFCTRL gradrf
Definition: KSFoundation.h:1143
KS_WAVE thetawave
Definition: KSFoundation.h:951
KS_DESCRIPTION description
Definition: KSFoundation.h:666
int is_cleared_on_tgt
Definition: KSFoundation.h:974
WF_PULSE ** wfpulse
Definition: KSFoundation.h:673
KS_WAVE rfwave
Definition: KSFoundation.h:949
float amp
Definition: KSFoundation.h:943
Definition: KSFoundation.h:1957
void ks_tgt_reset_gradrfctrl(KS_GRADRFCTRL *gradrfctrl)
Definition: KSFoundation_tgt.c:1481
STATUS ks_eval_addrftogradrfctrl(KS_GRADRFCTRL *gradrfctrl, KS_RF *rf)
*Internal use*. Adds an RF pulse to the KS_GRADRFCTRL struct for later SAR & RF power calculations in...
Definition: KSFoundation_common.c:3832
KS_WAVE omegawave
Definition: KSFoundation.h:950
KS_DESCRIPTION description
Definition: KSFoundation.h:1141
void ks_wave_multiplyval(KS_WAVE *wave, float val)
In-place scalar multiplication of a KS_WAVE
Definition: KSFoundation_common.c:1398
int psd_rf_wait
int res
Definition: KSFoundation.h:667
int duration
Definition: KSFoundation.h:668

◆ ks_pg_selrf()

STATUS ks_pg_selrf ( KS_SELRF selrf,
KS_SEQLOC  loc,
KS_SEQ_CONTROL ctrl 
)

Places a KS_SELRF sequence object on a gradient (and RF) board at some position in the pulse sequence

This function should be called in the @pg section to place out a KS_SELRF sequence object that creates an RF pulse with associated trapezoids in the pulse sequence. The same KS_SELRF object can be placed out as many times as desired, by only changing the second argument each time (to avoid waveform overlaps). The second argument is a KS_SEQLOC struct with three fields:

  • .board: Choose between XGRAD, YGRAD, ZGRAD for the board on which .pregrad, .grad (or .gradwave) and .postgrad should be placed
  • .pos: Absolute time in [us] of the start of the .pregrad attack ramp. If .pregrad has zero duration, .pos will in effect refer to the start of the .grad attack ramp
  • .ampscale: A factor that must be in range [-1.0,1.0] that is multiplied with rf.rfwave.amp to yield a per-instance (i.e. per-placement) amplitude. For KS_SELRF objects the .ampscale value will be passed on its internal call to ks_pg_rf(), but not to its internal ks_pg_trap() calls. Hence .ampscale will have the same effect for ks_pg_selrf() as for ks_pg_rf(), with no per-instance control of the amplitude of the gradients involved. A reduced .ampscale will cause the flip angle to be reduced on a per-instance basis. This can be used to generate a variable flip-angle train of selective RF pulses using a single KS_SELRF object. An .ampscale of -1.0 is equivalent to adding a 180 degree RF phase shift to the RF pulse

If the .gradwave field has a non-zero resolution, ks_pg_selrf() will place out the .gradwave (KS_WAVE) instead of the .grad (KS_TRAP) during the time the RF pulse is played out. An error is thrown if .gradwave.duration (if .gradwave.res > 0) is not equal to the .rf.rfwave.duration. If .pregrad or .postgrad has zero duration, they will not be placed out. The existance of non-zero .pregrad and .postgrad is determined by ks_eval_selrf() based on .rf.role

As ks_pg_selrf() internally calls ks_pg_wave() for its KS_WAVEs, hardware double buffering will be used for all waveforms, allowing waveforms to be replaced in run-time (scan()) using ks_scan_wave2hardware()

Regardless of the order placed out in the @pg section the KS_SELRF object is placed, the instance number of the KS_SELRF is automatically sorted in time. These abstract, sorted, instance numbers are used by ks_scan_***() functions in scan() to refer to a specific instance of a KS_SELRF object in the sequence.

GENERAL NOTE: It is a requirement that the function in the @pg section containing all ks_pg_***() calls is also called exactly once in cveval() after the calls to the corresponding ks_eval_***() functions that design the KS_*** sequence objects. Each ks_pg_***() will throw an error if this has not been done.

Parameters
[in,out]selrfPointer to a KS_SELRF sequence object
[in]locKS_SEQLOC struct to specify when and where to place the KS_SELRF
[in,out]ctrlPointer to the KS_SEQ_CONTROL struct corresponding to the sequence module for this KS_SELRF
Return values
STATUSSUCCESS or FAILURE
2204  {
2205  KS_SEQLOC rfloc;
2206  KS_SEQLOC gradloc;
2207 
2208  if (loc.board != XGRAD && loc.board != YGRAD && loc.board != ZGRAD) {
2209  return ks_error("%s(%s): loc.board (2nd arg) must be a gradient board", __FUNCTION__, selrf->rf.rfwave.description);
2210  }
2211 
2212  gradloc = loc; gradloc.ampscale = 1.0;
2213  rfloc = loc; rfloc.board = RHO;
2214 
2215  /* RF starts after the preselgrad (if present) */
2216  rfloc.pos += selrf->pregrad.duration;
2217 
2218  /* delay the RF pulse start by the ramptime of the slice sel gradient
2219  when .gradwave is used, .grad.ramptime will be correctly zero */
2220  rfloc.pos += selrf->grad.ramptime;
2221 
2222 
2223  /************* generate the RF (incl. optional THETA & OMEGA) *************/
2224 
2225  if (ks_pg_rf(&selrf->rf, rfloc, ctrl) == FAILURE)
2226  return FAILURE;
2227 
2228  /************************ place gradients *********************************/
2229 
2230  gradloc.ampscale = (selrf->rf.role != KS_RF_ROLE_EXC) ? loc.ampscale2 : 1.0;
2231  /* Pre-slice sel gradient. Roles: e.g. left crusher or dephasing gradient in bSSFP */
2232  if (ks_pg_trap(&selrf->pregrad, gradloc, ctrl) == FAILURE)
2233  return FAILURE;
2234  gradloc.pos += selrf->pregrad.duration;
2235 
2236 
2237 
2238  /* slice select gradient */
2239  gradloc.ampscale = 1.0;
2240 
2241  if (selrf->gradwave.res) {
2242  /* use gradwave (not trapezoid) */
2243 
2244  if (selrf->rf.rfwave.duration != (selrf->gradwave.duration) || (selrf->gradwave.res * GRAD_UPDATE_TIME != selrf->gradwave.duration)) {
2245  return ks_error("%s(%s): The duration of 'gradwave' (%d [us]) must equal the RF duration (%d [us])", __FUNCTION__,
2246  selrf->rf.rfwave.description, selrf->gradwave.duration, selrf->rf.rfwave.duration);
2247  }
2248 
2249  if (ks_pg_wave(&selrf->gradwave, gradloc, ctrl) == FAILURE)
2250  return FAILURE;
2251 
2252  gradloc.pos += selrf->gradwave.duration;
2253 
2254  } else {
2255  /* use trapezoid (not gradwave) */
2256 
2257  if (selrf->grad.plateautime != selrf->rf.rfwave.duration) {
2258  return ks_error("%s(%s): Plateau time of slice select (%d) != RF duration (%d)", __FUNCTION__, selrf->rf.rfwave.description, selrf->grad.plateautime, selrf->rf.rfwave.duration);
2259  } else {
2260  if (ks_pg_trap(&selrf->grad, gradloc, ctrl) == FAILURE)
2261  return FAILURE;
2262 
2263  gradloc.pos += selrf->grad.duration;
2264  }
2265 
2266  } /* slice select */
2267 
2268 
2269  /* post-slice sel gradient. Roles: e.g. right crusher or refocusing gradient */
2270  gradloc.ampscale = (selrf->rf.role != KS_RF_ROLE_EXC) ? loc.ampscale2 : 1.0;
2271  if (ks_pg_trap(&selrf->postgrad, gradloc, ctrl) == FAILURE)
2272  return FAILURE;
2273  gradloc.pos += selrf->postgrad.duration;
2274 
2275 
2276  return SUCCESS;
2277 
2278 } /* ks_pg_selrf */
float ampscale2
Definition: KSFoundation.h:391
int plateautime
Definition: KSFoundation.h:584
KS_TRAP grad
Definition: KSFoundation.h:1491
STATUS ks_pg_wave(KS_WAVE *wave, KS_SEQLOC loc, KS_SEQ_CONTROL *ctrl)
Places a KS_WAVE sequence object on a board at some position in the pulse sequence
Definition: KSFoundation_common.c:1849
int pos
Definition: KSFoundation.h:389
int role
Definition: KSFoundation.h:939
KS_WAVE gradwave
Definition: KSFoundation.h:1493
STATUS ks_error(const char *format,...)
Definition: KSFoundation_common.c:70
int board
Definition: KSFoundation.h:388
Definition: KSFoundation.h:1953
float ampscale
Definition: KSFoundation.h:390
KS_TRAP pregrad
Definition: KSFoundation.h:1490
STATUS ks_pg_rf(KS_RF *rf, KS_SEQLOC loc, KS_SEQ_CONTROL *ctrl)
Places a KS_RF sequence object on a board at some position in the pulse sequence
Definition: KSFoundation_common.c:2049
KS_RF rf
Definition: KSFoundation.h:1485
KS_DESCRIPTION description
Definition: KSFoundation.h:666
KS_WAVE rfwave
Definition: KSFoundation.h:949
typedef struct used as argument to ks_pg_*** functions to control where and when to place a sequence ...
Definition: KSFoundation.h:387
KS_TRAP postgrad
Definition: KSFoundation.h:1492
int duration
Definition: KSFoundation.h:585
STATUS ks_pg_trap(KS_TRAP *trap, KS_SEQLOC loc, KS_SEQ_CONTROL *ctrl)
Places a KS_TRAP sequence object on a board at some position in the pulse sequence
Definition: KSFoundation_common.c:1475
int res
Definition: KSFoundation.h:667
int ramptime
Definition: KSFoundation.h:583
int duration
Definition: KSFoundation.h:668

◆ ks_pg_wait()

STATUS ks_pg_wait ( KS_WAIT wait,
KS_SEQLOC  loc,
KS_SEQ_CONTROL ctrl 
)

Places a KS_WAIT sequence object on all boards at some position in the pulse sequence

This function should be called in the @pg section to place out a KS_WAIT sequence object that creates a wait pulse. The same KS_WAIT object can be placed out as many times as desired, by only changing the second argument each time (to avoid waveform overlaps). The second argument is a KS_SEQLOC struct with three fields:

  • .board: Ignored
  • .pos: Absolute time in [us] of the start of the wait pulse
  • .ampscale: Ignored

This function will insert a deadtime of duration equal to the .duration field in the KS_WAIT object at position .pos. This delay can be changed in run-time by calling ks_scan_wait().

GENERAL NOTE: It is a requirement that the function in the @pg section containing all ks_pg_***() calls is also called exactly once in cveval() after the calls to the corresponding ks_eval_***() functions that design the KS_*** sequence objects. Each ks_pg_***() will throw an error if this has not been done.

Parameters
[in,out]waitPointer to a KS_WAIT sequence object
[in]locKS_SEQLOC struct to specify when and where to place the KS_WAIT
[in,out]ctrlPointer to the KS_SEQ_CONTROL struct corresponding to the sequence module for this KS_WAIT
Return values
STATUSSUCCESS or FAILURE
2287  {
2288  int i;
2289  const int first = loc.board == KS_ALL ? XGRAD : loc.board;
2290  const int last = loc.board == KS_ALL ? SSP : loc.board;
2291 
2292  /* checking for NULL pointers */
2293  if (wait == NULL) {
2294  return ks_error("ks_pg_wait: First argument is NULL");
2295  }
2296 
2297  /* duration of 0 should not throw an error as this is a sign of that it has not been set up
2298  or has manually been disabled this way */
2299  if (wait->duration == 0) {
2300  return SUCCESS;
2301  }
2302 
2303  if (wait->description == NULL || wait->description[0] == 0 || wait->description[0] == ' ') {
2304  return ks_error("ks_pg_wait: Invalid description ('%s')", wait->description);
2305  }
2306 
2307  /* disallow EPI dual boards */
2308  if (loc.board >= KS_FREQX_PHASEY && loc.board <= KS_XYZ) {
2309  return ks_error("ks_pg_wait(%s): Invalid board selection: %d", wait->description, loc.board);
2310  }
2311 
2312  loc.pos = RUP_GRD(loc.pos);
2313 
2314 
2315 #ifdef IPG
2316  {
2317 
2318  /* TGT: Don't place anything on hardware if ctrl->duration = 0.
2319  This is in concert with:
2320  - not performing createseq() in KS_SEQLENGTH if seqctrl.duration = 0
2321  - not playing sequence modules in scan() using ks_scan_playsequence() if seqctrl.duration = 0
2322  */
2323  if (ctrl->duration == 0) {
2324  ks_dbg("%s(%s): Refusing to place wait pulse since seqctrl.duration = 0 (indicating `%s` not to be played in scan())", __FUNCTION__, wait->description, ctrl->description);
2325  return SUCCESS;
2326  }
2327 
2328  if (wait->base.ngenerated >= wait->base.ninst) {
2329  return ks_error("ks_pg_wait @tgt(%s): the number of instances generated on the target exceed the number of ones generated on the host", wait->description);
2330  }
2331 
2332  if (wait->wfpulse == NULL) {
2333  WF_PULSE wfInit = INITPULSE;
2334 
2335  const char boardc[9] = "xyzrrtos";
2336  char tmpstr[KS_DESCRIPTION_LENGTH + 4];
2337  tmpstr[KS_DESCRIPTION_LENGTH + 4 - 1] = 0;
2338 
2339  wait->wfpulse = (WF_PULSE *) AllocNode(8 * sizeof(WF_PULSE));
2340  for (i = XGRAD; i <= SSP; i++) {
2341  if (i != RHO2){
2342  /* init pulse structure */
2343  wait->wfpulse[i] = wfInit;
2344 
2345  /* Name for WF_PULSEs */
2346  sprintf(tmpstr, "%s.w%c", wait->description, boardc[i]); pulsename(&(wait->wfpulse[i]), tmpstr);
2347  createconst(&(wait->wfpulse[i]), (WF_PROCESSOR) i, wait->duration, 0);
2348 
2349  } /* per board */
2350  }
2351 
2352  } /* wfpulse == NULL */
2353 
2354  if (!wait->wfi) {
2355  wait->wfi = (KS_WFINSTANCE *) AllocNode(wait->base.ninst * sizeof(KS_WAIT));
2356  }
2357 
2358  for (i = first; i <= last; ++i) {
2359  if (i == KS_RHO2) continue; /* skip RHO2 */
2360 
2361  loc.board = i;
2362  int isrf = loc.board == RHO || loc.board == RHO2 || loc.board == THETA || loc.board == OMEGA;
2363  int isssp = loc.board == SSP;
2364 
2365  createinstr(&(wait->wfpulse[i]), loc.pos + (isrf * psd_rf_wait) + (isssp * psd_grd_wait), wait->duration, 0);
2366  wait->wfi[wait->base.ngenerated].boardinstance = wait->wfpulse[i].ninsts - 1; /* zero based */
2367  wait->wfi[wait->base.ngenerated].wf = &(wait->wfpulse[i]);
2368  wait->wfi[wait->base.ngenerated].loc = loc;
2369  wait->base.ngenerated++;
2370  }
2371 
2372  /* sort after the last instance is generated */
2373  if (wait->base.ngenerated == wait->base.ninst) {
2374  ks_sort_wfi_by_timeboard(wait->wfi, wait->base.ninst);
2375  }
2376  }
2377 #else
2378  {
2379  for (i = first; i <= last; ++i) {
2380  if (i == KS_RHO2) continue; /* skip RHO2 */
2381  loc.board = i;
2382  wait->locs[wait->base.ninst] = loc;
2383  wait->base.ninst++;
2384  }
2385  }
2386 #endif
2387 
2388  return SUCCESS;
2389 
2390 } /* ks_pg_wait */
void ks_sort_wfi_by_timeboard(KS_WFINSTANCE *a, int nitems)
Sort WF_INSTANCEs in time, then board
Definition: KSFoundation_common.c:3283
Definition: KSFoundation.h:1919
int boardinstance
Definition: KSFoundation.h:399
int pos
Definition: KSFoundation.h:389
Definition: KSFoundation.h:1915
(Internal use) Structure being a part of various sequence objects to sort them in time across boards...
Definition: KSFoundation.h:398
int ninst
Definition: KSFoundation.h:409
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
STATUS ks_error(const char *format,...)
Definition: KSFoundation_common.c:70
STATUS ks_dbg(const char *format,...)
Definition: KSFoundation_common.c:134
int board
Definition: KSFoundation.h:388
Definition: KSFoundation.h:1925
int duration
Definition: KSFoundation.h:1135
KS_DESCRIPTION description
Definition: KSFoundation.h:458
Definition: KSFoundation.h:1926
#define KS_DESCRIPTION_LENGTH
Definition: KSFoundation.h:185
KS_SEQLOC locs[KS_MAXINSTANCES]
Definition: KSFoundation.h:460
KS_SEQLOC loc
Definition: KSFoundation.h:401
KS_BASE base
Definition: KSFoundation.h:457
int duration
Definition: KSFoundation.h:459
KS_WFINSTANCE * wfi
Definition: KSFoundation.h:462
WF_PULSE * wf
Definition: KSFoundation.h:400
int psd_grd_wait
WF_PULSE * wfpulse
Definition: KSFoundation.h:461
KS_DESCRIPTION description
Definition: KSFoundation.h:1141
int ngenerated
Definition: KSFoundation.h:410
int psd_rf_wait

◆ ks_pg_isirot()

STATUS ks_pg_isirot ( KS_ISIROT isirot,
SCAN_INFO  scan_info,
int  pos,
void(*)()  rotfun,
KS_SEQ_CONTROL ctrl 
)

Places out an intersequence interrupt at a specific time in the sequence module

This function connects the execution of a psd-specific rotation function in realtime when time passes this point in the sequence. This is done by two KS_WAIT pulses (being part of KS_ISIROT) on the SSP board.

Example of use in a myseqmodule_pg() function of the sequence module (in pulsegen()):

// add some padding time before the SSP pulses (not well tested how much of KS_ISI_predelay that is necessary)
mypos += RUP_GRD(KS_ISI_predelay);
status = ks_pg_isirot(&myisirot, myscaninfo, mypos, myisirotatefun, myseqctrl);
if (status != SUCCESS) return status;
// add some padding time after the SSP pulses (not well tested how much of KS_ISI_postdelay that is necessary)
mypos += RUP_GRD(myisirot.duration + KS_ISI_postdelay);

where the psd-specific rotation function is declared as:

// Intra sequence interrupts placed as WAIT pulses on the SSP board may be used to call for an immediate
// rotation of the logical coordinate system in real time as these WAIT pulses are played out, as
// opposed to during the SSI time for the sequence module.
// The WAIT pulse is assigned a control number (isi number) that is then connected to this void function
// using `isivector()` in ks_pg_isirot(). No input arguments can be passed to this function. Instead the
// rotation information specific to the sequence module is provided in `myscaninfo.oprot`. The acutal
// rotation work, incl. keeping track of number of isi pulses played out is done by ks_scan_isirotate().
void myisirotatefun() {
#ifdef IPG
ks_scan_isirotate(&myisirot);
#endif
}

Note that the only difference between the psd-specific myisirotatefun() and the general ks_scan_isirotate() is that myisirotatefun() has no input arguments. Input arguments cannot be used as it is called from an interrupt routine, not from some other parent function.

Care must be taken when mixing the use of ks_scan_rotate() (executing during the SSI time) and these ISI interrupts, so that they are executed in the correct order. One way is to consistently avoid the use of ks_scan_rotate() and instead return to the prescribed slice rotation by calling ks_pg_isirot() again, last in the same myseqmodule_pg() function, this time using the prescribed scan_info struct:

status = ks_pg_isirot(&myisirot, ks_scan_info[0], mypos, myisirotatefun, myseqctrl);
if (status != SUCCESS) return status;
Parameters
[in,out]isirotPointer to a KS_ISIROT sequence object
[in]scan_infoSCAN_INFO struct holding an .oprot matrix to be used for real-time rotation
[in]posAbsolute position in [us] in the pulse sequence when this rotation should occur
[in]rotfunFunction pointer to the psd-specific rotation function
[in,out]ctrlPointer to the KS_SEQ_CONTROL struct corresponding to the sequence module
Return values
STATUSSUCCESS or FAILURE
2394  {
2395  KS_SEQLOC tmploc = KS_INIT_SEQLOC;
2396 
2397  if (! isirot->duration) {
2398  /* must have called ks_eval_isirot() first */
2399  return SUCCESS;
2400  }
2401 
2402  if (rotfun == NULL)
2403  return ks_error("%s: 4th arg (rotfun) cannot be NULL", __FUNCTION__);
2404 
2405 
2406  /* Set the ctrl_word to binary match the isi vector interupt number */
2407  /* /--------------------------------------------------------------\ */
2408  /* | interupt number | ctrl_word | */
2409  /* |--------------------------------------------------------------| */
2410  /* | 4 | PSD_ISI2_BIT | */
2411  /* | 5 | PSD_ISI0_BIT + PSD_ISI2_BIT | */
2412  /* | 6 | PSD_ISI1_BIT + PSD_ISI2_BIT | */
2413  /* | 7 | PSD_ISI0_BIT + PSD_ISI1_BIT + PSD_ISI2_BIT | */
2414  /* \--------------------------------------------------------------/ */
2415 
2416  tmploc.board = SSP;
2417  tmploc.pos = RUP_GRD(pos);
2418 
2419  if (ks_pg_wait(&isirot->waitfun, tmploc, ctrl) == FAILURE)
2420  return FAILURE;
2421 
2422  tmploc.pos += RUP_GRD(isirot->waitfun.duration);
2423 
2424  if (ks_pg_wait(&isirot->waitrot, tmploc, ctrl) == FAILURE)
2425  return FAILURE;
2426 
2427 
2428 #ifdef IPG
2429  {
2430  long ctrl_word = 0;
2431  int placementindx = isirot->waitfun.base.ngenerated - 1;
2432 
2433  /* SSP ISI number */
2434  getctrl(&ctrl_word, &isirot->waitfun.wfpulse[SSP], isirot->waitfun.wfpulse[SSP].ninsts - 1);
2435 
2436  if (isirot->isinumber == 4)
2437  ctrl_word = ctrl_word | PSD_ISI2_BIT;
2438  else if (isirot->isinumber == 5)
2439  ctrl_word = ctrl_word | PSD_ISI0_BIT | PSD_ISI2_BIT;
2440  else if (isirot->isinumber == 6)
2441  ctrl_word = ctrl_word | PSD_ISI1_BIT | PSD_ISI2_BIT;
2442  else if (isirot->isinumber == 7)
2443  ctrl_word = ctrl_word | PSD_ISI0_BIT | PSD_ISI1_BIT | PSD_ISI2_BIT;
2444 
2445  setctrl(ctrl_word, &isirot->waitfun.wfpulse[SSP], isirot->waitfun.wfpulse[SSP].ninsts - 1);
2446 
2447  /* SSP Rotation */
2448  getctrl(&ctrl_word, &isirot->waitrot.wfpulse[SSP], isirot->waitrot.wfpulse[SSP].ninsts - 1);
2449  ctrl_word |= PSD_MTX_UPDT;
2450  setctrl(ctrl_word, &isirot->waitrot.wfpulse[SSP], isirot->waitrot.wfpulse[SSP].ninsts - 1);
2451 
2452  /* Allocate memory for scan info array */
2453  if (isirot->scan_info == NULL) {
2454  isirot->scan_info = (SCAN_INFO *) AllocNode(isirot->numinstances * sizeof(SCAN_INFO));
2455  }
2456 
2457  /* Store the scan info for current ISI, where scan_info.oprot contains the rotation matrix */
2458  isirot->scan_info[placementindx] = scan_info;
2459 
2460  /* Reset num ISI and counter */
2461  isirot->counter = 0; /* make sure it is zero before scan starts */
2462 
2463  /* Connect ths isi number with the function pointer */
2464  isivector((short) isirot->isinumber, rotfun, (short) FALSE);
2465 
2466  }
2467 #else
2468 
2469  if (0) { /* make sure the WAIT pulses have been placed out in temporal order */
2470  int i;
2471  int lastpos = -1;
2472  WF_INSTR_HDR *instr = isirot->waitfun.wfpulse[SSP].inst_hdr_tail;
2473  for (i = 0; i < (isirot->waitfun.base.ninst - 1); i++) {
2474  if (lastpos > instr->start) {
2475  return ks_error("%s: Must place the ISI pulses in temporal order", __FUNCTION__);
2476  } else {
2477  lastpos = instr->start;
2478  instr = instr->next;
2479  }
2480  }
2481  }
2482 
2483  /* copy for convenience */
2484  isirot->numinstances = isirot->waitfun.base.ninst;
2485 
2486 #endif
2487 
2488  return SUCCESS;
2489 
2490 } /* ks_pg_isirot() */
int isinumber
Definition: KSFoundation.h:473
SCAN_INFO * scan_info
Definition: KSFoundation.h:472
int pos
Definition: KSFoundation.h:389
int ninst
Definition: KSFoundation.h:409
STATUS ks_error(const char *format,...)
Definition: KSFoundation_common.c:70
int board
Definition: KSFoundation.h:388
KS_WAIT waitfun
Definition: KSFoundation.h:470
KS_BASE base
Definition: KSFoundation.h:457
int counter
Definition: KSFoundation.h:475
int duration
Definition: KSFoundation.h:459
typedef struct used as argument to ks_pg_*** functions to control where and when to place a sequence ...
Definition: KSFoundation.h:387
int numinstances
Definition: KSFoundation.h:476
KS_WAIT waitrot
Definition: KSFoundation.h:471
STATUS ks_pg_wait(KS_WAIT *wait, KS_SEQLOC loc, KS_SEQ_CONTROL *ctrl)
Places a KS_WAIT sequence object on all boards at some position in the pulse sequence
Definition: KSFoundation_common.c:2287
#define KS_INIT_SEQLOC
Definition: KSFoundation.h:208
WF_PULSE * wfpulse
Definition: KSFoundation.h:461
int duration
Definition: KSFoundation.h:474
int ngenerated
Definition: KSFoundation.h:410

◆ ks_pg_epi_dephasers()

STATUS ks_pg_epi_dephasers ( KS_EPI epi,
KS_SEQLOC  loc,
KS_SEQ_CONTROL ctrl 
)

Places out the dephasing gradients of a KS_EPI object (used by ks_pg_epi())

This function is called internally by ks_pg_epi() to place out the dephasing gradients of a KS_EPI object. See ks_pg_epi() for more details

For advanced usage of KS_EPI, it is possible to call ks_pg_epi_dephaser(), ks_pg_epi_echo() and ks_pg_epi_rephasers() separately instead of the single call to ks_pg_epi(). This allows the dephasing and rephasing gradients to be detached from the core EPI readout part

Parameters
[in,out]epiPointer to a KS_EPI sequence object
[in]locKS_SEQLOC struct to specify when and where to place the dephasers of the KS_EPI object
[in]ctrlPointer to the KS_SEQ_CONTROL struct corresponding to the sequence module for this KS_EPI
Return values
STATUSSUCCESS or FAILURE
2496  {
2497  int readaxis, blipaxis, sliceaxis;
2498  KS_SEQLOC tmploc = KS_INIT_SEQLOC;
2499  int maxduration;
2500 
2501  if (!epi) { return FAILURE; }
2502 
2503  /* loc.ampscale may only be +1 or -1 and will control the read polarity.
2504  The blip polarity is controlled in run time with ks_scan_epi_shotcontrol() */
2505  if (!areSame(loc.ampscale, 1.0) && !areSame(loc.ampscale, -1.0)) {
2506  return ks_error("ks_pg_epi_dephasers: loc.ampscale must be +1 or -1 (controls read polarity)");
2507  }
2508 
2509  loc.pos = RDN_GRD(loc.pos);
2510 
2511  switch (loc.board) {
2512  case KS_FREQX_PHASEY:
2513  readaxis = XGRAD; blipaxis = YGRAD; sliceaxis = ZGRAD; break;
2514  case KS_FREQY_PHASEX:
2515  readaxis = YGRAD; blipaxis = XGRAD; sliceaxis = ZGRAD; break;
2516  case KS_FREQX_PHASEZ:
2517  readaxis = XGRAD; blipaxis = ZGRAD; sliceaxis = YGRAD; break;
2518  case KS_FREQZ_PHASEX:
2519  readaxis = ZGRAD; blipaxis = XGRAD; sliceaxis = YGRAD; break;
2520  case KS_FREQY_PHASEZ:
2521  readaxis = YGRAD; blipaxis = ZGRAD; sliceaxis = XGRAD; break;
2522  case KS_FREQZ_PHASEY:
2523  readaxis = ZGRAD; blipaxis = YGRAD; sliceaxis = XGRAD; break;
2524  default:
2525  ks_error("ks_pg_epi_dephasers: loc.board is not valid: %d", loc.board);
2526  return FAILURE;
2527  }
2528 
2529  /* allocate time corresponding to the longest of the three, but push the dephasers to the right below */
2530  maxduration = IMax(3, epi->readphaser.duration, epi->blipphaser.grad.duration, epi->zphaser.grad.duration);
2531 
2532  /* read phaser (same KS_TRAP for DE/REphasing => 2 instances per EPI) */
2533  if (epi->readphaser.duration > 0) {
2534  tmploc.board = readaxis;
2535  tmploc.ampscale = loc.ampscale;
2536  tmploc.pos = loc.pos + maxduration - epi->readphaser.duration;
2537  if (ks_pg_trap(&epi->readphaser, tmploc, ctrl) == FAILURE)
2538  return FAILURE;
2539  }
2540 
2541  /* blip phaser (same KS_PHASER for DE/REphasing => 2 instances per EPI) */
2542  if (epi->blipphaser.grad.duration > 0) {
2543  tmploc.board = blipaxis;
2544  tmploc.ampscale = 1.0;
2545  tmploc.pos = loc.pos + maxduration - epi->blipphaser.grad.duration;
2546  if (ks_pg_phaser(&epi->blipphaser, tmploc, ctrl) == FAILURE)
2547  return FAILURE;
2548  }
2549 
2550  /* slice phaser (same KS_PHASER for DE/REphasing => 2 instances per EPI) */
2551  if (epi->zphaser.grad.duration > 0) {
2552  tmploc.board = sliceaxis;
2553  tmploc.ampscale = 1.0;
2554  tmploc.pos = loc.pos + maxduration - epi->zphaser.grad.duration;
2555  if (ks_pg_phaser(&epi->zphaser, tmploc, ctrl) == FAILURE)
2556  return FAILURE;
2557  }
2558 
2559  return SUCCESS;
2560 } /* ks_pg_epi_dephasers */
STATUS ks_pg_phaser(KS_PHASER *phaser, KS_SEQLOC loc, KS_SEQ_CONTROL *ctrl)
Places a KS_PHASER sequence object on a board at some position in the pulse sequence
Definition: KSFoundation_common.c:1669
Definition: KSFoundation.h:1919
#define areSame(a, b)
Definition: KSFoundation.h:119
KS_TRAP grad
Definition: KSFoundation.h:1676
int pos
Definition: KSFoundation.h:389
Definition: KSFoundation.h:1921
KS_PHASER blipphaser
Definition: KSFoundation.h:1840
STATUS ks_error(const char *format,...)
Definition: KSFoundation_common.c:70
int board
Definition: KSFoundation.h:388
KS_PHASER zphaser
Definition: KSFoundation.h:1841
Definition: KSFoundation.h:1924
float ampscale
Definition: KSFoundation.h:390
Definition: KSFoundation.h:1922
Definition: KSFoundation.h:1920
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:1923
KS_TRAP readphaser
Definition: KSFoundation.h:1838
#define KS_INIT_SEQLOC
Definition: KSFoundation.h:208
int duration
Definition: KSFoundation.h:585
STATUS ks_pg_trap(KS_TRAP *trap, KS_SEQLOC loc, KS_SEQ_CONTROL *ctrl)
Places a KS_TRAP sequence object on a board at some position in the pulse sequence
Definition: KSFoundation_common.c:1475

◆ ks_pg_epi_rephasers()

STATUS ks_pg_epi_rephasers ( KS_EPI epi,
KS_SEQLOC  loc,
KS_SEQ_CONTROL ctrl 
)

Places out the rephasing gradients of a KS_EPI object (used by ks_pg_epi())

This function is called internally by ks_pg_epi() to place out the rephasing gradients of a KS_EPI object. See ks_pg_epi() for more details

For advanced usage of KS_EPI, it is possible to call ks_pg_epi_dephaser(), ks_pg_epi_echo() and ks_pg_epi_rephasers() separately instead of the single call to ks_pg_epi(). This allows the dephasing and rephasing gradients to be detached from the core EPI readout part

Parameters
[in,out]epiPointer to a KS_EPI sequence object
[in]locKS_SEQLOC struct to specify when to place the rephasers of the KS_EPI object
[in]ctrlPointer to the KS_SEQ_CONTROL struct corresponding to the sequence module for this KS_EPI
Return values
STATUSSUCCESS or FAILURE
2565  {
2566  int readaxis, blipaxis, sliceaxis;
2567  KS_SEQLOC tmploc = KS_INIT_SEQLOC;
2568 
2569  if (!epi) { return FAILURE; }
2570 
2571  /* loc.ampscale may only be +1 or -1 and will control the read polarity.
2572  The blip polarity is controlled in run time with ks_scan_epi_shotcontrol() */
2573  if (!areSame(loc.ampscale, 1.0) && !areSame(loc.ampscale, -1.0)) {
2574  return ks_error("ks_pg_epi_rephasers: loc.ampscale must be +1 or -1 (controls read polarity)");
2575  }
2576 
2577  loc.pos = RDN_GRD(loc.pos);
2578 
2579  switch (loc.board) {
2580  case KS_FREQX_PHASEY:
2581  readaxis = XGRAD; blipaxis = YGRAD; sliceaxis = ZGRAD; break;
2582  case KS_FREQY_PHASEX:
2583  readaxis = YGRAD; blipaxis = XGRAD; sliceaxis = ZGRAD; break;
2584  case KS_FREQX_PHASEZ:
2585  readaxis = XGRAD; blipaxis = ZGRAD; sliceaxis = YGRAD; break;
2586  case KS_FREQZ_PHASEX:
2587  readaxis = ZGRAD; blipaxis = XGRAD; sliceaxis = YGRAD; break;
2588  case KS_FREQY_PHASEZ:
2589  readaxis = YGRAD; blipaxis = ZGRAD; sliceaxis = XGRAD; break;
2590  case KS_FREQZ_PHASEY:
2591  readaxis = ZGRAD; blipaxis = YGRAD; sliceaxis = XGRAD; break;
2592  default:
2593  ks_error("ks_pg_epi_rephasers: loc.board is not valid: %d", loc.board);
2594  return FAILURE;
2595  }
2596 
2597  /* read phaser (same KS_TRAP for DE/REphasing => 2 instances per EPI) */
2598  if (epi->readphaser.duration > 0) {
2599  tmploc.board = readaxis;
2600  tmploc.pos = loc.pos;
2601  if (epi->etl % 2) /* same polarity of rephaser as dephaser if odd ETL */
2602  tmploc.ampscale = loc.ampscale;
2603  else
2604  tmploc.ampscale = -loc.ampscale;
2605  if (ks_pg_trap(&epi->readphaser, tmploc, ctrl) == FAILURE)
2606  return FAILURE;
2607  }
2608 
2609  /* blip phaser (same KS_PHASER for DE/REphasing => 2 instances per EPI) */
2610  if (epi->blipphaser.grad.duration > 0) {
2611  tmploc.board = blipaxis;
2612  tmploc.pos = loc.pos;
2613  tmploc.ampscale = 1.0;
2614  if (ks_pg_phaser(&epi->blipphaser, tmploc, ctrl) == FAILURE)
2615  return FAILURE;
2616  }
2617 
2618  /* slice phaser (same KS_PHASER for DE/REphasing => 2 instances per EPI) */
2619  if (epi->zphaser.grad.duration > 0) {
2620  tmploc.board = sliceaxis;
2621  tmploc.pos = loc.pos;
2622  tmploc.ampscale = 1.0;
2623  if (ks_pg_phaser(&epi->zphaser, tmploc, ctrl) == FAILURE)
2624  return FAILURE;
2625  }
2626 
2627  return SUCCESS;
2628 } /* ks_pg_epi_rephasers */
STATUS ks_pg_phaser(KS_PHASER *phaser, KS_SEQLOC loc, KS_SEQ_CONTROL *ctrl)
Places a KS_PHASER sequence object on a board at some position in the pulse sequence
Definition: KSFoundation_common.c:1669
Definition: KSFoundation.h:1919
#define areSame(a, b)
Definition: KSFoundation.h:119
KS_TRAP grad
Definition: KSFoundation.h:1676
int pos
Definition: KSFoundation.h:389
Definition: KSFoundation.h:1921
KS_PHASER blipphaser
Definition: KSFoundation.h:1840
STATUS ks_error(const char *format,...)
Definition: KSFoundation_common.c:70
int board
Definition: KSFoundation.h:388
KS_PHASER zphaser
Definition: KSFoundation.h:1841
Definition: KSFoundation.h:1924
float ampscale
Definition: KSFoundation.h:390
Definition: KSFoundation.h:1922
int etl
Definition: KSFoundation.h:1843
Definition: KSFoundation.h:1920
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:1923
KS_TRAP readphaser
Definition: KSFoundation.h:1838
#define KS_INIT_SEQLOC
Definition: KSFoundation.h:208
int duration
Definition: KSFoundation.h:585
STATUS ks_pg_trap(KS_TRAP *trap, KS_SEQLOC loc, KS_SEQ_CONTROL *ctrl)
Places a KS_TRAP sequence object on a board at some position in the pulse sequence
Definition: KSFoundation_common.c:1475

◆ ks_pg_epi_echo()

STATUS ks_pg_epi_echo ( KS_EPI epi,
KS_SEQLOC  loc,
KS_SEQ_CONTROL ctrl 
)

Places out the core part a KS_EPI object (used by ks_pg_epi())

This function is called internally by ks_pg_epi() to place out the core part of a KS_EPI object. The core part is the EPI train without leading dephaser gradients or trailing rephaser gradients. See ks_pg_epi() for more details

For advanced usage of KS_EPI, it is possible to call ks_pg_epi_dephaser(), ks_pg_epi_echo() and ks_pg_epi_rephasers() separately instead of the single call to ks_pg_epi(). This allows the dephasing and rephasing gradients to be detached from the core EPI readout part

Parameters
[in,out]epiPointer to a KS_EPI sequence object
[in]locKS_SEQLOC struct to specify when to place the core part of the KS_EPI object
[in]ctrlPointer to the KS_SEQ_CONTROL struct corresponding to the sequence module for this KS_EPI
Return values
STATUSSUCCESS or FAILURE
2633  {
2634  int readaxis, blipaxis;
2635  KS_SEQLOC readloc, bliploc;
2636  int i;
2637 
2638  if (!epi || !epi->read.grad.description) { return FAILURE; }
2639 
2640  /* loc.ampscale may only be +1 or -1 and will control the read polarity.</
2641  The blip polarity is controlled in run time with ks_scan_epi_shotcontrol() */
2642  if (!areSame(loc.ampscale, 1.0) && !areSame(loc.ampscale, -1.0)) {
2643  return ks_error("ks_pg_epi_echo: loc.ampscale must be +1 or -1 (controls read polarity)");
2644  }
2645 
2646  loc.pos = RUP_GRD(loc.pos);
2647 
2648  readloc.pos = loc.pos;
2649 
2650  switch (loc.board) {
2651  case KS_FREQX_PHASEY:
2652  readaxis = XGRAD; blipaxis = YGRAD; break;
2653  case KS_FREQY_PHASEX:
2654  readaxis = YGRAD; blipaxis = XGRAD; break;
2655  case KS_FREQX_PHASEZ:
2656  readaxis = XGRAD; blipaxis = ZGRAD; break;
2657  case KS_FREQZ_PHASEX:
2658  readaxis = ZGRAD; blipaxis = XGRAD; break;
2659  case KS_FREQY_PHASEZ:
2660  readaxis = YGRAD; blipaxis = ZGRAD; break;
2661  case KS_FREQZ_PHASEY:
2662  readaxis = ZGRAD; blipaxis = YGRAD; break;
2663  default:
2664  ks_error("ks_pg_epi_echo: field 'board' in 2nd arg is not valid: %d", loc.board);
2665  return FAILURE;
2666  }
2667 
2668  if (!areSame(fabs(loc.ampscale), 1.0)) {
2669  return ks_error("ks_pg_epi_echo: field 'ampscale' in 2nd arg must be +1 or -1 (controls read lobe polarity)");
2670  }
2671 
2672 
2673  for (i = 0; i < epi->etl; i++) {
2674 
2675  /* read trapezoid */
2676  readloc.board = readaxis;
2677  readloc.ampscale = (float) (i % 2) ? -loc.ampscale : loc.ampscale; /* +1 or -1 */
2678  if (ks_pg_readtrap(&epi->read, readloc, ctrl) == FAILURE)
2679  return FAILURE;
2680 
2681  /* phase encoding blip */
2682  if (i < epi->etl - 1) {
2683  /* etl-1, since no blip after the last read lobe */
2684  bliploc.board = blipaxis;
2685  bliploc.pos = RUP_GRD(readloc.pos + epi->read.grad.duration - epi->blip.duration / 2 + epi->read_spacing / 2);
2686  bliploc.ampscale = 1.0;
2687  if (ks_pg_trap(&epi->blip, bliploc, ctrl) == FAILURE)
2688  return FAILURE;
2689  }
2690 
2691  /* read_spacing is additional gap between lobes (normally 0) */
2692  readloc.pos += RUP_GRD(epi->read.grad.duration + epi->read_spacing);
2693 
2694  } /* etl */
2695 
2696 
2697  return SUCCESS;
2698 
2699 } /* ks_pg_epi_echo */
KS_TRAP blip
Definition: KSFoundation.h:1839
Definition: KSFoundation.h:1919
#define areSame(a, b)
Definition: KSFoundation.h:119
int pos
Definition: KSFoundation.h:389
Definition: KSFoundation.h:1921
STATUS ks_error(const char *format,...)
Definition: KSFoundation_common.c:70
int board
Definition: KSFoundation.h:388
Definition: KSFoundation.h:1924
KS_TRAP grad
Definition: KSFoundation.h:1586
float ampscale
Definition: KSFoundation.h:390
Definition: KSFoundation.h:1922
int etl
Definition: KSFoundation.h:1843
KS_READTRAP read
Definition: KSFoundation.h:1837
Definition: KSFoundation.h:1920
typedef struct used as argument to ks_pg_*** functions to control where and when to place a sequence ...
Definition: KSFoundation.h:387
KS_DESCRIPTION description
Definition: KSFoundation.h:580
Definition: KSFoundation.h:1923
int read_spacing
Definition: KSFoundation.h:1844
int duration
Definition: KSFoundation.h:585
STATUS ks_pg_readtrap(KS_READTRAP *readtrap, KS_SEQLOC loc, KS_SEQ_CONTROL *ctrl)
Places a KS_READTRAP sequence object on a board at some position in the pulse sequence
Definition: KSFoundation_common.c:1785
STATUS ks_pg_trap(KS_TRAP *trap, KS_SEQLOC loc, KS_SEQ_CONTROL *ctrl)
Places a KS_TRAP sequence object on a board at some position in the pulse sequence
Definition: KSFoundation_common.c:1475

◆ ks_pg_epi()

STATUS ks_pg_epi ( KS_EPI epi,
KS_SEQLOC  loc,
KS_SEQ_CONTROL ctrl 
)

Places out a KS_EPI object (EPI train including dephaser and rephaser gradients)

This function should be called in the @pg section to place out a KS_EPI sequence object that creates an EPI readout including dephaser and rephaser gradients. The moments of all trapezoids in the KS_EPI object sum to zero on the two axes the KS_EPI object is placed on. The KS_EPI object is to be placed on two out of three gradients, why special values for the field loc.board must be used. The value should state which logical board (X,Y or Z) that should be used for the readout lobes (FREQ) and which logical board (X, Y or Z) that should be used for the blips (PHASE). One example is KS_FREQX_PHASEY, which will put the EPI readout lobes on XGRAD and the phase encoding blips on YGRAD

The same KS_EPI object can be placed out 16 times per sequence. This limitation is due to the hard limitation on the number of echoes (i.e. different k-spaces per image plane and image volume) that can be acquired per scan. Each time an instance of the KS_EPI object is placed out using ks_pg_epi(), the second argument (the KS_SEQLOC struct) should be modified:

  • .board: Choose between KS_FREQX_PHASEY, KS_FREQY_PHASEX, KS_FREQX_PHASEZ, KS_FREQZ_PHASEX, KS_FREQY_PHASEZ, KS_FREQZ_PHASEY
  • .pos: Absolute time in [us] of the start of the first dephaser gradient
  • .ampscale: For KS_EPI objects, valid values are only +1.0 and -1.0, and this will control the polarity of the first readout gradient. This is rarely needed, and 1.0 should be the standard choice as this does only affect gradient delays and Nyquist ghosting, not the geometric distortion direction.

The sign of the EPI blips will control the direction of the geometric distortions, and this is not done via .ampscale but in run-time using ks_scan_epi_shotcontrol()

For advanced usage of KS_EPI, it is possible to call ks_pg_epi_dephaser(), ks_pg_epi_echo() and ks_pg_epi_rephasers() separately instead of the single call to ks_pg_epi(). This allows the dephasing and rephasing gradients to be detached from the core EPI readout part

Parameters
[in,out]epiPointer to a KS_EPI sequence object
[in]locKS_SEQLOC struct to specify when to place the core part of the KS_EPI object
[in]ctrlPointer to the KS_SEQ_CONTROL struct corresponding to the sequence module for this KS_EPI
Return values
STATUSSUCCESS or FAILURE
2704  {
2705  KS_SEQLOC tmploc = loc;
2706 
2707  /* loc.ampscale may only be +1 or -1 and will control the read polarity.
2708  The blip polarity is controlled in run time with ks_scan_epi_shotcontrol() */
2709  if (!areSame(loc.ampscale, 1.0) && !areSame(loc.ampscale, -1.0)) {
2710  return ks_error("ks_pg_epi: loc.ampscale must be +1 or -1 (controls read polarity)");
2711  }
2712 
2713  if (ks_pg_epi_dephasers(epi, tmploc, ctrl) == FAILURE)
2714  return FAILURE;
2715 
2716  tmploc.pos += IMax(3, epi->readphaser.duration, epi->blipphaser.grad.duration, epi->zphaser.grad.duration);
2717 
2718  if (ks_pg_epi_echo(epi, tmploc, ctrl) == FAILURE) /* main epi train (w/o DE/REphasers) */
2719  return FAILURE;
2720 
2721  tmploc.pos += epi->read.grad.duration * epi->etl;
2722  if (ks_pg_epi_rephasers(epi, tmploc, ctrl) == FAILURE)
2723  return FAILURE;
2724 
2725  return SUCCESS;
2726 } /* ks_pg_epi */
#define areSame(a, b)
Definition: KSFoundation.h:119
KS_TRAP grad
Definition: KSFoundation.h:1676
int pos
Definition: KSFoundation.h:389
STATUS ks_pg_epi_echo(KS_EPI *epi, KS_SEQLOC loc, KS_SEQ_CONTROL *ctrl)
Places out the core part a KS_EPI object (used by ks_pg_epi())
Definition: KSFoundation_common.c:2633
KS_PHASER blipphaser
Definition: KSFoundation.h:1840
STATUS ks_error(const char *format,...)
Definition: KSFoundation_common.c:70
KS_PHASER zphaser
Definition: KSFoundation.h:1841
KS_TRAP grad
Definition: KSFoundation.h:1586
float ampscale
Definition: KSFoundation.h:390
int etl
Definition: KSFoundation.h:1843
KS_READTRAP read
Definition: KSFoundation.h:1837
STATUS ks_pg_epi_dephasers(KS_EPI *epi, KS_SEQLOC loc, KS_SEQ_CONTROL *ctrl)
Places out the dephasing gradients of a KS_EPI object (used by ks_pg_epi())
Definition: KSFoundation_common.c:2496
typedef struct used as argument to ks_pg_*** functions to control where and when to place a sequence ...
Definition: KSFoundation.h:387
KS_TRAP readphaser
Definition: KSFoundation.h:1838
int duration
Definition: KSFoundation.h:585
STATUS ks_pg_epi_rephasers(KS_EPI *epi, KS_SEQLOC loc, KS_SEQ_CONTROL *ctrl)
Places out the rephasing gradients of a KS_EPI object (used by ks_pg_epi())
Definition: KSFoundation_common.c:2565

◆ ks_mat4_zero()

void ks_mat4_zero ( KS_MAT4x4  m)

Makes a 4x4 zero matrix

Parameters
[in,out]mMatrix (KS_MAT4x4)
Returns
void
2877  {
2878  int i;
2879  for (i = 0; i < 16; i++) {
2880  m[i] = 0.0;
2881  }
2882 }

◆ ks_mat4_identity()

void ks_mat4_identity ( KS_MAT4x4  m)

Creates a 4x4 identity matrix

Parameters
[in,out]mMatrix (KS_MAT4x4)
Returns
void
2885  {
2886  int i;
2887  ks_mat4_zero(m);
2888  for (i = 0; i < 4; i++) {
2889  m[i + (i * 4)] = 1.0;
2890  }
2891 }
void ks_mat4_zero(KS_MAT4x4 m)
Makes a 4x4 zero matrix
Definition: KSFoundation_common.c:2877

◆ ks_mat4_print()

void ks_mat4_print ( const KS_MAT4x4  m)

Prints a 4x4 matrix to stdout

Parameters
[in]mMatrix (KS_MAT4x4)
Returns
void
2893  {
2894  int i;
2895  for (i = 0; i < 4; i++) {
2896  printf("| %0.4f %0.4f %0.4f %0.4f |\n", m[i], m[i + 4], m[i + 8], m[i + 12]);
2897  }
2898  fflush(stdout);
2899 }

◆ ks_mat4_multiply()

void ks_mat4_multiply ( KS_MAT4x4  lhs,
const KS_MAT4x4  rhs_left,
const KS_MAT4x4  rhs_right 
)

Multiplication of two 4x4 matrices

Matrix product: [rhs_left] * [rhs_right]

Parameters
[out]lhsMatrix product (KS_MAT4x4)
[in]rhs_leftLeft matrix (KS_MAT4x4)
[in]rhs_rightRight matrix (KS_MAT4x4)
Returns
void
2903  {
2904 
2905  /* Define local variables */
2906  int m, n, k;
2907  int msize = 4;
2908  KS_MAT4x4 Mtmp;
2909  double *ptr;
2910 
2911  if (lhs == rhs_left || lhs == rhs_right) { /* Check if output is equal to any input */
2912  ptr = Mtmp;
2913  } else {
2914  ptr = lhs;
2915  }
2916  for (k = 0; k < (msize * msize); k++) { /* Zero left-hand side*/
2917  ptr[k] = 0.0;
2918  }
2919 
2920  /* Multiply the matrices*/
2921  for (n = 0; n < msize; n++) {
2922  for (m = 0; m < msize; m++) {
2923  for (k = 0; k < msize; k++) {
2924  ptr[m + (n * msize)] += rhs_left[m + (k * msize)] * rhs_right[k + (n * msize)];
2925  }
2926  }
2927  }
2928 
2929  if (lhs == rhs_left || lhs == rhs_right ) { /* Write back (if needed) */
2930  for (k = 0; k < (msize * msize); k++) {
2931  lhs[k] = ptr[k];
2932  }
2933  }
2934 }
double KS_MAT4x4[16]
Definition: KSFoundation.h:265

◆ ks_mat4_invert()

void ks_mat4_invert ( KS_MAT4x4  lhs,
const KS_MAT4x4  rhs 
)

Inversion of a 4x4 matrix

4x4 matrix inversion (http://download.intel.com/design/PentiumIII/sml/24504301.pdf

Parameters
[out]lhsInverted matrix (KS_MAT4x4)
[in]rhsMatrix to be inverted (KS_MAT4x4)
Returns
void
2937  {
2938  double tmp[12]; /* temp array for pairs */
2939  double src[16]; /* array of transpose source rhsrix */
2940  double det; /* determinant */
2941  int i, j;
2942 
2943  /* transpose rhsrix */
2944  for (i = 0; i < 4; i++) {
2945  src[i] = rhs[i * 4];
2946  src[i + 4] = rhs[i * 4 + 1];
2947  src[i + 8] = rhs[i * 4 + 2];
2948  src[i + 12] = rhs[i * 4 + 3];
2949  }
2950 
2951  /* calculate pairs for first 8 elements (cofactors) */
2952  tmp[0] = src[10] * src[15];
2953  tmp[1] = src[11] * src[14];
2954  tmp[2] = src[9] * src[15];
2955  tmp[3] = src[11] * src[13];
2956  tmp[4] = src[9] * src[14];
2957  tmp[5] = src[10] * src[13];
2958  tmp[6] = src[8] * src[15];
2959  tmp[7] = src[11] * src[12];
2960  tmp[8] = src[8] * src[14];
2961  tmp[9] = src[10] * src[12];
2962  tmp[10] = src[8] * src[13];
2963  tmp[11] = src[9] * src[12];
2964 
2965  /* calculate first 8 elements (cofactors) */
2966  lhs[0] = tmp[0] * src[5] + tmp[3] * src[6] + tmp[4] * src[7];
2967  lhs[0] -= tmp[1] * src[5] + tmp[2] * src[6] + tmp[5] * src[7];
2968  lhs[1] = tmp[1] * src[4] + tmp[6] * src[6] + tmp[9] * src[7];
2969  lhs[1] -= tmp[0] * src[4] + tmp[7] * src[6] + tmp[8] * src[7];
2970  lhs[2] = tmp[2] * src[4] + tmp[7] * src[5] + tmp[10] * src[7];
2971  lhs[2] -= tmp[3] * src[4] + tmp[6] * src[5] + tmp[11] * src[7];
2972  lhs[3] = tmp[5] * src[4] + tmp[8] * src[5] + tmp[11] * src[6];
2973  lhs[3] -= tmp[4] * src[4] + tmp[9] * src[5] + tmp[10] * src[6];
2974  lhs[4] = tmp[1] * src[1] + tmp[2] * src[2] + tmp[5] * src[3];
2975  lhs[4] -= tmp[0] * src[1] + tmp[3] * src[2] + tmp[4] * src[3];
2976  lhs[5] = tmp[0] * src[0] + tmp[7] * src[2] + tmp[8] * src[3];
2977  lhs[5] -= tmp[1] * src[0] + tmp[6] * src[2] + tmp[9] * src[3];
2978  lhs[6] = tmp[3] * src[0] + tmp[6] * src[1] + tmp[11] * src[3];
2979  lhs[6] -= tmp[2] * src[0] + tmp[7] * src[1] + tmp[10] * src[3];
2980  lhs[7] = tmp[4] * src[0] + tmp[9] * src[1] + tmp[10] * src[2];
2981  lhs[7] -= tmp[5] * src[0] + tmp[8] * src[1] + tmp[11] * src[2];
2982 
2983  /* calculate pairs for second 8 elements (cofactors) */
2984  tmp[0] = src[2] * src[7];
2985  tmp[1] = src[3] * src[6];
2986  tmp[2] = src[1] * src[7];
2987  tmp[3] = src[3] * src[5];
2988  tmp[4] = src[1] * src[6];
2989  tmp[5] = src[2] * src[5];
2990  tmp[6] = src[0] * src[7];
2991  tmp[7] = src[3] * src[4];
2992  tmp[8] = src[0] * src[6];
2993  tmp[9] = src[2] * src[4];
2994  tmp[10] = src[0] * src[5];
2995  tmp[11] = src[1] * src[4];
2996 
2997  /* calculate second 8 elements (cofactors) */
2998  lhs[8] = tmp[0] * src[13] + tmp[3] * src[14] + tmp[4] * src[15];
2999  lhs[8] -= tmp[1] * src[13] + tmp[2] * src[14] + tmp[5] * src[15];
3000  lhs[9] = tmp[1] * src[12] + tmp[6] * src[14] + tmp[9] * src[15];
3001  lhs[9] -= tmp[0] * src[12] + tmp[7] * src[14] + tmp[8] * src[15];
3002  lhs[10] = tmp[2] * src[12] + tmp[7] * src[13] + tmp[10] * src[15];
3003  lhs[10] -= tmp[3] * src[12] + tmp[6] * src[13] + tmp[11] * src[15];
3004  lhs[11] = tmp[5] * src[12] + tmp[8] * src[13] + tmp[11] * src[14];
3005  lhs[11] -= tmp[4] * src[12] + tmp[9] * src[13] + tmp[10] * src[14];
3006  lhs[12] = tmp[2] * src[10] + tmp[5] * src[11] + tmp[1] * src[9];
3007  lhs[12] -= tmp[4] * src[11] + tmp[0] * src[9] + tmp[3] * src[10];
3008  lhs[13] = tmp[8] * src[11] + tmp[0] * src[8] + tmp[7] * src[10];
3009  lhs[13] -= tmp[6] * src[10] + tmp[9] * src[11] + tmp[1] * src[8];
3010  lhs[14] = tmp[6] * src[9] + tmp[11] * src[11] + tmp[3] * src[8];
3011  lhs[14] -= tmp[10] * src[11] + tmp[2] * src[8] + tmp[7] * src[9];
3012  lhs[15] = tmp[10] * src[10] + tmp[4] * src[8] + tmp[9] * src[9];
3013  lhs[15] -= tmp[8] * src[9] + tmp[11] * src[10] + tmp[5] * src[8];
3014 
3015  /* calculate determinant */
3016  det = src[0] * lhs[0] + src[1] * lhs[1] + src[2] * lhs[2] + src[3] * lhs[3];
3017 
3018  /* calculate rhsrix inverse */
3019  det = 1 / det;
3020  for (j = 0; j < 16; j++)
3021  lhs[j] *= det;
3022 }

◆ ks_mat4_setgeometry()

void ks_mat4_setgeometry ( KS_MAT4x4  lhs,
float  x,
float  y,
float  z,
float  xr,
float  yr,
float  zr 
)

Set geometry for KS_MAT4x4

Parameters
[out]lhsset matrix (KS_MAT4x4)
[in]xdisplacement (mm)
[in]ydisplacement (mm)
[in]zdisplacement (mm)
[in]xrrotation (deg)
[in]yrrotation (deg)
[in]zrrotation (deg)
Returns
void
3024  {
3025  double cosa;
3026  double sina;
3027  double cosb;
3028  double sinb;
3029  double cosg;
3030  double sing;
3031 
3032  ks_mat4_identity(lhs);
3033 
3034  xr *= PI / 180.0;
3035  yr *= PI / 180.0;
3036  zr *= PI / 180.0;
3037 
3038  /*
3039  Rotation matrices for positive counterclockwise angles about the positive
3040  coordinate axis for a right handed coordinate system:
3041 
3042  | 1 0 0 |
3043  R1(xr) = | 0 cos(xr) -sin(xr)|
3044  | 0 sin(xr) cos(xr)|
3045 
3046 
3047  | cos(yr) 0 sin(yr) |
3048  R2(yr) = | 0 1 0 |
3049  | -sin(yr) 0 cos(yr) |
3050 
3051  | cos(zr) -sin(zr) 0 |
3052  R3(zr) = | sin(zr) cos(zr) 0 |
3053  | 0 0 1 |
3054  */
3055 
3056  cosa = cos(xr);
3057  sina = sin(xr);
3058  cosb = cos(yr);
3059  sinb = sin(yr);
3060  cosg = cos(zr);
3061  sing = sin(zr);
3062 
3063  /* Rotations: R_z * R_y * R_x */
3064  lhs[ 0] = cosb * cosg;
3065  lhs[ 1] = sina * sinb * cosg - cosa * sing;
3066  lhs[ 2] = cosa * sinb * cosg + sina * sing;
3067  lhs[ 4] = cosb * sing;
3068  lhs[ 5] = sina * sinb * sing + cosa * cosg;
3069  lhs[ 6] = cosa * sinb * sing - sina * cosg;
3070  lhs[ 8] = -sinb;
3071  lhs[ 9] = sina * cosb;
3072  lhs[10] = cosa * cosb;
3073 
3074  /* Translations */
3075  lhs[ 3] = x;
3076  lhs[ 7] = y;
3077  lhs[11] = z;
3078 }
void ks_mat4_identity(KS_MAT4x4 m)
Creates a 4x4 identity matrix
Definition: KSFoundation_common.c:2885

◆ ks_mat4_setrotation1axis()

void ks_mat4_setrotation1axis ( KS_MAT4x4  rhs,
float  rot,
char  axis 
)

Create a new 4x4 matrix with a rotation around a single axis

Parameters
[out]rhsRotation matrix (KS_MAT4x4)
[in]rotAmount of rotation around axis [degrees]
[in]axisOne of (include the quotes): 'x', 'y', 'z'
Returns
void
3082  {
3083  ks_mat4_identity(rhs);
3084 
3085  rot *= PI / 180.0; /* deg->rad */
3086 
3087  switch (axis) {
3088  case 'x':
3089  rhs[5] = cos(rot);
3090  rhs[6] = sin(rot);
3091  rhs[9] = -sin(rot);
3092  rhs[10] = cos(rot);
3093  break;
3094  case 'y':
3095  rhs[0] = cos(rot);
3096  rhs[2] = sin(rot);
3097  rhs[8] = -sin(rot);
3098  rhs[10] = cos(rot);
3099  break;
3100  case 'z':
3101  rhs[0] = cos(rot);
3102  rhs[1] = sin(rot);
3103  rhs[4] = -sin(rot);
3104  rhs[5] = cos(rot);
3105  break;
3106  }
3107 
3108 }
void ks_mat4_identity(KS_MAT4x4 m)
Creates a 4x4 identity matrix
Definition: KSFoundation_common.c:2885

◆ ks_mat4_extractrotation()

void ks_mat4_extractrotation ( KS_MAT3x3  R,
const KS_MAT4x4  M 
)

Extract a 3x3 rotation matrix from a 4x4 transformation matrix

Parameters
[out]R3x3 rotation matrix (KS_MAT3x3), row major
[in]MTransformation matrix (KS_MAT4x4), row major
Returns
void
3110  {
3111  int i, j;
3112  for (i = 0; i < 3; ++i) {
3113  for (j = 0; j < 3; ++j) {
3114  R[3 * i + j] = M[4 * i + j];
3115  }
3116  }
3117 }

◆ ks_mat4_extracttranslation()

void ks_mat4_extracttranslation ( double *  T,
const KS_MAT4x4  M 
)

Extract a 3 element array with translations from a 4x4 transformation matrix

Parameters
[out]TFloat array with 3 elements (must be allocated)
[in]MTransformation matrix (KS_MAT4x4), row major
Returns
void
3119  {
3120  int i;
3121  for (i = 0; i < 3; ++i) {
3122  T[i] = M[4 * i + 3];
3123  }
3124 }

◆ ks_mat3_identity()

void ks_mat3_identity ( KS_MAT3x3  m)

Creates a 3x3 identity matrix

Parameters
[in,out]mMatrix (KS_MAT3x3)
Returns
void
3127  {
3128  int i;
3129  for (i = 0; i < 9; ++i) {
3130  m[i] = 0;
3131  }
3132  m[0] = 1;
3133  m[4] = 1;
3134  m[8] = 1;
3135 }

◆ ks_mat3_multiply()

void ks_mat3_multiply ( KS_MAT3x3  lhs,
const KS_MAT3x3  rhs_left,
const KS_MAT3x3  rhs_right 
)

Multiplication of two 3x3 matrices

Matrix product: [rhs_left] * [rhs_right]

Parameters
[out]lhsMatrix product (KS_MAT3x3)
[in]rhs_leftLeft matrix (KS_MAT3x3)
[in]rhs_rightRight matrix (KS_MAT3x3)
Returns
void
3137  {
3138 
3139  /* TODO: add checks for overlapping arrays */
3140 
3141  int i, j, k;
3142  for (i = 0; i < 3; ++i) {
3143  for (j = 0; j < 3; ++j) {
3144  double acc = 0;
3145  for (k = 0; k < 3; ++k) {
3146  acc += rhs_left[3 * i + k] * rhs_right[3 * k + j];
3147  }
3148  lhs[3 * i + j] = acc;
3149  }
3150  }
3151 }

◆ ks_mat3_print()

void ks_mat3_print ( const KS_MAT3x3  m)

Prints a 3x3 matrix to stdout

Parameters
[in]mMatrix (KS_MAT3x3)
Returns
void
3153  {
3154  int i;
3155  for (i = 0; i < 3; ++i) {
3156  fprintf(stderr, "|%.2f,\t%.2f\t%.2f|\n", m[3 * i], m[3 * i + 1], m[3 * i + 2]);
3157  }
3158  fprintf(stderr, "\n");
3159 }

◆ ks_mat3_apply()

void ks_mat3_apply ( double *  w,
const KS_MAT3x3  R,
const double *  v 
)

Rotate a 3x1 vector by a 3x3 rotation matrix (alibi)

Apply an active (alibi) rotation – i.e w = R * v

Parameters
[out]wOutput vector (float array with 3 elements)
[in]RRotation matrix (KS_MAT3x3)
[in]vInput vector (float array with 3 elements)
Returns
void
3161  {
3162  int i, j;
3163  for (i = 0; i < 3; ++i) {
3164  double acc = 0;
3165  for (j = 0; j < 3; ++j) {
3166  acc += R[3 * i + j] * v[j];
3167  }
3168  w[i] = acc;
3169  }
3170 }

◆ ks_mat3_invapply()

void ks_mat3_invapply ( double *  w,
const KS_MAT3x3  R,
const double *  v 
)

Rotate a 3x1 vector by the inverse of 3x3 rotation matrix (alias)

Apply a passive (alias) rotation – i.e w = R' * v = R^-1 * v

Parameters
[out]wOutput vector (float array with 3 elements)
[in]RRotation matrix (KS_MAT3x3)
[in]vInput vector (float array with 3 elements)
Returns
void
3172  {
3173  int i, j;
3174  for (i = 0; i < 3; ++i) {
3175  double acc = 0;
3176  for (j = 0; j < 3; ++j) {
3177  acc += v[j] * R[3 * j + i];
3178  }
3179  w[i] = acc;
3180  }
3181 }

◆ ks_scan_update_slice_location()

void ks_scan_update_slice_location ( SCAN_INFO *  new_loc,
const SCAN_INFO  orig_loc,
const KS_MAT4x4  M_physical,
const KS_MAT4x4  M_logical 
)

Updates a SCAN_INFO struct using physical and logical 4x4 transformation matrices

Updates the slice location – e.g for motion correction (physical space) or propeller rotations (logical space)

M_physical and M_logical are row-major 4x4 matricies that describe rotations (R_physical, R_logical) and translations (T_physical, T_logical) of the FOV in the physical and logical coordinate systems, respectively. If R_slice and T_slice describe the position of the prescribed FOV contained in orig_loc then new_loc will describe the overall rotation R_new and translation T_new of the FOV such that:

R_new = R_physical * R_slice * R_logical

T_new = R_logical^-1 * (R_slice^-1 * R_physical^-1 * T_physical + T_slice + T_logical)

NOTE: All transformations are expected to be active (alibi) that denote a change of position/orientation, not passive (alias) that denote a change of coordinates system. See https://en.wikipedia.org/wiki/Active_and_passive_transformation

SCAN_INFO is defined in $ESE_TOP/psd/include/epic_geometry_types.h and the global scan_info variable is an array of SCAN_INFO structs holding information of the graphically prescribed slices

Parameters
[out]new_locPointer to a SCAN_INFO struct holding the new slice information
[in]orig_locSCAN_INFO struct holding the original slice information
[in]M_physicalTransformation matrix (4x4) in the physical space
[in]M_logicalTransformation matrix (4x4) in the logical (i.e. XGRAD, YGRAD, ZGRAD) space
Returns
void
3186  {
3187  int i;
3188  KS_MAT3x3 R_m;
3189  KS_MAT3x3 R_l;
3190  KS_MAT3x3 R_ms;
3191  KS_MAT3x3 R_msl;
3192 
3193  const double T_s[3] = {orig_loc.oprloc, orig_loc.opphasoff, orig_loc.optloc};
3194  double T_m[3];
3195  double T_l[3];
3196  double T_msl[3];
3197  double T_new[3];
3198 
3199  /* initialize to identity matrix (will be used if NULL in 3rd or 4th arg) */
3200  KS_MAT4x4 Mphys = KS_MAT4x4_IDENTITY;
3202 
3203  /* copy slice rotation and translation */
3204  KS_MAT3x3 R_s;
3205  for (i = 0; i < 9; i++) {
3206  R_s[i] = orig_loc.oprot[i];
3207  }
3208 
3209  /* copy transformation matrices (if they are not NULL) to local matrices */
3210  if (M_physical != NULL) {
3211  memcpy(Mphys, M_physical, sizeof(KS_MAT4x4));
3212  }
3213  if (M_logical != NULL) {
3214  memcpy(Mlog, M_logical, sizeof(KS_MAT4x4));
3215  }
3216 
3217  ks_mat4_extractrotation(R_m, Mphys);
3218  ks_mat4_extractrotation(R_l, Mlog);
3219 
3220  ks_mat4_extracttranslation(T_m, Mphys);
3221  ks_mat4_extracttranslation(T_l, Mlog);
3222 
3223  /* R_ms = R_m * R_s */
3224  ks_mat3_multiply(R_ms, R_m, R_s);
3225 
3226  /* R_ms^-1 * T_m = T_m' * R_ms' */
3227  ks_mat3_invapply(T_msl, R_ms, T_m);
3228 
3229  for (i = 0; i < 3; ++i) {
3230  T_msl[i] += T_s[i] + T_l[i];
3231  }
3232 
3233  ks_mat3_invapply(T_new, R_l, T_msl);
3234  new_loc->oprloc = T_new[0];
3235  new_loc->opphasoff = T_new[1];
3236  new_loc->optloc = T_new[2];
3237 
3238  ks_mat3_multiply(R_msl, R_ms, R_l);
3239  for (i=0; i<9; ++i) {
3240  new_loc->oprot[i] = R_msl[i];
3241  }
3242 } /* ks_scan_update_slice_location */
void ks_mat3_invapply(double *w, const KS_MAT3x3 R, const double *v)
Rotate a 3x1 vector by the inverse of 3x3 rotation matrix (alias)
Definition: KSFoundation_common.c:3172
double KS_MAT4x4[16]
Definition: KSFoundation.h:265
double KS_MAT3x3[9]
Definition: KSFoundation.h:264
void ks_mat4_extractrotation(KS_MAT3x3 R, const KS_MAT4x4 M)
Extract a 3x3 rotation matrix from a 4x4 transformation matrix
Definition: KSFoundation_common.c:3110
#define KS_MAT4x4_IDENTITY
Definition: KSFoundation.h:256
void ks_mat4_extracttranslation(double *T, const KS_MAT4x4 M)
Extract a 3 element array with translations from a 4x4 transformation matrix
Definition: KSFoundation_common.c:3119
void ks_mat3_multiply(KS_MAT3x3 lhs, const KS_MAT3x3 rhs_left, const KS_MAT3x3 rhs_right)
Multiplication of two 3x3 matrices
Definition: KSFoundation_common.c:3137

◆ ks_scan_rotate()

void ks_scan_rotate ( SCAN_INFO  slice_info)

Performs a rotation of the logical system on hardware (WARP)

The field .oprot (9-element array) in the SCAN_INFO struct holds the rotation matrix that should be played out

This function performs the necessary scaling of the rotation matrix using loggrd and phygrd and then calls setrotatearray(), which performs the actual rotation on hardware during the next SSI time.

See ks_scan_update_slice_location() for detail on how to create new SCAN_INFO structs in run-time

Parameters
[in]slice_infoSCAN_INFO struct holding new slice information
Returns
void
989  {
990 #ifdef IPG
991 
992  /* scale to long int rotation matrix (accounting for gradient scaling) */
993  scale(&slice_info.oprot, &rsprot[0], 1, &loggrd, &phygrd, 0);
994 
995  setrotatearray(1, rsprot[0]);
996 
997 #endif
998 }
PHYS_GRAD phygrd
LOG_GRAD loggrd
STATUS scale(FLOAT(*inrotmat)[9], long(*outrotmat)[9], INT slquant, LOG_GRAD *lgrad, PHYS_GRAD *pgrad, INT contdebug)

◆ ks_scan_isirotate()

void ks_scan_isirotate ( KS_ISIROT isirot)

Performs an immediate rotation of the logical system on hardware (WARP)

This function needs a psd-specific wrapper function to execute (see ks_pg_isirot()) since the ISI interupt routine is calling a void function without input arguments. This psd-specific wrapper function should only contain the call to ks_scan_isirotate() with the psd-specific KS_ISIROT set up in pulsegen().

For N number of calls to ks_pg_isirot() in the sequence module's pg-function, the field isirot.numinstances will be set to N, and this many ISI interrupt with a corresponding isirot.isinumber exist in the sequence module. Valid ISI numbers are 4-7, and one available number should be linked to one specific wrapper function using ks_eval_isirot() and ks_pg_isirot().

In real-time, ks_scan_isirotate() will increment the isirot.counter field by 1 and restart at 0 after isirot->numinstances. isirot.counter is set to 0 in ks_pg_isirot(). Based on the value of the .counter field, it will assign a pre-stored SCAN_INFO struct corresponding to this counter value. Again, this connection is done by ks_pg_isirot().

ks_scan_isirotate() takes the current SCAN_INFO and converts it to long int, and then calls setrotateimm(..., WARP_UPDATE_ON_SSP_INT);

Parameters
[in]isirotPointer to the KS_ISIROT struct set up by ks_pg_isirot()
Returns
void
1002  {
1003 #ifdef IPG
1004 
1005  if (isirot == NULL || isirot->numinstances < 1)
1006  return;
1007 
1008  /* wrap around after numinstances interupts */
1009  isirot->counter = isirot->counter % isirot->numinstances;
1010 
1011  /* scale to long int rotation matrix (accounting for gradient scaling) */
1012  scale(&isirot->scan_info[isirot->counter++].oprot, &rsprot[0], 1, &loggrd, &phygrd, 0);
1013 
1014  /* rotate on SSP interrupt */
1015  setrotateimm(rsprot[0], WARP_UPDATE_ON_SSP_INT);
1016 
1017 #endif
1018 }
SCAN_INFO * scan_info
Definition: KSFoundation.h:472
PHYS_GRAD phygrd
LOG_GRAD loggrd
int counter
Definition: KSFoundation.h:475
STATUS scale(FLOAT(*inrotmat)[9], long(*outrotmat)[9], INT slquant, LOG_GRAD *lgrad, PHYS_GRAD *pgrad, INT contdebug)
int numinstances
Definition: KSFoundation.h:476

◆ ks_pg_fse_flip_angle_taperoff()

STATUS ks_pg_fse_flip_angle_taperoff ( double *  flip_angles,
int  etl,
double  flip1,
double  flip2,
double  flip3,
double  target_flip,
int  start_middle 
)
2767  {
2768 
2769  int i;
2770 
2771  flip_angles[0] = flip1;
2772  flip_angles[1] = flip2;
2773  for (i = 2; i < etl; i++) {
2774  flip_angles[i] = flip3;
2775  }
2776 
2777  if (flip3 <= target_flip || etl < 5) {
2778  /* flip3 must be larger than target_flip and etl must be larger than 4 */
2779  return SUCCESS;
2780  }
2781 
2782  /* after half of the echo train or third pulse, ramp down flip angles to last_flip */
2783  double first_flip = flip3;
2784  double last_flip = target_flip;
2785  int start_pulse = start_middle ? etl/2 : 2;
2786  for (i = start_pulse; i < etl; i++) {
2787  flip_angles[i] = first_flip + (((last_flip - first_flip)/(etl - start_pulse - 1)) * (i - start_pulse));
2788  }
2789 
2790  return SUCCESS;
2791 
2792 } /* ks_pg_fse_flip_angle_taperoff */

◆ ks_pg_mod_fse_rfpulse_structs()

void ks_pg_mod_fse_rfpulse_structs ( KS_SELRF rf1,
KS_SELRF rf2,
KS_SELRF rf3,
const double *  flip_angles,
const int  etl 
)
2732  {
2733 
2734  float SAR_scale_1 = 0.0;
2735  float SAR_scale_2 = 0.0;
2736  float SAR_scale_3 = 0.0;
2737  int i;
2738 
2739  SAR_scale_1 = pow(flip_angles[0] / rf1->rf.flip, 2.0);
2740  SAR_scale_2 = pow(flip_angles[1] / rf2->rf.flip, 2.0);
2741  for (i = 2; i < etl; i++) {
2742  SAR_scale_3 += pow(flip_angles[i] / rf3->rf.flip, 2.0);
2743  }
2744  SAR_scale_3 /= etl - 2;
2745 
2746  rf1->rf.rfpulse.effwidth *= SAR_scale_1;
2747  rf1->rf.rfpulse.max_rms_b1 *= sqrt(SAR_scale_1);
2748  rf1->rf.rfpulse.abswidth *= sqrt(SAR_scale_1);
2749 
2750  rf2->rf.rfpulse.effwidth *= SAR_scale_2;
2751  rf2->rf.rfpulse.max_rms_b1 *= sqrt(SAR_scale_2);
2752  rf2->rf.rfpulse.abswidth *= sqrt(SAR_scale_2);
2753 
2754  rf3->rf.rfpulse.effwidth *= SAR_scale_3;
2755  rf3->rf.rfpulse.max_rms_b1 *= sqrt(SAR_scale_3);
2756  rf3->rf.rfpulse.abswidth *= sqrt(SAR_scale_3);
2757 
2758 } /* ks_pg_mod_fse_rfpulse_structs */
RF_PULSE rfpulse
Definition: KSFoundation.h:948
float flip
Definition: KSFoundation.h:940
KS_RF rf
Definition: KSFoundation.h:1485

◆ ks_instancereset_trap()

void ks_instancereset_trap ( KS_TRAP trap)

Resets the usage counters on TGT for a KS_TRAP sequence object (advanced use)

This function is for advanced use where a single sequence generating function is used in parallel in multiple sequences (i.e. in different SEQLENGTH()). This should be called after calling ks_copy_and_reset_obj()

The function sets .base.ngenerated to 0 (counter for number of times placed out on TGT), and sets .wfpulse and .wfi to NULL to trigger the allocation of new hardware memory, before running the sequence generating function again.

Parameters
[in,out]trapPointer to a KS_TRAP sequence object
Returns
void
2797  {
2798  if (!trap) { return; }
2799 
2800  trap->base.ngenerated = 0;
2801  trap->wfpulse = NULL;
2802  trap->wfi = NULL;
2803 }
KS_BASE base
Definition: KSFoundation.h:579
KS_WFINSTANCE * wfi
Definition: KSFoundation.h:591
int ngenerated
Definition: KSFoundation.h:410
WF_PULSE ** wfpulse
Definition: KSFoundation.h:590

◆ ks_instancereset_wait()

void ks_instancereset_wait ( KS_WAIT wait)

Resets the usage counters on TGT for a KS_WAIT sequence object (advanced use)

This function is for advanced use where a single sequence generating function is used in parallel in multiple sequences (i.e. in different SEQLENGTH()). This should be called after calling ks_copy_and_reset_obj()

The function sets .base.ngenerated to 0 (counter for number of times placed out on TGT), and sets .wfpulse and .wfi to NULL to trigger the allocation of new hardware memory, before running the sequence generating function again.

Parameters
[in,out]waitPointer to a KS_WAIT sequence object
Returns
void
2815  {
2816  if (!wait) { return; }
2817 
2818  wait->base.ngenerated = 0;
2819  wait->wfpulse = NULL;
2820  wait->wfi = NULL;
2821 }
KS_BASE base
Definition: KSFoundation.h:457
KS_WFINSTANCE * wfi
Definition: KSFoundation.h:462
WF_PULSE * wfpulse
Definition: KSFoundation.h:461
int ngenerated
Definition: KSFoundation.h:410

◆ ks_instancereset_phaser()

void ks_instancereset_phaser ( KS_PHASER phaser)

Resets the usage counters on TGT for a KS_PHASER sequence object (advanced use)

This function is for advanced use where a single sequence generating function is used in parallel in multiple sequences (i.e. in different SEQLENGTH()). This should be called after calling ks_copy_and_reset_obj()

The function sets .base.ngenerated to 0 (counter for number of times placed out on TGT), and sets .wfpulse and .wfi to NULL to trigger the allocation of new hardware memory, before running the sequence generating function again.

Parameters
[in,out]phaserPointer to a KS_PHASER sequence object
Returns
void
2823  {
2824  if (!phaser) { return; }
2825 
2826  ks_instancereset_trap(&phaser->grad);
2827 }
KS_TRAP grad
Definition: KSFoundation.h:1676
void ks_instancereset_trap(KS_TRAP *trap)
Resets the usage counters on TGT for a KS_TRAP sequence object (advanced use)
Definition: KSFoundation_common.c:2797

◆ ks_instancereset_readtrap()

void ks_instancereset_readtrap ( KS_READTRAP readtrap)

Resets the usage counters on TGT for a KS_READTRAP sequence object (advanced use)

This function is for advanced use where a single sequence generating function is used in parallel in multiple sequences (i.e. in different SEQLENGTH()). This should be called after calling ks_copy_and_reset_obj()

The function sets .base.ngenerated to 0 (counter for number of times placed out on TGT), and sets .wfpulse and .wfi to NULL to trigger the allocation of new hardware memory, before running the sequence generating function again.

Parameters
[in,out]readtrapPointer to a KS_READTRAP sequence object
Returns
void
2829  {
2830  if (!read) { return; }
2831 
2832  read->acq.base.ngenerated = 0;
2833  read->acq.echo = NULL;
2834 
2835  ks_instancereset_trap(&read->grad);
2836  ks_instancereset_trap(&read->omega);
2837 }
void ks_instancereset_trap(KS_TRAP *trap)
Resets the usage counters on TGT for a KS_TRAP sequence object (advanced use)
Definition: KSFoundation_common.c:2797

◆ ks_instancereset_rf()

void ks_instancereset_rf ( KS_RF rf)

Resets the usage counters on TGT for a KS_RF sequence object (advanced use)

This function is for advanced use where a single sequence generating function is used in parallel in multiple sequences (i.e. in different SEQLENGTH()). This should be called after calling ks_copy_and_reset_obj()

The function sets .base.ngenerated to 0 (counter for number of times placed out on TGT), and sets .wfpulse and .wfi to NULL to trigger the allocation of new hardware memory, before running the sequence generating function again.

Parameters
[in,out]rfPointer to a KS_RF sequence object
Returns
void
2839  {
2840  if (!rf) { return; }
2841 
2845 
2846 }
KS_WAVE thetawave
Definition: KSFoundation.h:951
KS_WAVE rfwave
Definition: KSFoundation.h:949
void ks_instancereset_wave(KS_WAVE *wave)
Definition: KSFoundation_common.c:2806
KS_WAVE omegawave
Definition: KSFoundation.h:950

◆ ks_instancereset_selrf()

void ks_instancereset_selrf ( KS_SELRF selrf)

Resets the usage counters on TGT for a KS_SELRF sequence object (advanced use)

This function is for advanced use where a single sequence generating function is used in parallel in multiple sequences (i.e. in different SEQLENGTH()). This should be called after calling ks_copy_and_reset_obj()

The function sets .base.ngenerated to 0 (counter for number of times placed out on TGT), and sets .wfpulse and .wfi to NULL to trigger the allocation of new hardware memory, before running the sequence generating function again.

Parameters
[in,out]selrfPointer to a KS_SELRF sequence object
Returns
void
2848  {
2849  if (!selrf) { return; }
2850 
2851  ks_instancereset_rf(&selrf->rf);
2852  ks_instancereset_trap(&selrf->pregrad);
2853  ks_instancereset_trap(&selrf->grad);
2855 
2857 
2858 }
KS_TRAP grad
Definition: KSFoundation.h:1491
KS_WAVE gradwave
Definition: KSFoundation.h:1493
KS_TRAP pregrad
Definition: KSFoundation.h:1490
KS_RF rf
Definition: KSFoundation.h:1485
void ks_instancereset_wave(KS_WAVE *wave)
Definition: KSFoundation_common.c:2806
void ks_instancereset_rf(KS_RF *rf)
Resets the usage counters on TGT for a KS_RF sequence object (advanced use)
Definition: KSFoundation_common.c:2839
KS_TRAP postgrad
Definition: KSFoundation.h:1492
void ks_instancereset_trap(KS_TRAP *trap)
Resets the usage counters on TGT for a KS_TRAP sequence object (advanced use)
Definition: KSFoundation_common.c:2797

◆ ks_instancereset_epi()

void ks_instancereset_epi ( KS_EPI epi)

Resets the usage counters on TGT for a KS_EPI sequence object (advanced use)

This function is for advanced use where a single sequence generating function is used in parallel in multiple sequences (i.e. in different SEQLENGTH()). This should be called after calling ks_copy_and_reset_obj()

The function sets .base.ngenerated to 0 (counter for number of times placed out on TGT), and sets .wfpulse and .wfi to NULL to trigger the allocation of new hardware memory, before running the sequence generating function again.

Parameters
[in,out]epiPointer to a KS_EPI sequence object
Returns
void
2860  {
2861  if (!epi) { return; }
2862 
2865  ks_instancereset_trap(&epi->blip);
2867 }
KS_TRAP blip
Definition: KSFoundation.h:1839
void ks_instancereset_phaser(KS_PHASER *phaser)
Resets the usage counters on TGT for a KS_PHASER sequence object (advanced use)
Definition: KSFoundation_common.c:2823
KS_PHASER blipphaser
Definition: KSFoundation.h:1840
void ks_instancereset_readtrap(KS_READTRAP *read)
Resets the usage counters on TGT for a KS_READTRAP sequence object (advanced use)
Definition: KSFoundation_common.c:2829
KS_READTRAP read
Definition: KSFoundation.h:1837
KS_TRAP readphaser
Definition: KSFoundation.h:1838
void ks_instancereset_trap(KS_TRAP *trap)
Resets the usage counters on TGT for a KS_TRAP sequence object (advanced use)
Definition: KSFoundation_common.c:2797

◆ ks_compare_wfi_by_timeboard()

int ks_compare_wfi_by_timeboard ( const KS_WFINSTANCE a,
const KS_WFINSTANCE b 
)

Compares two WF_INSTANCEs by time, then board

This function is used by the qsort() routine in ks_compare_wfi_by_timeboard()

If two WF_INSTANCEs occur at the same time, XGRAD will come before YGRAD and YGRAD before ZGRAD

Parameters
[in]aPointer to the first KS_WFINSTANCE
[in]bPointer to the second KS_WFINSTANCE
Return values
valueLarger or less than 0 depending on sorting order
3265  {
3266 
3267  return (a->loc.pos > b->loc.pos) - (a->loc.pos < b->loc.pos) +
3268  (a->loc.pos == b->loc.pos) * ((a->loc.board > b->loc.board) - (a->loc.board < b->loc.board));
3269 }
int pos
Definition: KSFoundation.h:389
int board
Definition: KSFoundation.h:388
KS_SEQLOC loc
Definition: KSFoundation.h:401

◆ ks_compare_wfi_by_boardtime()

int ks_compare_wfi_by_boardtime ( const KS_WFINSTANCE a,
const KS_WFINSTANCE b 
)

Compares two WF_INSTANCES by board, then time

This function is used by the qsort() routine in ks_compare_wfi_by_boardtime()

WF_INSTANCEs are sorted first by board. If two WF_INSTANCEs occur on the same board, t they will be sorted in time

Parameters
[in]aPointer to the first KS_WFINSTANCE
[in]bPointer to the second KS_WFINSTANCE
Return values
valueLarger or less than 0 depending on sorting order
3272  {
3273 
3274  return (a->loc.board > b->loc.board) - (a->loc.board < b->loc.board) +
3275  (a->loc.board == b->loc.board) * ((a->loc.pos > b->loc.pos) - (a->loc.pos < b->loc.pos));
3276 }
int pos
Definition: KSFoundation.h:389
int board
Definition: KSFoundation.h:388
KS_SEQLOC loc
Definition: KSFoundation.h:401

◆ ks_compare_wfp_by_time()

int ks_compare_wfp_by_time ( const WF_PULSE *  a,
const WF_PULSE *  b 
)

Compares two WF_PULSES in time

This function is used by the qsort() routine in ks_sort_wfp_by_time(), which is called from ks_pg_read() for data acquisition sorting purposes. It is assumed both WF_PULSEs have only one instance

Parameters
[in]aPointer to the first WF_PULSE
[in]bPointer to the second WF_PULSE
Return values
valueLarger or less than 0 depending on sorting order
3278  {
3279 
3280  return (a->inst_hdr_head->start > b->inst_hdr_head->start) - (a->inst_hdr_head->start < b->inst_hdr_head->start);
3281 }

◆ ks_compare_pint()

int ks_compare_pint ( const void *  v1,
const void *  v2 
)

Compares two int pointers

Parameters
[in]v1Pointer to the first int pointer
[in]v2Pointer to the second int pointer
Return values
valueLarger or less than 0 depending on sorting order
3308  {
3309  const int i1 = **(const int **) v1;
3310  const int i2 = **(const int **) v2;
3311 
3312  return i1 < i2 ? -1 : (i1 > i2);
3313 }

◆ ks_compare_pshort()

int ks_compare_pshort ( const void *  v1,
const void *  v2 
)

Compares two short pointers

Parameters
[in]v1Pointer to the first short pointer
[in]v2Pointer to the second shot pointer
Return values
valueLarger or less than 0 depending on sorting order
3300  {
3301  const short i1 = **(const short **) v1;
3302  const short i2 = **(const short **) v2;
3303 
3304  return i1 < i2 ? -1 : (i1 > i2);
3305 }

◆ ks_compare_pfloat()

int ks_compare_pfloat ( const void *  v1,
const void *  v2 
)

Compares two float pointers

Parameters
[in]v1Pointer to the first float pointer
[in]v2Pointer to the second float pointer
Return values
valueLarger or less than 0 depending on sorting order
3316  {
3317  const float i1 = **(const float **) v1;
3318  const float i2 = **(const float **) v2;
3319 
3320  return i1 < i2 ? -1 : (i1 > i2);
3321 }

◆ ks_compare_int()

int ks_compare_int ( const void *  v1,
const void *  v2 
)

Compares two integers (int)

Parameters
[in]v1Pointer to the first int
[in]v2Pointer to the second int
Return values
valueLarger or less than 0 depending on sorting order
3332  {
3333  const int i1 = *(const int *) v1;
3334  const int i2 = *(const int *) v2;
3335 
3336  return i1 < i2 ? -1 : (i1 > i2);
3337 }

◆ ks_compare_short()

int ks_compare_short ( const void *  v1,
const void *  v2 
)

Compares two integers (short)

Parameters
[in]v1Pointer to the first short
[in]v2Pointer to the second short
Return values
valueLarger or less than 0 depending on sorting order
3324  {
3325  const short i1 = *(const short *) v1;
3326  const short i2 = *(const short *) v2;
3327 
3328  return i1 < i2 ? -1 : (i1 > i2);
3329 }

◆ ks_compare_float()

int ks_compare_float ( const void *  v1,
const void *  v2 
)

Compares two floats

Parameters
[in]v1Pointer to the first float
[in]v2Pointer to the second float
Return values
valueLarger or less than 0 depending on sorting order
3340  {
3341  const float i1 = *(const float *) v1;
3342  const float i2 = *(const float *) v2;
3343 
3344  return i1 < i2 ? -1 : (i1 > i2);
3345 }

◆ ks_sort_getsortedindx()

void ks_sort_getsortedindx ( int *  sortedindx,
int *  array,
int  n 
)

Sort an array of integers (int)

Parameters
[out]sortedindxArray of indices into the array to make it sorted
[in,out]arrayArray to be sorted
[in]nNumber of elements in array
Returns
void
3349  {
3350  int i;
3351  int *parray[n];
3352 
3353  for (i = 0; i < n; i++)
3354  parray[i] = &array[i];
3355 
3356  qsort(parray, n, sizeof * parray, ks_compare_pint);
3357 
3358  for (i = 0; i < n; i++)
3359  sortedindx[i] = parray[i] - array;
3360 
3361 }
int ks_compare_pint(const void *v1, const void *v2)
Compares two int pointers
Definition: KSFoundation_common.c:3308

◆ ks_sort_getsortedindx_s()

void ks_sort_getsortedindx_s ( int *  sortedindx,
short *  array,
int  n 
)

Sort an array of integers (short)

Parameters
[out]sortedindxArray of indices into the array to make it sorted
[in,out]arrayArray to be sorted
[in]nNumber of elements in array
Returns
void
3363  {
3364  int i;
3365  short *parray[n];
3366 
3367  for (i = 0; i < n; i++)
3368  parray[i] = &array[i];
3369 
3370  qsort(parray, n, sizeof * parray, ks_compare_pshort);
3371 
3372  for (i = 0; i < n; i++)
3373  sortedindx[i] = parray[i] - array;
3374 
3375 }
int ks_compare_pshort(const void *v1, const void *v2)
Compares two short pointers
Definition: KSFoundation_common.c:3300

◆ ks_sort_getsortedindx_f()

void ks_sort_getsortedindx_f ( int *  sortedindx,
float *  array,
int  n 
)

Sort an array of floats

Parameters
[out]sortedindxArray of indices into the array to make it sorted
[in,out]arrayArray to be sorted
[in]nNumber of elements in array
Returns
void
3378  {
3379  int i;
3380  float *parray[n];
3381 
3382  for (i = 0; i < n; i++)
3383  parray[i] = &array[i];
3384 
3385  qsort(parray, n, sizeof * parray, ks_compare_pfloat);
3386 
3387  for (i = 0; i < n; i++)
3388  sortedindx[i] = parray[i] - array;
3389 
3390 }
int ks_compare_pfloat(const void *v1, const void *v2)
Compares two float pointers
Definition: KSFoundation_common.c:3316

◆ ks_sort_wfi_by_timeboard()

void ks_sort_wfi_by_timeboard ( KS_WFINSTANCE a,
int  nitems 
)

Sort WF_INSTANCEs in time, then board

This is the sorting method used in all ks_pg_***() functions

Parameters
[in,out]aArray of KS_WFINSTANCE elements
[in]nitemsNumber of elements in array
Returns
void
3283  {
3284  qsort(a, nitems, sizeof(KS_WFINSTANCE),
3285  (int(*)(const void *, const void *))ks_compare_wfi_by_timeboard);
3286 }
(Internal use) Structure being a part of various sequence objects to sort them in time across boards...
Definition: KSFoundation.h:398
int ks_compare_wfi_by_timeboard(const KS_WFINSTANCE *a, const KS_WFINSTANCE *b)
Compares two WF_INSTANCEs by time, then board
Definition: KSFoundation_common.c:3265

◆ ks_sort_wfi_by_boardtime()

void ks_sort_wfi_by_boardtime ( KS_WFINSTANCE a,
int  nitems 
)

Sort WF_INSTANCEs by board, then time

This function is an alternative to ks_sort_wfi_by_timeboard(), which is not used at the moment

Parameters
[in,out]aArray of KS_WFINSTANCE elements
[in]nitemsNumber of elements in array
Returns
void
3288  {
3289  qsort(a, nitems, sizeof(KS_WFINSTANCE), (int(*)(const void *, const void *))ks_compare_wfi_by_boardtime);
3290 }
(Internal use) Structure being a part of various sequence objects to sort them in time across boards...
Definition: KSFoundation.h:398
int ks_compare_wfi_by_boardtime(const KS_WFINSTANCE *a, const KS_WFINSTANCE *b)
Compares two WF_INSTANCES by board, then time
Definition: KSFoundation_common.c:3272

◆ ks_sort_wfp_by_time()

void ks_sort_wfp_by_time ( WF_PULSE *  a,
int  nitems 
)

Sort WF_PULSEs in time

This is the sorting method used in ks_pg_read()

It is assumed that all a[idx] for idx in [0,nitems) have only one instance each

Parameters
[in,out]aArray of WF_PULSE elements
[in]nitemsNumber of elements in array
Returns
void
3292  {
3293  qsort(a, nitems, sizeof(WF_PULSE),
3294  (int(*)(const void *, const void *))ks_compare_wfp_by_time);
3295 }
int ks_compare_wfp_by_time(const WF_PULSE *a, const WF_PULSE *b)
Compares two WF_PULSES in time
Definition: KSFoundation_common.c:3278

◆ 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_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_slicetime_begin()

void ks_plot_slicetime_begin ( )
3396  {
3397 #ifdef IPG
3398  return;
3399 #endif
3401 }
void ks_plot_host_slicetime_begin()
Definition: KSFoundation_host.c:5390

◆ ks_plot_slicetime()

void ks_plot_slicetime ( KS_SEQ_CONTROL ctrl,
int  nslices,
float *  slicepos_mm,
float  slthick_mm,
KS_PLOT_EXCITATION_MODE  exctype 
)
3404  {
3405 #ifdef IPG
3406  return;
3407 #endif
3408  ks_plot_host_slicetime(ctrl, nslices, slicepos_mm, slthick_mm, excmode);
3409 }
void ks_plot_host_slicetime(KS_SEQ_CONTROL *ctrl, int nslices, float *slicepos_mm, float slthick_mm, KS_PLOT_EXCITATION_MODE exctype)
Definition: KSFoundation_host.c:5481

◆ ks_plot_slicetime_endofslicegroup()

void ks_plot_slicetime_endofslicegroup ( const char *  desc)
3428  {
3429 #ifdef IPG
3430  return;
3431 #endif
3433 }
Definition: KSFoundation.h:347
void ks_plot_host_slicetime_endofslicegroup(const char *desc, const KS_PLOT_SLICEGROUP_MODE tag)
Definition: KSFoundation_host.c:5420

◆ ks_plot_slicetime_endofslicegroup_tagged()

void ks_plot_slicetime_endofslicegroup_tagged ( const char *  desc,
const KS_PLOT_SLICEGROUP_MODE  tag 
)
3420  {
3421 #ifdef IPG
3422  return;
3423 #endif
3425 }
void ks_plot_host_slicetime_endofslicegroup(const char *desc, const KS_PLOT_SLICEGROUP_MODE tag)
Definition: KSFoundation_host.c:5420

◆ ks_plot_slicetime_endofpass()

void ks_plot_slicetime_endofpass ( KS_PLOT_PASS_MODE  )
3412  {
3413 #ifdef IPG
3414  return;
3415 #endif
3417 }
void ks_plot_host_slicetime_endofpass(KS_PLOT_PASS_MODE)
Definition: KSFoundation_host.c:5450

◆ ks_plot_slicetime_end()

void ks_plot_slicetime_end ( )
3437  {
3438 #ifdef IPG
3439  return;
3440 #endif
3442 }
void ks_plot_host_slicetime_end()
Definition: KSFoundation_host.c:5536

◆ 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()

void ks_plot_host_slicetime ( KS_SEQ_CONTROL ctrl,
int  nslices,
float *  slicepos_mm,
float  slthick_mm,
KS_PLOT_EXCITATION_MODE  exctype 
)
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_endofslicegroup()

void ks_plot_host_slicetime_endofslicegroup ( const char *  desc,
const KS_PLOT_SLICEGROUP_MODE  tag 
)
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  )
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_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 **  plan,
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

◆ ks_plot_tgt_reset()

void ks_plot_tgt_reset ( KS_SEQ_CONTROL ctrl)
3445  {
3446  FILE *fp;
3447  char fname_json[1000];
3448  char cmd[200];
3449  #ifdef PSD_HW
3450  return;
3451  #endif
3452 
3453 
3454  if (ks_plot_filefmt == KS_PLOT_OFF) {
3455  return;
3456  }
3457 
3458  system("mkdir -p ./plot/tgt/");
3459  sprintf(fname_json, "./plot/tgt/psdplot_tgt_%s_%s.json", ks_psdname, ctrl->description);
3460  sprintf(cmd, "rm -f %s ./plot/tgt/%s_%s_*_tgt.*",
3461  fname_json, ks_psdname, ctrl->description);
3462  system(cmd);
3463  fp = fopen(fname_json, "w");
3464  fprintf(fp, "{\n"
3465  "\"metadata\": {\n"
3466  "\t\"mode\": \"tgt\",\n"
3467  "\t\"psdname\": \"%s\",\n"
3468  "\t\"sequence\": \"%s\",\n"
3469  "\t\"duration\": %0.3f,\n"
3470  "\t\"min_duration\": %0.3f,\n"
3471  "\t\"ssi_time\": %0.3f,\n"
3472  "\t\"momentstart\": %.3f\n"
3473  "},\n"
3474  "\"frames\": [\n"
3475  "{}]}",
3476  ks_psdname,
3477  ctrl->description,
3478  ctrl->duration/1000.0,
3479  ctrl->min_duration/1000.0,
3480  ctrl->ssi_time/1000.0,
3481  ctrl->momentstart/1000.0
3482  );
3483  fclose(fp);
3484 }
Definition: KSFoundation.h:329
int momentstart
Definition: KSFoundation.h:1136
int duration
Definition: KSFoundation.h:1135
int ks_plot_filefmt
Definition: GERequired.e:245
int min_duration
Definition: KSFoundation.h:1132
int ssi_time
Definition: KSFoundation.h:1134
char ks_psdname[256]
Definition: GERequired.e:221
KS_DESCRIPTION description
Definition: KSFoundation.h:1141

◆ ks_plot_tgt_addframe()

void ks_plot_tgt_addframe ( KS_SEQ_CONTROL ctrl)

Writes a plot frame to file

Parameters
ctrlPointer to sequence control
3488  {
3489  #if (!(defined(SIM) && defined(IPG)))
3490  return;
3491  #endif
3492 
3493  if (ks_plot_filefmt == KS_PLOT_OFF) {
3494  return;
3495  }
3496 
3497  char boardc[8] = "xyzrRto"; /* x,y,z,rho1, rho2, theta, omega*/
3498  char fname[1000];
3499  sprintf(fname, "./plot/tgt/psdplot_tgt_%s_%s.json", ks_psdname, ctrl->description);
3500  FILE *fp;
3501  int i, w, inst, boardinstance;
3502  KS_SEQLOC loc;
3503  fp = fopen(fname, "r+");
3504  fseek(fp, -2, SEEK_END);
3505  fputs(",{\n", fp);
3506  short iamp;
3507  float gradmax;
3508  float amp;
3509 
3510  /* TRAPEZOIDS */
3511  fprintf(fp, "\"trapz\": {\n");
3512  fflush(fp);
3513  KS_TRAP* trap;
3514  for (i = 0; i < ctrl->gradrf.numtrap; i++) { /* Each unique trap object */
3515  trap = ctrl->gradrf.trapptr[i];
3516  fprintf(fp, "\t\"%s\": {\n", trap->description);
3517  fprintf(fp, "\t\t\"ramptime\": %0.3f,\n", trap->ramptime / 1000.0);
3518  fprintf(fp, "\t\t\"plateautime\": %0.3f,\n", trap->plateautime / 1000.0);
3519  fprintf(fp, "\t\t\"duration\": %0.3f,\n", trap->duration / 1000.0);
3520  fprintf(fp, "\t\t\"amp\": %0.6f,\n", 1.0);
3521  fprintf(fp, "\t\t\"instances\": [{\n");
3522  fflush(fp);
3523  for (inst = 0; inst < ctrl->gradrf.trapptr[i]->base.ngenerated; inst++) { /* Each instance of this trap */
3524  loc = trap->locs[inst];
3525  fprintf(fp, "\t\t\t\"board\": \"%c\",\n", boardc[loc.board]);
3526  fprintf(fp, "\t\t\t\"time\": %0.3f,\n", loc.pos / 1000.0);
3527 
3528  WF_PULSE* plateau_ptr = trap->wfi[inst].wf;
3529  boardinstance = trap->wfi[inst].boardinstance;
3530  /* Get the linked list of the plateau (used to get the position of all wf instances) */
3531  /* WF_INSTR_HDR instr_plateau = plateau_ptr->inst_hdr_tail; */
3532  /* WF_INSTR_HDR instr_attack = plateau_ptr->assoc_pulse->inst_hdr_tail; */ /* Attack is the associated pulse of the plateau */
3533  /* WF_INSTR_HDR instr_decay = plateau_ptr->assoc_pulse->assoc_pulse->inst_hdr_tail; */ /* Decay is the associated pulse of the attack */
3534  /*
3535  for (idx = 0; idx < (boardinstance); idx++) {
3536  instr_plateau = instr_plateau->next;
3537  instr_attack = instr_attack->next;
3538  instr_decay = instr_decay->next;
3539  }
3540  */
3541  /* Plateau amplitude is written in G/cm */
3542  getiamp(&iamp, plateau_ptr, boardinstance);
3543  fprintf(fp, "\t\t\t\"ampscale\": 1.0,\n");
3544  gradmax = ks_syslimits_gradtarget(loggrd, trap->wfi[inst].loc.board);
3545  if (loc.board == OMEGA || loc.board == THETA) {
3546  gradmax = 1;
3547  }
3548  amp = gradmax * (float)iamp / MAX_PG_IAMP;
3549  fprintf(fp, "\t\t\t\"rtscale\": %.12f\n", amp);
3550 
3551  /* char pulsename[100]; */
3552  /* (float)pbeg(plateau_ptr, pulsename, boardinstance)/1000.0 */
3553  /* sprintf(pulsename, "%s%s", plateau_ptr->pulsename, "a"); */
3554 
3555  if (inst == (trap->base.ngenerated -1)) {
3556  fprintf(fp, "\t\t}]\n");
3557  } else {
3558  fprintf(fp, "\t\t},{\n");
3559  }
3560  } /* inst */
3561  if (i == (ctrl->gradrf.numtrap -1)) {
3562  fprintf(fp, "\t}\n");
3563  } else {
3564  fprintf(fp, "\t},\n");
3565  }
3566  }
3567  fprintf(fp, "},\n"); /* Trapezoids */
3568 
3569  fprintf(fp, "\"acquisitions\": [\n");
3570  /* ACQUISITIONS */
3571  for (i = 0; i < ctrl->gradrf.numacq; i++) { /* Each unique KS_READ object */
3572  KS_READ* acq = ctrl->gradrf.readptr[i];
3573  fprintf(fp, "\t{\n");
3574  fprintf(fp, "\t\t\"description\": \"%s\",\n", acq->description);
3575  fprintf(fp, "\t\t\"duration\": %0.3f,\n", acq->duration / 1000.0);
3576  fprintf(fp, "\t\t\"rbw\": %0.3f,\n", acq->rbw);
3577  fprintf(fp, "\t\t\"samples\": %d,\n", acq->filt.outputs);
3578  fprintf(fp, "\t\t\"time\": [");
3579  for (inst = 0; inst < acq->base.ngenerated; ++inst) {
3580  fprintf(fp, "%0.3f", acq->pos[inst] / 1000.0);
3581  if (inst == (acq->base.ninst -1)) {
3582  fprintf(fp, "]\n");
3583  } else {
3584  fprintf(fp, ", ");
3585  }
3586  } /* inst */
3587  if (i == (ctrl->gradrf.numacq -1)) {
3588  fprintf(fp, "\t}\n");
3589  } else {
3590  fprintf(fp, "\t},\n");
3591  }
3592  } /* ACQUISITIONS */
3593  fprintf(fp, "],\n"); /* End acquisitions */
3594 
3595  /* RF */
3596  WF_PULSE* rf_wf;
3597  WF_PULSE* omega_wf;
3598  WF_PULSE* theta_wf;
3599  short new_wave_iamp;
3600  KS_IWAVE acquired_wave; /* short int array */
3601  fprintf(fp, "\"rf\": {\n");
3602  for (i = 0; i < ctrl->gradrf.numrf; ++i) { /* Each unique KS_RF object */
3603  KS_RF* rfptr = ctrl->gradrf.rfptr[i];
3604 
3605  if (rfptr->rfwave.res > 0) {
3606  /* RHO */
3607  char roledesc[50];
3608  if (rfptr->role == KS_RF_ROLE_EXC) {
3609  strcpy(roledesc,"KS_RF_ROLE_EXC");
3610  } else if (rfptr->role == KS_RF_ROLE_REF) {
3611  strcpy(roledesc,"KS_RF_ROLE_REF");
3612  } else if (rfptr->role == KS_RF_ROLE_CHEMSAT) {
3613  strcpy(roledesc, "KS_RF_ROLE_CHEMSAT");
3614  } else if (rfptr->role == KS_RF_ROLE_INV) {
3615  strcpy(roledesc, "KS_RF_ROLE_INV");
3616  } else if (rfptr->role == KS_RF_ROLE_SPSAT) {
3617  strcpy(roledesc, "KS_RF_ROLE_SPSAT");
3618  } else {
3619  strcpy(roledesc,"NONE");
3620  }
3621  fprintf(fp, "\t\"%s\": {\n", rfptr->rfwave.description);
3622  fprintf(fp, "\t\t\"description\": \"%s\",\n", rfptr->rfwave.description);
3623  fprintf(fp, "\t\t\"flipangle\": %0.3f,\n", rfptr->flip);
3624  fprintf(fp, "\t\t\"nominal_flipangle\": %0.3f,\n", rfptr->rfpulse.nom_fa);
3625  fprintf(fp, "\t\t\"max_b1\": %0.6f,\n", rfptr->rfpulse.max_b1);
3626  fprintf(fp, "\t\t\"isodelay\": %0.3f,\n", rfptr->start2iso / 1000.0);
3627  fprintf(fp, "\t\t\"duration\": %0.3f,\n", rfptr->rfwave.duration / 1000.0);
3628  fprintf(fp, "\t\t\"role\": \"%s\",\n", roledesc);
3629  fprintf(fp, "\t\t\"amp\": %.3f,\n", rfptr->amp);
3630  /* OMEGA */
3631  fprintf(fp, "\t\t\"omega\": {\n");
3632  if (rfptr->omegawave.res > 0) {
3633  fprintf(fp, "\t\t\t\"description\": \"%s\",\n", rfptr->omegawave.description);
3634  fprintf(fp, "\t\t\t\"duration\": %0.3f,\n", rfptr->omegawave.duration / 1000.0);
3635  fprintf(fp, "\t\t\t\"instances\": [{\n");
3636  for (inst = 0; inst < rfptr->omegawave.base.ngenerated; ++inst) {
3637  loc = rfptr->omegawave.locs[inst];
3638  boardinstance = rfptr->omegawave.wfi[inst].boardinstance;
3639  omega_wf = &(rfptr->omegawave.wfpulse[OMEGA][KS_WF_MAIN]);
3640  movewaveimm(acquired_wave, omega_wf, boardinstance, rfptr->omegawave.res, FROMHARDWARE);
3641  getiamp(&new_wave_iamp, omega_wf, boardinstance);
3642  fprintf(fp, "\t\t\t\t\"waveform\": [0,");
3643  for (w = 0; w < rfptr->omegawave.res; w++) {
3644  fprintf(fp, "%d,", acquired_wave[w]);
3645  }
3646  fprintf(fp, "0],\n");
3647  fprintf(fp, "\t\t\t\t\"time\": %0.3f,\n", loc.pos/1000.0);
3648  fprintf(fp, "\t\t\t\t\"board\": \"%c\",\n", boardc[loc.board]);
3649  fprintf(fp, "\t\t\t\t\"ampscale\": 1.0,\n");
3650  fprintf(fp, "\t\t\t\t\"rtscale\": %0.12f\n", (float)(new_wave_iamp) / (max_pg_wamp * max_pg_wamp));
3651  if (inst == (rfptr->omegawave.base.ngenerated -1)) {
3652  fprintf(fp, "\t\t\t}]\n");
3653  } else {
3654  fprintf(fp, "\t\t\t},{\n");
3655  }
3656  }
3657  }
3658  fprintf(fp, "\t\t},\n"); /* Omega */
3659 
3660  /* THETA */
3661  fprintf(fp, "\t\t\"theta\": {\n");
3662  if (rfptr->thetawave.res > 0) {
3663  fprintf(fp, "\t\t\t\"description\": \"%s\",\n", rfptr->thetawave.description);
3664  fprintf(fp, "\t\t\t\"duration\": %0.3f,\n", rfptr->thetawave.duration / 1000.0);
3665  fprintf(fp, "\t\t\t\"instances\": [{\n");
3666  for (inst = 0; inst < rfptr->thetawave.base.ngenerated; ++inst) {
3667  loc = rfptr->thetawave.locs[inst];
3668  boardinstance = rfptr->thetawave.wfi[inst].boardinstance;
3669  theta_wf = &(rfptr->thetawave.wfpulse[THETA][KS_WF_MAIN]);
3670  movewaveimm(acquired_wave, theta_wf, boardinstance, rfptr->thetawave.res, FROMHARDWARE);
3671  getiamp(&new_wave_iamp, theta_wf, boardinstance);
3672  fprintf(fp, "\t\t\t\t\"waveform\": [0");
3673  for (w = 0; w < rfptr->thetawave.res; w++) {
3674  fprintf(fp, "%d,", acquired_wave[w]);
3675  }
3676  fprintf(fp, "0],\n");
3677  fprintf(fp, "\t\t\t\t\"time\": %0.3f,\n", loc.pos/1000.0);
3678  fprintf(fp, "\t\t\t\t\"board\": \"%c\",\n", boardc[loc.board]);
3679  fprintf(fp, "\t\t\t\t\"ampscale\": 1.0,\n");
3680  fprintf(fp, "\t\t\t\t\"rtscale\": %0.12f\n", (float)(new_wave_iamp) / (max_pg_wamp * max_pg_wamp));
3681  if (inst == (rfptr->thetawave.base.ngenerated -1)) {
3682  fprintf(fp, "\t\t\t}]\n");
3683  } else {
3684  fprintf(fp, "\t\t\t},{\n");
3685  }
3686  }
3687  }
3688  fprintf(fp, "\t\t},\n"); /* Theta */
3689 
3690  fprintf(fp, "\t\t\"instances\": [{\n");
3691  for (inst = 0; inst < rfptr->rfwave.base.ngenerated; ++inst) {
3692  loc = rfptr->rfwave.locs[inst];
3693  boardinstance = rfptr->rfwave.wfi[inst].boardinstance;
3694  rf_wf = &(rfptr->rfwave.wfpulse[RHO][KS_WF_MAIN]);
3695  /* Get waveform from hardware */
3696  movewaveimm(acquired_wave, rf_wf, boardinstance, rfptr->rfwave.res, FROMHARDWARE);
3697  /* B1max recalculations */
3698  getiamp(&new_wave_iamp, rf_wf, boardinstance);
3699  fprintf(fp, "\t\t\t\"waveform\": [0,");
3700  for (w = 0; w < rfptr->rfwave.res; w++) {
3701  fprintf(fp, "%d,", acquired_wave[w]);
3702  }
3703  fprintf(fp, "0],\n");
3704  fprintf(fp, "\t\t\t\"board\": \"%c\",\n", boardc[loc.board]);
3705  fprintf(fp, "\t\t\t\"time\": %0.3f,\n", loc.pos/1000.0);
3706  fprintf(fp, "\t\t\t\"ampscale\": 1.0,\n");
3707  fprintf(fp, "\t\t\t\"rtscale\": %0.12f\n", (float)new_wave_iamp / (rfptr->amp * max_pg_wamp * max_pg_wamp));
3708  if (inst == (rfptr->rfwave.base.ngenerated -1)) {
3709  fprintf(fp, "\t\t}]\n");
3710  } else {
3711  fprintf(fp, "\t\t},{\n");
3712  }
3713  }
3714  if (i == (ctrl->gradrf.numrf -1)) {
3715  fprintf(fp, "\t}\n");
3716  } else {
3717  fprintf(fp, "\t},\n");
3718  }
3719  } /* res > 0 */
3720 
3721 
3722  } /* End RF */
3723  fprintf(fp, "},\n"); /* End RF */
3724  /* other (non-RF) waves */
3725  fprintf(fp, "\"waves\": {\n");
3726 
3727  for (i = 0; i < ctrl->gradrf.numwave; i++) { /* each unique wave object */
3728  KS_WAVE* waveptr = ctrl->gradrf.waveptr[i];
3729  if (waveptr->res > 0) {
3730  fprintf(fp, "\t\"%s\": {\n", waveptr->description);
3731  fprintf(fp, "\t\t\"description\": \"%s\",\n", waveptr->description);
3732  fprintf(fp, "\t\t\"duration\": %0.3f,\n", waveptr->duration / 1000.0);
3733  fprintf(fp, "\t\t\"instances\": [{\n");
3734  for (inst = 0; inst < waveptr->base.ninst; inst++) { /* each instance of the wave */
3735  loc = waveptr->locs[inst];
3736  boardinstance = waveptr->wfi[inst].boardinstance;
3737  WF_PULSE* other_wf = &(waveptr->wfpulse[loc.board][KS_WF_MAIN]);
3738  movewaveimm(acquired_wave, other_wf, boardinstance, waveptr->res, FROMHARDWARE);
3739  getiamp(&new_wave_iamp, other_wf, boardinstance);
3740  fprintf(fp, "\t\t\t\"waveform\": [0,");
3741  for (w = 0; w < waveptr->res; w++) {
3742  fprintf(fp, "%d,", acquired_wave[w]); /* Or just waveptr->waveform[w] ? */
3743  }
3744  fprintf(fp, "0],\n");
3745  fprintf(fp, "\t\t\t\"board\": \"%c\",\n", boardc[loc.board]);
3746  fprintf(fp, "\t\t\t\"time\": %0.3f,\n", loc.pos/1000.0);
3747  fprintf(fp, "\t\t\t\"ampscale\": 1.0,\n");
3748  fprintf(fp, "\t\t\t\"rtscale\": %0.12f\n", ks_syslimits_gradtarget(loggrd, loc.board) * (float)new_wave_iamp / (max_pg_wamp * max_pg_wamp));
3749  if (inst == (waveptr->base.ninst -1)) {
3750  fprintf(fp, "\t\t}]\n");
3751  } else {
3752  fprintf(fp, "\t\t},{\n");
3753  }
3754  }
3755  if (i == (ctrl->gradrf.numwave -1)) {
3756  fprintf(fp, "\t}\n");
3757  } else {
3758  fprintf(fp, "\t},\n");
3759  }
3760  } /* res > 0 */
3761  }
3762  fprintf(fp, "}\n"); /* End waves */
3763  fprintf(fp, "}\n"); /* End frame */
3764  fprintf(fp,"]}"); /* End frames*/
3765 
3766  fflush(fp);
3767  fclose(fp);
3768  return;
3769 }
int plateautime
Definition: KSFoundation.h:584
short KS_IWAVE[KS_MAXWAVELEN]
Definition: KSFoundation.h:263
int boardinstance
Definition: KSFoundation.h:399
Core sequence object for making trapezoids on X,Y,Z, and OMEGA boards
Definition: KSFoundation.h:578
int start2iso
Definition: KSFoundation.h:944
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
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 board
Definition: KSFoundation.h:388
float rbw
Definition: KSFoundation.h:734
float ks_syslimits_gradtarget(LOG_GRAD loggrd, int board)
Returns the maximum target amplitude for a board (internal use)
Definition: KSFoundation_common.c:220
Definition: KSFoundation.h:1953
int numrf
Definition: KSFoundation.h:967
float flip
Definition: KSFoundation.h:940
KS_BASE base
Definition: KSFoundation.h:731
KS_GRADRFCTRL gradrf
Definition: KSFoundation.h:1143
KS_WFINSTANCE * wfi
Definition: KSFoundation.h:591
KS_DESCRIPTION description
Definition: KSFoundation.h:732
KS_WFINSTANCE * wfi
Definition: KSFoundation.h:674
Composite sequence object for RF (with optional OMEGA & THETA pulses)
Definition: KSFoundation.h:938
KS_WAVE thetawave
Definition: KSFoundation.h:951
KS_SEQLOC loc
Definition: KSFoundation.h:401
KS_WAVE * waveptr[KS_MAXUNIQUE_WAVE]
Definition: KSFoundation.h:970
KS_DESCRIPTION description
Definition: KSFoundation.h:666
LOG_GRAD loggrd
WF_PULSE ** wfpulse
Definition: KSFoundation.h:673
KS_WAVE rfwave
Definition: KSFoundation.h:949
int ks_plot_filefmt
Definition: GERequired.e:245
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
typedef struct used as argument to ks_pg_*** functions to control where and when to place a sequence ...
Definition: KSFoundation.h:387
WF_PULSE * wf
Definition: KSFoundation.h:400
KS_SEQLOC locs[KS_MAXINSTANCES]
Definition: KSFoundation.h:589
KS_TRAP * trapptr[KS_MAXUNIQUE_TRAP]
Definition: KSFoundation.h:968
Definition: KSFoundation.h:1957
KS_DESCRIPTION description
Definition: KSFoundation.h:580
int numwave
Definition: KSFoundation.h:971
Definition: KSFoundation.h:1953
FILTER_INFO filt
Definition: KSFoundation.h:735
char ks_psdname[256]
Definition: GERequired.e:221
KS_WAVE omegawave
Definition: KSFoundation.h:950
KS_DESCRIPTION description
Definition: KSFoundation.h:1141
int ngenerated
Definition: KSFoundation.h:410
int duration
Definition: KSFoundation.h:585
KS_READ * readptr[KS_MAXUNIQUE_READ]
Definition: KSFoundation.h:972
int res
Definition: KSFoundation.h:667
Definition: KSFoundation.h:1953
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

◆ ks_scan_rf_ampscale()

void ks_scan_rf_ampscale ( KS_RF rf,
int  instanceno,
float  ampscale 
)

Changes the amplitude of one or all instances of an RF pulse (KS_RF)

This function multiplies one instance of a KS_RF object with an amplitude scale factor (3rd arg) that must be in range [-1.0,1.0]. To change all instances of a KS_RF object, use INSTRALL as the 2nd argument.

The actual flip angle for an instance of a KS_RF object is the multiplication of the three factors:

  1. The designed flip angle
  2. The per-instance .ampscale in the KS_SEQLOC struct passed to ks_pg_rf()
  3. The ampscale value passed in as 3rd argument to this function. Since both ampscale factors are forced to be in range [-1.0,1.0], it is not possible to increase the flip angle beyond the designed value (.flip)
Parameters
[in,out]rfPointer to KS_RF
[in]instancenoInstance of KS_RF to change (INSTRALL changes all instances)
[in]ampscaleRF amplitude scale factor in range [-1.0,1.0]
379  {
380  int firstinstance, lastinstance, i;
381 #ifdef IPG
382  int instramp;
383  KS_WFINSTANCE *wfi;
384  int numplaced = rf->rfwave.base.ngenerated;
385 #else
386  int numplaced = rf->rfwave.base.ninst;
387  if (!numplaced) {
388  return;
389  }
390  float* tmp = (float*)realloc(rf->rfwave.rtscale, numplaced * sizeof(float));
391  if (!tmp) {
392  ks_error("%s: rtscale realloc failed", __FUNCTION__);
393  return;
394  }
395  rf->rfwave.rtscale = tmp;
396 #endif
397 
398  if (instanceno == INSTRALL) {
399  firstinstance = 0;
400  lastinstance = numplaced - 1;
401  } else {
402  firstinstance = instanceno;
403  lastinstance = instanceno;
404  }
405 
406  /* input validation */
407  if (numplaced == 0) {
408  return;
409  }
410 
411  if (firstinstance < 0 || lastinstance >= numplaced) {
412  ks_error("ks_scan_rf_ampscale(%s): instanceno (%d) out of range [0,%d]", rf->rfwave.description, instanceno, numplaced - 1);
413  return;
414  }
415 
416  if (fabs(ampscale) > 1.0) {
417  ks_error("ks_scan_rf_ampscale(%s): ampscale too large (%f)", rf->rfwave.description, ampscale);
418  return;
419  }
420 
421 #ifdef IPG
422 
423  /* set amplitude(s) */
424  for (i = firstinstance; i <= lastinstance; i++) {
425  wfi = &rf->rfwave.wfi[i];
426  instramp = (int)(rf->amp * wfi->loc.ampscale * ampscale * MAX_PG_IAMP);
427  setiamp(instramp, wfi->wf, wfi->boardinstance);
428  } /* for */
429 
430 #else
431  for (i = firstinstance; i <= lastinstance; i++) {
432  rf->rfwave.rtscale[i] = ampscale;
433  }
434 #endif
435 
436 } /* ks_scan_rf_ampscale */
float * rtscale
Definition: KSFoundation.h:675
int boardinstance
Definition: KSFoundation.h:399
STATUS ks_error(const char *format,...) __attribute__((format(printf
Common error message function for HOST and TGT
(Internal use) Structure being a part of various sequence objects to sort them in time across boards...
Definition: KSFoundation.h:398
int ninst
Definition: KSFoundation.h:409
float ampscale
Definition: KSFoundation.h:390
KS_WFINSTANCE * wfi
Definition: KSFoundation.h:674
KS_SEQLOC loc
Definition: KSFoundation.h:401
KS_DESCRIPTION description
Definition: KSFoundation.h:666
KS_WAVE rfwave
Definition: KSFoundation.h:949
KS_BASE base
Definition: KSFoundation.h:665
float amp
Definition: KSFoundation.h:943
WF_PULSE * wf
Definition: KSFoundation.h:400
int ngenerated
Definition: KSFoundation.h:410

◆ ks_scan_rf_on()

void ks_scan_rf_on ( KS_RF rf,
int  instanceno 
)

Resets the amplitude of one or all instances of an RF pulse (KS_RF)

This function (re)sets the RF amplitude to the state given by ks_pg_rf()

Parameters
[in,out]rfPointer to KS_RF
[in]instancenoInstance of KS_RF to change (INSTRALL changes all instances)
442  {
443 
444  ks_scan_rf_ampscale(rf, instanceno, 1.0);
445 
446 }
void ks_scan_rf_ampscale(KS_RF *rf, int instanceno, float ampscale)
Changes the amplitude of one or all instances of an RF pulse (KS_RF)
Definition: KSFoundation_tgt.c:379

◆ ks_scan_rf_on_chop()

void ks_scan_rf_on_chop ( KS_RF rf,
int  instanceno 
)

Resets the amplitude of one or all instances of an RF pulse (KS_RF) and toggles the sign (chopping)

Everytime this function is called, the magnitude of the RF amplitude will be (re)set the RF amplitude to the state given by ks_pg_rf() and the polarity of the RF amplitude will be changed. If this function is called each TR (RF chopping) for a linear single-line k-space acquisition, a FOV/2 shift will occur in the image with any DC component shifted out to the edges of the image FOV.

Parameters
[in,out]rfPointer to KS_RF
[in]instancenoInstance of KS_RF to change (INSTRALL changes all instances)
457  {
458  static float chopsign = 1.0;
459 
460  ks_scan_rf_ampscale(rf, instanceno, chopsign);
461 
462  chopsign *= -1.0;
463 
464 }
void ks_scan_rf_ampscale(KS_RF *rf, int instanceno, float ampscale)
Changes the amplitude of one or all instances of an RF pulse (KS_RF)
Definition: KSFoundation_tgt.c:379

◆ ks_scan_rf_off()

void ks_scan_rf_off ( KS_RF rf,
int  instanceno 
)

Sets the amplitude of one or all instances of an RF pulse (KS_RF) to zero

This can be undone by calling ks_scan_rf_on(), ks_scan_rf_on_chop(), or ks_scan_rf_ampscale()

Parameters
[in,out]rfPointer to KS_RF
[in]instancenoInstance of KS_RF to change (INSTRALL changes all instances)
469  {
470 
471  ks_scan_rf_ampscale(rf, instanceno, 0.0);
472 
473 }
void ks_scan_rf_ampscale(KS_RF *rf, int instanceno, float ampscale)
Changes the amplitude of one or all instances of an RF pulse (KS_RF)
Definition: KSFoundation_tgt.c:379

◆ ks_scan_selrf_setfreqphase()

void ks_scan_selrf_setfreqphase ( KS_SELRF selrf,
int  instanceno,
SCAN_INFO  sliceinfo,
float  rfphase 
)

Updates the frequency and phase of one or all instances of a slice selective RF pulse (KS_SELRF)

This function alters the frequency of the RF pulse in a KS_SELRF object to excite a spatial location corresponding to the information in sliceinfo.tloc. The phase of the RF pulse is also updated

Parameters
[in,out]selrfPointer to KS_SELRF
[in]instancenoInstance of KS_RF to change (INSTRALL changes all instances)
[in]sliceinfoSCAN_INFO struct for the current slice to be played out
[in]rfphasePhase of the RF pulse in [degrees]
Return values
STATUSSUCCESS or FAILURE
581  {
582  int firstinstance, lastinstance;
583  int i;
584  float freqoffHz, hertzPerMm, ampscale;
585 #ifdef IPG
586  float tloc = sliceinfo.optloc;
587  int numplaced = selrf->rf.rfwave.base.ngenerated;
588 #else
589  float tloc = 1.0; /* Simulate offset of 1 mm for plotting in units of [Hz/mm] */
590  int numplaced = selrf->rf.rfwave.base.ninst;
591 #endif
592 
593  if (instanceno == INSTRALL) {
594  firstinstance = 0;
595  lastinstance = numplaced - 1;
596  } else {
597  firstinstance = instanceno;
598  lastinstance = instanceno;
599  }
600 
601  if (numplaced == 0) {
602  return;
603  }
604  if (firstinstance < 0 || lastinstance >= numplaced) {
605  ks_error("ks_scan_selrf_setfreqphase(%s): instanceno (%d) out of range [0,%d]", selrf->rf.rfwave.description, instanceno, numplaced - 1);
606  return;
607  }
608 
609  /* set frequency */
610  for (i = firstinstance; i <= lastinstance; i++) {
611 #ifdef IPG
612  if (!(i < selrf->rf.rfwave.base.ngenerated &&
613  selrf->rf.rfwave.wfi[i].wf->wavegen_type == TYPRHO1 &&
614  selrf->rf.rfwave.wfi[i].wf->assoc_pulse->tag == SSPFREQ)) { /* make sure it has been set up properly */
615  ks_error("ks_scan_rf_setfreqphase(%s): RF pulse not properly set up or instance # too high", selrf->rf.rfwave.description);
616  return;
617  }
618  if (selrf->gradwave.res > 0) {
619  /* gradwave */
620  if (selrf->gradwave.wfi != NULL) {
621  ampscale = selrf->gradwave.wfi[i].loc.ampscale;
622  } else {
623  ks_error("ks_scan_rf_setfreqphase(%s): no gradwave has been set up, setting ampscale = 0", selrf->rf.rfwave.description);
624  ampscale = 0;
625  }
626  } else {
627  /* trapezoid */
628  if (selrf->grad.wfi != NULL) {
629  ampscale = selrf->grad.wfi[i].loc.ampscale;
630  } else {
631  ks_error("ks_scan_rf_setfreqphase(%s): no grad has been set up, setting ampscale = 0", selrf->rf.rfwave.description);
632  ampscale = 0; /* protect against wfi[i] = NULL, then return 0 amp */
633  }
634  }
635 #else
636  ampscale = (selrf->gradwave.res > 0) ? selrf->gradwave.locs[i].ampscale
637  : selrf->grad.locs[i].ampscale;
638 #endif
639 
640  /* ampscale (a.u.) * GAM [Hz/G] / 10.0 [cm->mm] * gradamp [G/cm] = [Hz/mm] */
641  hertzPerMm = ampscale * GAM / 10.0 *
642  ((selrf->gradwave.res > 0) ? ks_calc_selgradamp(selrf->rf.bw, selrf->slthick) /* gradwave slice selection */
643  : selrf->grad.amp); /* trapezoid slice selection */
644  freqoffHz = selrf->rf.cf_offset; /* [Hz] */
645 
646  /* optloc-dependent freq offset */
647  if (selrf->rf.omegawave.res > 0) {
648  ks_scan_omegawave_hz(&selrf->rf.omegawave, i, hertzPerMm * tloc /* [Hz] */);
649  } else {
650  freqoffHz += hertzPerMm * tloc; /* [Hz] */
651  }
652 
653 #ifdef IPG
654  /* set RF frequency offset */
655  setfrequency((int)(freqoffHz / TARDIS_FREQ_RES), selrf->rf.rfwave.wfi[i].wf, selrf->rf.rfwave.wfi[i].boardinstance);
656 
657  /* calculate phase */
658  {
659  double ftime_delay; /* floating point time delay in seconds */
660  double temp_freq; /* frequency offset */
661  long tmpphase;
662  int syncpos, rfstartpos, time_delay;
663 
664  /* start of RF pulse wave form (for 1st boardinstance) */
665  rfstartpos = selrf->rf.rfwave.wfi[i].wf->inst_hdr_tail->start;
666  /* start of RF SSP frq pulse (for 1st boardinstance) */
667  syncpos = selrf->rf.rfwave.wfi[i].wf->assoc_pulse->inst_hdr_tail->start;
668 
669  /* time difference between SSP frq and isocenter of RF pulse */
670  time_delay = rfstartpos - syncpos - 9 /* frq2sync_dly */ + RUP_FACTOR(selrf->rf.rfwave.duration - selrf->rf.iso2end, 2);
671 
672  /*** Modified from GE's setupphases() ***/
673  ftime_delay = ((double)time_delay) / ((double) 1.0e6);
674  temp_freq = (double) freqoffHz;
675  tmpphase = (ks_degrees_to_iphase(rfphase) /* deg->iphase */ + ks_cycles_to_iphase( - temp_freq * ftime_delay )) % FS_2PI; /* radians */
676  setiphase(tmpphase, selrf->rf.rfwave.wfi[i].wf, selrf->rf.rfwave.wfi[i].boardinstance);
677  }
678 #endif
679  } /* for each instance */
680 } /* ks_scan_selrf_setfreqphase */
KS_TRAP grad
Definition: KSFoundation.h:1491
int boardinstance
Definition: KSFoundation.h:399
float cf_offset
Definition: KSFoundation.h:942
unsigned int ks_degrees_to_iphase(double degrees)
Definition: KSFoundation_tgt.c:495
KS_WAVE gradwave
Definition: KSFoundation.h:1493
KS_SEQLOC locs[KS_MAXINSTANCES]
Definition: KSFoundation.h:672
STATUS ks_error(const char *format,...) __attribute__((format(printf
Common error message function for HOST and TGT
int ninst
Definition: KSFoundation.h:409
float ampscale
Definition: KSFoundation.h:390
KS_WFINSTANCE * wfi
Definition: KSFoundation.h:591
KS_WFINSTANCE * wfi
Definition: KSFoundation.h:674
KS_SEQLOC loc
Definition: KSFoundation.h:401
KS_RF rf
Definition: KSFoundation.h:1485
KS_DESCRIPTION description
Definition: KSFoundation.h:666
void ks_scan_omegawave_hz(KS_WAVE *wave, int instanceno, float Hz)
Updates a KS_WAVE object on the OMEGA board to produce a frequency offset
Definition: KSFoundation_tgt.c:57
KS_WAVE rfwave
Definition: KSFoundation.h:949
KS_BASE base
Definition: KSFoundation.h:665
int iso2end
Definition: KSFoundation.h:945
KS_SEQLOC locs[KS_MAXINSTANCES]
Definition: KSFoundation.h:589
WF_PULSE * wf
Definition: KSFoundation.h:400
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
unsigned int ks_cycles_to_iphase(double cycles)
Definition: KSFoundation_tgt.c:483
KS_WAVE omegawave
Definition: KSFoundation.h:950
int ngenerated
Definition: KSFoundation.h:410
float GAM
int res
Definition: KSFoundation.h:667
int duration
Definition: KSFoundation.h:668
float slthick
Definition: KSFoundation.h:1487
float bw
Definition: KSFoundation.h:941

◆ ks_scan_selrf_setfreqphase_pins()

void ks_scan_selrf_setfreqphase_pins ( KS_SELRF selrf,
int  instanceno,
SCAN_INFO  sliceinfo,
int  sms_multiband_factor,
float  sms_slice_gap,
float  rfphase 
)

Updates the off-center phase-modulation of one or all instances of a PINS RF pulse (KS_SELRF)

This function alters the phase of the PINS RF pulse in a KS_SELRF object to excite spatial locations corresponding to the information in sliceinfo.tloc.

Parameters
[in,out]selrfPointer to KS_SELRF
[in]instancenoInstance of KS_RF to change (INSTRALL changes all instances)
[in]sliceinfoSCAN_INFO struct for the current slice to be played out
[in]sms_multiband_factor
[in]sms_slice_gapin [mm]
[in]rfphasePhase of the RF pulse in [degrees]
Return values
STATUSSUCCESS or FAILURE
509  {
510 
511  int firstinstance, lastinstance, j;
512  int phaseIndex = 1;
513  KS_WAVE newwave = KS_INIT_WAVE;
514  float locOffset;
515  float phaseIncrement;
516 #ifdef IPG
517  int i;
518  int numplaced = selrf->rf.rfwave.base.ngenerated;
519 #else
520  int numplaced = selrf->rf.rfwave.base.ninst;
521 #endif
522 
523  if (instanceno == INSTRALL) {
524  firstinstance = 0;
525  lastinstance = numplaced - 1;
526  } else {
527  firstinstance = instanceno;
528  lastinstance = instanceno;
529  }
530 
531  if (numplaced == 0) {
532  return;
533  }
534 
535  if (firstinstance < 0 || lastinstance >= numplaced) {
536  ks_error("ks_scan_selrf_setfreqphase_pins(%s): instanceno (%d) out of range [0,%d]", selrf->rf.rfwave.description, instanceno, numplaced - 1);
537  return;
538  }
539 
540  newwave = selrf->rf.thetawave;
541 
542  locOffset = sliceinfo.optloc;
543  phaseIncrement = (360.0 / sms_slice_gap) * locOffset;
544 
545  /* If the MB-factor is even, center the slice group */
546  if (sms_multiband_factor % 2 == 0) {
547  phaseIncrement += 180.0;
548  }
549 
550  for (j = 0; j < selrf->rf.thetawave.res; j++) {
551 
552  if (!areSame(selrf->rf.rfwave.waveform[j], 0.0)) {
553  newwave.waveform[j] = selrf->rf.thetawave.waveform[j] + ((float)phaseIndex * phaseIncrement) + rfphase;
554  }
555 
556  if (j < selrf->rf.thetawave.res - 1) {
557  if (!areSame(selrf->rf.rfwave.waveform[j], 0.0) && areSame(selrf->rf.rfwave.waveform[j + 1], 0.0)) {
558  phaseIndex++;
559  }
560  }
561  }
562 
563 #ifdef IPG
564 
565  for (i = firstinstance; i <= lastinstance; i++) {
566  /* set RF frequency & phase offset to zero */
567  setfrequency(0, selrf->rf.rfwave.wfi[i].wf, selrf->rf.rfwave.wfi[i].boardinstance);
568  setphase(0.0, selrf->rf.rfwave.wfi[i].wf, selrf->rf.rfwave.wfi[i].boardinstance);
569 
570  /* move modified thetawave to hardware */
571  ks_scan_wave2hardware(&selrf->rf.thetawave, newwave.waveform);
572  }
573 
574 #endif
575 
576 } /* ks_scan_selrf_setfreqphase_pins */
void ks_scan_wave2hardware(KS_WAVE *wave, const KS_WAVEFORM newwave)
Moves a waveform belonging to a KS_WAVE sequence object to hardware
Definition: KSFoundation_tgt.c:743
int boardinstance
Definition: KSFoundation.h:399
#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
#define KS_INIT_WAVE
Definition: KSFoundation.h:227
STATUS ks_error(const char *format,...) __attribute__((format(printf
Common error message function for HOST and TGT
int ninst
Definition: KSFoundation.h:409
KS_WFINSTANCE * wfi
Definition: KSFoundation.h:674
KS_WAVE thetawave
Definition: KSFoundation.h:951
KS_RF rf
Definition: KSFoundation.h:1485
KS_DESCRIPTION description
Definition: KSFoundation.h:666
KS_WAVE rfwave
Definition: KSFoundation.h:949
KS_BASE base
Definition: KSFoundation.h:665
WF_PULSE * wf
Definition: KSFoundation.h:400
int ngenerated
Definition: KSFoundation.h:410
int res
Definition: KSFoundation.h:667
KS_WAVEFORM waveform
Definition: KSFoundation.h:669

◆ ks_scan_rf_setphase()

void ks_scan_rf_setphase ( KS_RF rf,
int  instanceno,
float  rfphase 
)

Updates the phase of one or all instances of an RF pulse (KS_RF)

This function sets the phase of an RF pulse object (KS_RF). Can be used for RF spoiling

Parameters
[in,out]rfPointer to KS_SEL
[in]instancenoInstance of KS_RF to change (INSTRALL changes all instances)
[in]rfphasePhase of the RF pulse in [degrees]
685  {
686  int firstinstance, lastinstance;
687  #ifdef IPG
688  int i;
689  int numplaced = rf->rfwave.base.ngenerated;
690  #else
691  int numplaced = rf->rfwave.base.ninst;
692  #endif
693 
694  if (instanceno == INSTRALL) {
695  firstinstance = 0;
696  lastinstance = numplaced - 1;
697  } else {
698  firstinstance = instanceno;
699  lastinstance = instanceno;
700  }
701 
702  if (numplaced == 0) {
703  return;
704  }
705  if (firstinstance < 0 || lastinstance >= numplaced) {
706  ks_error("%s(%s): instanceno (%d) out of range [0,%d]", __FUNCTION__, rf->rfwave.description, instanceno, numplaced - 1);
707  return;
708  }
709 
710  #ifdef IPG
711  /* set frequency */
712  for (i = firstinstance; i <= lastinstance; i++) {
713  /* calculate phase */
714  {
715  double ftime_delay; /* floating point time delay in seconds */
716  double cf_offset = (double) rf->cf_offset; /* frequency offset */
717  long tmpphase;
718  int syncpos, rfstartpos, time_delay;
719 
720  /* start of RF pulse wave form (for 1st boardinstance) */
721  rfstartpos = rf->rfwave.wfi[i].wf->inst_hdr_tail->start;
722  /* start of RF SSP frq pulse (for 1st boardinstance) */
723  syncpos = rf->rfwave.wfi[i].wf->assoc_pulse->inst_hdr_tail->start;
724 
725  /* time difference between SSP frq and isocenter of RF pulse */
726  time_delay = rfstartpos - syncpos - 9 /* frq2sync_dly */ + RUP_FACTOR(rf->rfwave.duration - rf->iso2end, 2);
727 
728  /*** Modified from GE's setupphases() ***/
729  ftime_delay = ((double)time_delay) / ((double) 1.0e6);
730  tmpphase = (ks_degrees_to_iphase(rfphase) /* deg->iphase */ + ks_cycles_to_iphase( - cf_offset * ftime_delay )) % FS_2PI; /* radians */
731  setiphase(tmpphase, rf->rfwave.wfi[i].wf, rf->rfwave.wfi[i].boardinstance);
732  }
733 
734  } /* for each instance */
735 
736  #endif
737 
738 
739 } /* ks_scan_rf_setphase */
int boardinstance
Definition: KSFoundation.h:399
float cf_offset
Definition: KSFoundation.h:942
unsigned int ks_degrees_to_iphase(double degrees)
Definition: KSFoundation_tgt.c:495
STATUS ks_error(const char *format,...) __attribute__((format(printf
Common error message function for HOST and TGT
int ninst
Definition: KSFoundation.h:409
KS_WFINSTANCE * wfi
Definition: KSFoundation.h:674
KS_DESCRIPTION description
Definition: KSFoundation.h:666
KS_WAVE rfwave
Definition: KSFoundation.h:949
KS_BASE base
Definition: KSFoundation.h:665
int iso2end
Definition: KSFoundation.h:945
WF_PULSE * wf
Definition: KSFoundation.h:400
unsigned int ks_cycles_to_iphase(double cycles)
Definition: KSFoundation_tgt.c:483
int ngenerated
Definition: KSFoundation.h:410
int duration
Definition: KSFoundation.h:668

◆ ks_scan_wave2hardware()

void ks_scan_wave2hardware ( KS_WAVE wave,
const KS_WAVEFORM  newwave 
)

Moves a waveform belonging to a KS_WAVE sequence object to hardware

This function copies a waveform to one of the two hardware waveform buffers (belonging to the KS_WAVE object) that is currently not in use for the current sequence playout. The selection of which buffer to update is handled automatically by the function, which will also change the waveform pointer to the updated buffer so it will be used for the next sequence playout

If the 2nd argument (newwave, of type KS_WAVEFORM (float array)) is

  1. not NULL, the content of newwave is used
  2. NULL, it is assumed that the .waveform field of KS_WAVE has been updated with new contents and is copied to hardware instead of newwave
Parameters
[in,out]wavePointer to KS_WAVE
[in]newwaveKS_WAVEFORM to copy to hardware (if NULL, the .waveform field in KS_WAVE will be used instead)
743  {
744 #ifdef IPG
745  KS_IWAVE iwave;
746  WF_PULSE *wfpulse;
747  LONG current_wave_ptrs[KS_WF_SIZE];
748  LONG wave_ptr;
749  int i,j;
750  STATUS status;
751  int numplaced = wave->base.ngenerated;
752 #else
753  int numplaced = wave->base.ninst;
754 #endif
755 
756  if (numplaced == 0) {
757  return;
758  }
759 
760 #ifdef IPG
761 
762  for (i = 0; i < 7; i++) {
763 
764  wfpulse = wave->wfpulse[i];
765 
766  if (!wfpulse) {
767  continue;
768  }
769 
770  if (newwave != NULL) {
771  /* use the new float array passed in as 3rd argument */
772  status = ks_waveform2iwave(iwave, newwave, wave->res, i);
773  } else {
774  /* use the data in the `.waveform` field of the KS_WAVE (1st arg) */
775  status = ks_wave2iwave(iwave, wave, i); /* iwave is waveform memory */
776  }
777 
778  /* Extract current wave pointers */
779  for (j = KS_WF_MAIN; j < KS_WF_SIZE; j++) {
780  getwave(&current_wave_ptrs[j], &wfpulse[j]);
781  }
782 
783  /* Compare the main pointer with the buffers and find wich buffer was used
784  last time, then move the new waveform to the other one. Also, get the new pointer.
785  movewaveimm might take some time to compute if the wave is long, consider increasing
786  the SSI-time if you expericence any problems */
787  if (current_wave_ptrs[KS_WF_MAIN] == current_wave_ptrs[KS_WF_BUF2]) {
788  movewaveimm(iwave, &wfpulse[KS_WF_BUF1], (int) 0, wave->res, TOHARDWARE);
789  getwave(&wave_ptr, &wfpulse[KS_WF_BUF1]);
790  } else {
791  movewaveimm(iwave, &wfpulse[KS_WF_BUF2], (int) 0, wave->res, TOHARDWARE);
792  getwave(&wave_ptr, &wfpulse[KS_WF_BUF2]);
793  }
794 
795  /* Make the new pointer the main one */
796  setwave(wave_ptr, &wfpulse[KS_WF_MAIN], -1);
797 
798  } /* for */
799 
800 #endif
801 
802 } /* ks_scan_wave2hardware */
short KS_IWAVE[KS_MAXWAVELEN]
Definition: KSFoundation.h:263
Definition: KSFoundation.h:1957
int ninst
Definition: KSFoundation.h:409
WF_PULSE ** wfpulse
Definition: KSFoundation.h:673
KS_BASE base
Definition: KSFoundation.h:665
STATUS ks_waveform2iwave(KS_IWAVE iwave, const KS_WAVEFORM waveform, int res, int board) WARN_UNUSED_RESULT
(Internal use) Conversion of a KS_WAVEFORM to a short int array for use on hardware
Definition: KSFoundation_common.c:1418
Definition: KSFoundation.h:1957
int ngenerated
Definition: KSFoundation.h:410
Definition: KSFoundation.h:1957
STATUS ks_wave2iwave(KS_IWAVE iwave, const KS_WAVE *wave, int board) WARN_UNUSED_RESULT
(Internal use) Conversion of waveform content in a KS_WAVE sequence object to a short int array for u...
Definition: KSFoundation_common.c:1440
int res
Definition: KSFoundation.h:667
Definition: KSFoundation.h:1957

◆ ks_scan_offsetfov_iso()

void ks_scan_offsetfov_iso ( KS_READTRAP readtrap,
int  instanceno,
SCAN_INFO  sliceinfo,
double  ky,
double  kz,
double  rcvphase 
)

Updates the frequency and phase to create a FOV shift assuming that the k-space voxels/pixels are isometric

Parameters
[in,out]readtrapPointer to KS_READTRAP
[in]instancenoInstance of KS_RF to change (INSTRALL changes all instances)
[in]sliceinfoSCAN_INFO struct for the current slice to be played out
[in]kyphase offset in k-space measured in voxels/pixels
[in]kzphase offset in k-space measured in voxels/pixels
[in]rcvphaseReceiver phase in [degrees] of the acquisition window corresponding to the KS_READTRAP and instanceno
806  {
807  /* Warning: We never have more than one instance (i.e. instruction) per readtrap.echo[i] (see ks_pg_readtrap())
808  unlike KS_TRAP and KS_RF. This means that the 2nd input arg here ('instanceno') means the instanceno'th echo. */
809 
810 #ifdef IPG
811  const int numplaced = readtrap->acq.base.ngenerated;
812 #else
813  const int numplaced = readtrap->acq.base.ninst;
814 #endif
815 
816  int firstreadout, lastreadout;
817  if (instanceno == INSTRALL) {
818  firstreadout = 0;
819  lastreadout = numplaced - 1;
820  } else {
821  firstreadout = instanceno;
822  lastreadout = instanceno;
823  }
824 
825  if (numplaced == 0) {
826  return;
827  }
828 
829  if (firstreadout < 0 || lastreadout >= numplaced) {
830  ks_error("ks_scan_offsetfov(%s): readout instance (%d) out of range [0,%d]", readtrap->grad.description, instanceno, numplaced - 1);
831  return;
832  }
833 
834  int i;
835  for (i = firstreadout; i <= lastreadout; i++) {
836 #ifdef IPG
837  /* gradient amplitude for this instance -> Hz */
838  const double mmtoHz = readtrap->grad.amp * readtrap->grad.wfi[i].loc.ampscale * GAM / 10.0; /* [Hz/mm]. designed amp [G/cm] * instance amp * GAM [Hz/G] / 10.0 [cm->mm] */
839  float rloc = sliceinfo.oprloc;
840 #else
841  const double mmtoHz = readtrap->grad.amp * readtrap->grad.locs[i].ampscale * GAM / 10.0;
842  float rloc = 1;
843 #endif
844 
845 
846  /* omega offset for rampsampled cases */
847  if (readtrap->omega.duration > 0) {
848  ks_scan_omegatrap_hz(&readtrap->omega, i, mmtoHz * rloc /* [Hz] */);
849  }
850 
851 #ifdef IPG
852 
853  const int gradsign = (mmtoHz >= 0) ? 1 : -1;
854  /* Frequency offsets [Hz]:
855  - cfreceiveroffsetfreq: GE's standard frequency offset
856  - readtrap->freqoffHz: Additional frequency offset for current KS_READTRAP. This value is multiplied with the gradient polarity sign
857  - mmtoHz * sliceinfo.oprloc: Slice-dependent and gradient polarity dependent frequency offset (when omega is off) */
858  const double freqoffHz = cfreceiveroffsetfreq + readtrap->freqoffHz * gradsign + (readtrap->omega.duration == 0) * (mmtoHz * rloc); /* [Hz] */
859 
860  /* set readout frequency offset */
861  if (i < readtrap->acq.base.ngenerated && readtrap->acq.echo[i].tag == SSPDAB) { /* make sure it has been set up properly */
862  setfrequency((int)(freqoffHz / TARDIS_FREQ_RES), &readtrap->acq.echo[i], 0);
863  }
864  /* set phase offset */
865  if (i < readtrap->acq.base.ngenerated && readtrap->acq.echo[i].tag == SSPDAB) {
866 
867  const double time2echo = (double)(readtrap->time2center - readtrap->acqdelay);
868  /* The clock source at setfrequency() starts before the first readout of the ADC, we need to account for the phase that accunmulates during this time.
869  acqq.cpp - the RBA packet [ADC sync time?] has a fixed delay of RBA_length[PSD_XCVR2] which is set in EpicConf.cpp to RBA_LENGTH (= 4)
870  It seems that all known hardware uses PSD_XCVR2 as the index and RBA_length isn't included in the scope of this file.*/
871  int adcstartpos = readtrap->acq.echo[i].assoc_pulse->assoc_pulse->inst_hdr_tail->start + RBA_LENGTH;
872  /* start of ADC SSP frq pulse (for 1st boardinstance) */
873  int syncpos = readtrap->acq.echo[i].assoc_pulse->inst_hdr_tail->start;
874  /* time difference between SSP frq and start of the ADC readout */
875  int time_delay = adcstartpos - syncpos - 10;
876 
877  const unsigned int iphase_due_to_freqoffset = ks_cycles_to_iphase(- freqoffHz * ((double)time_delay + time2echo) * 1e-6);
878  const unsigned int iphase_ky = ks_cycles_to_iphase(- ky * sliceinfo.opphasoff / readtrap->fov);
879  const unsigned int iphase_kz = ks_cycles_to_iphase(- kz * sliceinfo.optloc / readtrap->fov);
880  const unsigned int ircvphase = ks_degrees_to_iphase(rcvphase);
881  long iphase = (iphase_ky + iphase_kz + ircvphase + iphase_due_to_freqoffset) % FS_2PI;
882  setiphase(iphase, &readtrap->acq.echo[i], 0);
883 
884  }
885 #endif
886 
887  } /* for */
888 } /* ks_scan_offsetfov_iso */
WF_PULSE * echo
Definition: KSFoundation.h:738
void ks_scan_omegatrap_hz(KS_TRAP *trp, int instanceno, float Hz)
Updates a KS_TRAP object on the OMEGA board to produce a frequency offset
Definition: KSFoundation_tgt.c:105
unsigned int ks_degrees_to_iphase(double degrees)
Definition: KSFoundation_tgt.c:495
STATUS ks_error(const char *format,...) __attribute__((format(printf
Common error message function for HOST and TGT
int ninst
Definition: KSFoundation.h:409
float fov
Definition: KSFoundation.h:1576
int cfreceiveroffsetfreq
KS_TRAP grad
Definition: KSFoundation.h:1586
float ampscale
Definition: KSFoundation.h:390
KS_BASE base
Definition: KSFoundation.h:731
KS_WFINSTANCE * wfi
Definition: KSFoundation.h:591
KS_SEQLOC loc
Definition: KSFoundation.h:401
KS_TRAP omega
Definition: KSFoundation.h:1587
float amp
Definition: KSFoundation.h:581
KS_SEQLOC locs[KS_MAXINSTANCES]
Definition: KSFoundation.h:589
KS_DESCRIPTION description
Definition: KSFoundation.h:580
unsigned int ks_cycles_to_iphase(double cycles)
Definition: KSFoundation_tgt.c:483
float freqoffHz
Definition: KSFoundation.h:1582
int ngenerated
Definition: KSFoundation.h:410
KS_READ acq
Definition: KSFoundation.h:1574
int duration
Definition: KSFoundation.h:585
float GAM
int time2center
Definition: KSFoundation.h:1585
int acqdelay
Definition: KSFoundation.h:1580

◆ ks_scan_offsetfov()

void ks_scan_offsetfov ( KS_READTRAP readtrap,
int  instanceno,
SCAN_INFO  sliceinfo,
float  view,
float  phasefovratio,
float  rcvphase 
)

Updates the frequency and phase of one or all instances of a KS_READTRAP to create a FOV shift

This function can be used by 2D Cartesian pulse sequences to build up the phase ramp in k-space necessary to shift the image FOV. The desired image FOV shift is given by the SCAN_INFO struct passed in as 3rd argument. The view field and the phasefovratio arguments are necessary to know where in the physical k-space the acquired data is placed. Knowing this and the FOV offset in the phase encoding direction, the necessary receiver phase for the current view can be set. After this function has been called for all view numbers, the necessary phase ramp has been set up in k-space to perform the phase FOV shift in the image domain. The rcvphase is added to the receive phase required for the FOV shift. In general, the rcvphase should be the same as the phase of the RF excitation pulse.

For rampsampled acquisitions (.rampsampling = 1 in KS_READTRAP), the ks_scan_offsetfov_iso() function called will internally call ks_scan_omegatrap_hz() for FOV shifts in the readout direction. Both ks_scan_offsetfov() and ks_scan_offsetfov3D() calls the same ks_scan_offsetfov_iso() function after performing unit conversions of the input arguments.

Parameters
[in,out]readtrapPointer to KS_READTRAP
[in]instancenoInstance of KS_RF to change (INSTRALL changes all instances)
[in]sliceinfoSCAN_INFO struct for the current slice to be played out
[in]viewPhase encoding line to acquire with index corresponding to a fully sampled k-space [0, res-1]
[in]phasefovratioThe ratio of FOVphase/FOVfreq (as opphasefov)
[in]rcvphaseReceiver phase in [degrees] of the acquisition window corresponding to the KS_READTRAP and instanceno
968  {
969  const double koffset = (double)(ky) / phasefovratio;
970  ks_scan_offsetfov_iso(readtrap, instanceno, sliceinfo, koffset, 0, rcvphase);
971 }
void ks_scan_offsetfov_iso(KS_READTRAP *readtrap, int instanceno, SCAN_INFO sliceinfo, double ky, double kz, double rcvphase)
Updates the frequency and phase to create a FOV shift assuming that the k-space voxels/pixels are iso...
Definition: KSFoundation_tgt.c:806

◆ ks_scan_offsetfov3D()

void ks_scan_offsetfov3D ( KS_READTRAP readtrap,
int  instanceno,
SCAN_INFO  sliceinfo,
float  kyview,
float  phasefovratio,
float  kzview,
float  zphasefovratio,
float  rcvphase 
)

Updates the frequency and phase of one or all instances of a KS_READTRAP to create a FOV shift

This function can be used by 3D Cartesian pulse sequences to build up the phase ramp in k-space necessary to shift the image FOV. The desired image FOV shift is given by the SCAN_INFO struct passed in as 3rd argument. The kyview/kzview field and the phasefovratio/zphasefovratio arguments are necessary to know where in the physical k-space the acquired data is placed. Knowing this and the FOV offset in both phase encoding directions, the necessary receiver phase for the current kyview/kzview can be set. After this function has been called for all kyview/kzview numbers, the necessary phase ramp has been set up in k-space to perform the phase FOV shift in the image domain. The rcvphase is added to the receive phase required for the FOV shift. In general, the rcvphase should be the same as the phase of the RF excitation pulse.

For rampsampled acquisitions (.rampsampling = 1 in KS_READTRAP), the ks_scan_offsetfov_iso() function called will internally call ks_scan_omegatrap_hz() for FOV shifts in the readout direction. Both ks_scan_offsetfov() and ks_scan_offsetfov3D() calls the same ks_scan_offsetfov_iso() function after performing unit conversions of the input arguments.

Parameters
[in,out]readtrapPointer to KS_READTRAP
[in]instancenoInstance of KS_RF to change (INSTRALL changes all instances)
[in]sliceinfoSCAN_INFO struct for the current slice to be played out
[in]kyviewPhase encoding line to acquire with index corresponding to a fully sampled k-space [0, res-1] (YGRAD)
[in]phasefovratioThe ratio of FOVphase/FOVfreq (as opphasefov)
[in]kzviewZ Phase encoding line to acquire with index corresponding to a fully sampled k-space [0, res-1] (ZGRAD)
[in]zphasefovratioThe ratio of FOVslice/FOVfreq (as (opslquant * opslthick) / opfov)
[in]rcvphaseReceiver phase in [degrees] of the acquisition window corresponding to the KS_READTRAP and instanceno
974  {
975  const double kyoffset = (double)(ky) / phasefovratio;
976  const double kzoffset = (double)(kz) / zphasefovratio;
977  ks_scan_offsetfov_iso(readtrap, instanceno, sliceinfo, kyoffset, kzoffset, rcvphase);
978 }
void ks_scan_offsetfov_iso(KS_READTRAP *readtrap, int instanceno, SCAN_INFO sliceinfo, double ky, double kz, double rcvphase)
Updates the frequency and phase to create a FOV shift assuming that the k-space voxels/pixels are iso...
Definition: KSFoundation_tgt.c:806

◆ ks_scan_offsetfov_readwave()

void ks_scan_offsetfov_readwave ( KS_READWAVE readwave,
int  instanceno,
SCAN_INFO  sliceinfo,
float  kyview,
float  phasefovratio,
float  rcvphase 
)
981  {
982  const double koffset = (double)(ky) / phasefovratio;
983  ks_scan_offsetfov_iso_readwave(readwave, instanceno, sliceinfo, koffset, 0, rcvphase);
984 }
void ks_scan_offsetfov_iso_readwave(KS_READWAVE *readwave, int instanceno, SCAN_INFO sliceinfo, double ky, double kz, double rcvphase)
Definition: KSFoundation_tgt.c:891

◆ ks_scan_omegatrap_hz()

void ks_scan_omegatrap_hz ( KS_TRAP trap,
int  instanceno,
float  Hz 
)

Updates a KS_TRAP object on the OMEGA board to produce a frequency offset

A KS_TRAP object on OMEGA is used for rampsampled acquisitions and is placed out using ks_pg_readtrap() for a KS_READTRAP object if the field .rampsampling = 1. This will cause a trapezoidal frequency shift during the readout instead of a fixed frequency offset when rampsampling is not used. An error is returned if the current instance of the KS_TRAP is on another board than OMEGA

For Cartesian applications, there is no need to call this function directly, since ks_scan_offsetfov() already calls this function

Special note for OMEGA: Neither the .amp field nor the .ampscale field passed in to ks_pg_trap() has an effect. Only the Hz argument controls the frequency shift

Parameters
[in,out]trapPointer to KS_TRAP
[in]instancenoInstance of KS_RF to change (INSTRALL changes all instances)
[in]HzDesired frequency offset in [Hz] at the plateau of the trapezoid
Return values
STATUSSUCCESS or FAILURE
105  {
106  int iamp;
107  float omega_scale = 256.0;
108 
109  iamp = (int) (Hz / (TARDIS_FREQ_RES * omega_scale));
110  if (abs(iamp) > 32768) {
111  ks_error("ks_scan_omegatrap_hz(%s): the integer amplitude is too large (%d) for instance %d", trp->description, iamp, instanceno);
112  return;
113  }
114 
115 #ifdef IPG
116 
117  if (trp->wfi[instanceno].loc.board != OMEGA) {
118  ks_error("ks_scan_omegatrap_hz(%s): Wrong function - use ks_scan_trap_ampscale() KS_TRAPs on XGRAD, YGRAD, ZGRAD", trp->description);
119  return;
120  }
121 
122  /* input validation */
123  if (instanceno < 0 || instanceno >= trp->base.ngenerated) {
124  ks_error("ks_scan_omegatrap_hz(%s): instanceno (%d) out of range [0,%d)", trp->description, instanceno, trp->base.ngenerated);
125  return;
126  }
127 
128  if (setiampt((short) iamp, trp->wfi[instanceno].wf, trp->wfi[instanceno].boardinstance) != SUCCESS) {
129  ks_error("ks_scan_omegatrap_hz(%s): An error occurred calling 'setiampt()'", trp->description);
130  return;
131  }
132 
133 #else
134  int numplaced = trp->base.ninst;
135  if (!numplaced) {
136  return;
137  }
138  float* tmp = (float*)realloc(trp->rtscale, numplaced * sizeof(float));
139  if (!tmp) {
140  ks_error("%s: rtscale realloc failed", __FUNCTION__);
141  return;
142  }
143  trp->rtscale = tmp;
144  trp->rtscale[instanceno] = Hz;
145 #endif
146 
147 } /* ks_scan_omegatrap_hz */
STATUS ks_error(const char *format,...) __attribute__((format(printf
Common error message function for HOST and TGT

◆ ks_scan_omegawave_hz()

void ks_scan_omegawave_hz ( KS_WAVE wave,
int  instanceno,
float  Hz 
)

Updates a KS_WAVE object on the OMEGA board to produce a frequency offset

A KS_WAVE object on OMEGA can be used to perform a dynamic frequency offset

  1. while an RF pulse is played out. One example is Spectral-Spatial RF pulses
  2. during data acquisition
Parameters
[in,out]wavePointer to KS_WAVE
[in]instancenoInstance of KS_RF to change (INSTRALL changes all instances)
[in]HzDesired frequency offset in [Hz] for the point in the KS_WAVE with the largest absolute amplitude
Return values
STATUSSUCCESS or FAILURE
57  {
58  int iamp;
59  float omega_scale = 256.0;
60 
61  iamp = (int) (Hz / (TARDIS_FREQ_RES * omega_scale));
62 
63  if (abs(iamp) > 32768) {
64  ks_error("ks_scan_omegawave_hz(%s): the integer amplitude is too large (%d) for instance %d", wave->description, iamp, instanceno);
65  return;
66  }
67 
68 #ifdef IPG
69 
70  if (wave->wfi[instanceno].loc.board != OMEGA) {
71  ks_error("ks_scan_omegawave_hz(%s): Only omega board is supported", wave->description);
72  return;
73  }
74 
75  /* input validation */
76  if (instanceno < 0 || instanceno >= wave->base.ngenerated) {
77  ks_error("ks_scan_omegawave_hz(%s): instanceno (%d) out of range [0,%d)", wave->description, instanceno, wave->base.ngenerated);
78  return;
79  }
80 
81  if (setiamp((short) iamp, wave->wfi[instanceno].wf, wave->wfi[instanceno].boardinstance) != SUCCESS) {
82  ks_error("ks_scan_omegawave_hz(%s): An error occurred calling 'setiampt()'", wave->description);
83  return;
84  }
85 
86 #else
87  int numplaced = wave->base.ninst;
88  if (!numplaced) {
89  return;
90  }
91  float* tmp = (float*)realloc(wave->rtscale, numplaced * sizeof(float));
92  if (!tmp) {
93  ks_error("%s: rtscale realloc failed", __FUNCTION__);
94  return;
95  }
96  wave->rtscale = tmp;
97  wave->rtscale[instanceno] = Hz;
98 
99 #endif
100 
101 } /* ks_scan_omegawave_hz */
float * rtscale
Definition: KSFoundation.h:675
int boardinstance
Definition: KSFoundation.h:399
STATUS ks_error(const char *format,...) __attribute__((format(printf
Common error message function for HOST and TGT
int ninst
Definition: KSFoundation.h:409
int board
Definition: KSFoundation.h:388
KS_WFINSTANCE * wfi
Definition: KSFoundation.h:674
KS_SEQLOC loc
Definition: KSFoundation.h:401
KS_DESCRIPTION description
Definition: KSFoundation.h:666
KS_BASE base
Definition: KSFoundation.h:665
WF_PULSE * wf
Definition: KSFoundation.h:400
int ngenerated
Definition: KSFoundation.h:410

◆ ks_scan_wait()

void ks_scan_wait ( KS_WAIT wait,
int  waitperiod 
)

Updates the wait period of all instances of a KS_WAIT sequence object. The value of waitperiod must not exceed

.duration.

Parameters
[in,out]waitPointer to KS_WAIT
[in]waitperiodTime in [us] to wait
Return values
STATUSSUCCESS or FAILURE
151  {
152 
153 #ifdef IPG
154  int i;
155  int numplaced = wait->base.ngenerated;
156 #else
157  int numplaced = wait->base.ninst;
158 #endif
159 
160  if (numplaced == 0) {
161  return;
162  }
163 
164  /* input validation */
165  if (waitperiod < 0) {
166  ks_error("ks_scan_wait(%s): wait period %d is negative", wait->description, waitperiod);
167  return;
168  }
169  if (waitperiod < GRAD_UPDATE_TIME) {
170  waitperiod = GRAD_UPDATE_TIME;
171  }
172  if (waitperiod % GRAD_UPDATE_TIME) {
173  ks_error("ks_scan_wait(%s): waitperiod (3rd arg) on gradient boards must be divisible by 4", wait->description);
174  return;
175  }
176 
177 #ifdef IPG
178 
179  if (wait->wfi == NULL) {
180  ks_error("ks_scan_wait(%s): WF_INSTANCE of trap is NULL", wait->description);
181  }
182 
183  /* set amplitude(s) */
184  for (i = 0; i < numplaced; i++) {
185 
186  if (setperiod(waitperiod, wait->wfi[i].wf, wait->wfi[i].boardinstance) != SUCCESS) {
187  ks_error("ks_scan_wait(%s): An error occurred calling 'setperiod()'", wait->description);
188  return;
189  }
190  }
191 
192 #endif
193 
194 } /* ks_scan_wait */
int boardinstance
Definition: KSFoundation.h:399
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
KS_BASE base
Definition: KSFoundation.h:457
KS_WFINSTANCE * wfi
Definition: KSFoundation.h:462
WF_PULSE * wf
Definition: KSFoundation.h:400
int ngenerated
Definition: KSFoundation.h:410

◆ ks_scan_trap_ampscale()

void ks_scan_trap_ampscale ( KS_TRAP trap,
int  instanceno,
float  ampscale 
)

Updates the amplitude of one or all instances of a KS_TRAP sequence object

This function multiplies one instance of a KS_TRAP object with an amplitude scale factor (3rd arg) that should be in range [-1.0,1.0] to avoid slewrate issues. To change all instances of a KS_TRAP object, use INSTRALL as the 2nd argument.

The actual amplitude for an instance of a KS_TRAP object is the multiplication of the three factors:

  1. The designed amplitude [G/cm]
  2. The per-instance .ampscale in the KS_SEQLOC struct passed to ks_pg_trap()
  3. The ampscale value passed in as 3rd argument to this function.
Parameters
[in,out]trapPointer to KS_TRAP
[in]instancenoInstance of KS_TRAP to change (INSTRALL changes all instances)
[in]ampscaleGradient amplitude scale factor, normally in range [-1.0,1.0], but the range [-1.2, 1.2] is allowed to allow certain run-time gradient corrections
Return values
STATUSSUCCESS or FAILURE
199  {
200  int firstinstance, lastinstance, i;
201 #ifdef IPG
202  int iamp;
203  float gradmax;
204  int numplaced = trp->base.ngenerated;
205 #else
206  int numplaced = trp->base.ninst;
207  if (!numplaced) {
208  return;
209  }
210  float* tmp = (float*)realloc(trp->rtscale, numplaced * sizeof(float));
211  if (!tmp) {
212  ks_error("%s: rtscale realloc failed", __FUNCTION__);
213  return;
214  }
215  trp->rtscale = tmp;
216 #endif
217 
218  if (instanceno == INSTRALL) {
219  firstinstance = 0;
220  lastinstance = numplaced - 1;
221  } else {
222  firstinstance = instanceno;
223  lastinstance = instanceno;
224  }
225 
226  /* input validation */
227  if (numplaced == 0) {
228  return;
229  }
230  if (firstinstance < 0 || lastinstance >= numplaced) {
231  ks_error("ks_scan_trap_ampscale(%s): instanceno (%d) out of range [0,%d]", trp->description, instanceno, numplaced - 1);
232  return;
233  }
234 
235  /* Allow ampscale to be 20% larger than 1. This, to allow future minor runtime changes of trapezoid amplitudes
236  for e.g. (future possible implementations):
237  - oblique ghost correction for EPI, where odd/even blip amps need to be made smaller/larger depending on slice angle and delays
238  - eddy current correction for DW-EPI, where readout lobe amp needs to be changed per diffusion direction etc.
239  If 'ampscale' is larger than 1.0 (i.e. between 1.0 and 1.2), the trapezoid must have been designed with a slewrate that is slightly
240  under the very limit of the system (and the dB/dt limits for PNS). Use with caution. */
241 
242  if (fabs(ampscale) > sqrt(3)) {
243  ks_error("ks_scan_trap_ampscale(%s): ampscale too large (%f)", trp->description, ampscale);
244  return;
245  }
246 
247  /*
248  - trp->amp: The amplitude that was initially designed for the KS_TRAP. Should never be exceeded, or slew rate issues will occur
249  - trp->wfi[i].loc.ampscale: -1.0 -> +1.0. Amp scale factor for each instance of the KS_TRAP placed in the sequence.
250  - ampscale (3rd input arg): -1.0 -> +1.0 (in normal cases). The dynamic amp scale (i.e. over time, or TRs).
251  The product [trp->wfi[i].loc.ampscale * ampscale] should be in range [-1, 1] to avoid slew rate issues.
252  */
253 
254 #ifdef IPG
255 
256  if (trp->wfi == NULL) {
257  ks_error("ks_scan_trap_ampscale(%s): WF_INSTANCE of trap is NULL", trp->description);
258  return;
259  }
260 
261  /* set amplitude(s) */
262  for (i = firstinstance; i <= lastinstance; i++) {
263  gradmax = ks_syslimits_gradtarget(loggrd, trp->wfi[i].loc.board);
264  iamp = (int)((ampscale * trp->wfi[i].loc.ampscale * trp->amp / gradmax) * MAX_PG_IAMP);
265 
266  if (abs(iamp) > 32768) {
267  ks_error("ks_scan_trap_ampscale(%s): the integer amplitude is too large (%d) for instance %d", trp->description, iamp, i);
268  return;
269  }
270  if (trp->wfi[i].loc.board == OMEGA) {
271  ks_error("ks_scan_trap_ampscale(%s): Instance %d on board OMEGA. Please use ks_scan_omegatrap_hz() instead", trp->description, i);
272  return;
273  }
274  if (setiampt((short) iamp, trp->wfi[i].wf, trp->wfi[i].boardinstance) != SUCCESS) {
275  ks_error("ks_scan_trap_ampscale(%s): An error occurred calling 'setiampt()'", trp->description);
276  return;
277  }
278 
279  }
280 
281 #else
282  for (i = firstinstance; i <= lastinstance; i++) {
283  trp->rtscale[i] = ampscale;
284  }
285 #endif
286 } /* ks_scan_trap_ampscale */
float ks_syslimits_gradtarget(LOG_GRAD loggrd, int board)
Returns the maximum target amplitude for a board (internal use)
Definition: KSFoundation_common.c:220
STATUS ks_error(const char *format,...) __attribute__((format(printf
Common error message function for HOST and TGT
LOG_GRAD loggrd

◆ ks_scan_trap_ampscale_slice()

void ks_scan_trap_ampscale_slice ( KS_TRAP trap,
int  start,
int  skip,
int  count,
float  ampscale 
)

Updates the amplitude of a selection of instances of a KS_TRAP sequence object

This function multiplies some instances of a KS_TRAP object with an amplitude scale factor (5th arg) that should be in range [-1.0,1.0] to avoid slewrate issues.

The instances to change are start + i * skip, where i goes from 0 to count. FAILURE is returned if either start or start + count * skip out of range or count is negative

Parameters
[in,out]trapPointer to KS_TRAP
[in]startFirst instance number of the KS_TRAP
[in]skipDifference in instance number between consecutive instances
[in]countNumber of instances to change in total
[in]ampscaleGradient amplitude scale factor, normally in range [-1.0,1.0], but the range [-1.2, 1.2] is allowed to allow certain run-time gradient corrections
Return values
STATUSSUCCESS or FAILURE
290  {
291  int i;
292 #ifdef IPG
293  int numplaced = trp->base.ngenerated;
294 #else
295  int numplaced = trp->base.ninst;
296 #endif
297 
298  if (numplaced == 0) {
299  return;
300  }
301 
302  /* check range */
303  const int end = start + (count - 1) * skip;
304 
305  if (start < 0 || start >= numplaced || end < 0 || end >= numplaced || count < 0) {
306  ks_error("ks_scan_trap_ampscale_slice(%s): slice with start = %d, skip = %d and count = %d has endpoints outside [0,%d)", trp->description, start, skip, count, numplaced);
307  return;
308  }
309 
310  for (i = 0; i < count; ++i) {
311  ks_scan_trap_ampscale(trp, start + i * skip, ampscale);
312  }
313 
314 } /* ks_scan_trap_ampscale_slice */
STATUS ks_error(const char *format,...) __attribute__((format(printf
Common error message function for HOST and TGT
void ks_scan_trap_ampscale(KS_TRAP *trp, int instanceno, float ampscale)
Updates the amplitude of one or all instances of a KS_TRAP sequence object
Definition: KSFoundation_tgt.c:199

◆ ks_scan_phaser_kmove()

void ks_scan_phaser_kmove ( KS_PHASER phaser,
int  instanceno,
double  pixelunits 
)

Updates the amplitude of a KS_PHASER sequence object to move some arbitrary distance in k-space

This function sets the amplitude of the gradient for a KS_PHASER object so that a shift in k-space of pixelunits number of pixels is produced. If pixelunits is 1.0, the gradient area corresponds to one pixel shift in a fully sampled k-space (no parallel imaging) along the board the KS_PHASER object is placed on. pixelunits is of type float and any non-integer value is accepted, and the sign of pixelunits determines the direction of k-space shift

Parameters
[in,out]phaserPointer to KS_PHASER
[in]instancenoInstance of KS_TRAP to change (INSTRALL changes all instances)
[in]pixelunitsNon-integer pixel units in a fully sampled k-space to move
Return values
STATUSSUCCESS or FAILURE
318  {
319  double phasepixelarea = (double) ks_calc_fov2gradareapixel(phaser->fov);
320  double newarea = pixelunits * phasepixelarea + (double) phaser->areaoffset;
321  double ampscale = newarea / phaser->grad.area;
322 
323  if (phaser == NULL)
324  return;
325 
326  if (fabs(ampscale) > 1.00003) { /* 1.00003 corresponds to 32768 / 32767, i.e. excess of one short int */
327 #ifndef IPG
328  ks_error("ks_scan_phaser_kmove(%s): Too large value (%g) in 3rd arg", phaser->grad.description, pixelunits);
329 #endif
330  } else if (fabs(ampscale) > 1.0) {
331  /* small round-off fix when ampscale is in range [1.0, 1.00003] */
332  ampscale = ampscale < 0 ? -1.0 : 1.0;
333  }
334 
335  ks_scan_trap_ampscale(&phaser->grad, instanceno, ampscale);
336 
337 } /* ks_scan_phaser_kmove */
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_error(const char *format,...) __attribute__((format(printf
Common error message function for HOST and TGT
float areaoffset
Definition: KSFoundation.h:1683
float area
Definition: KSFoundation.h:582
KS_DESCRIPTION description
Definition: KSFoundation.h:580
void ks_scan_trap_ampscale(KS_TRAP *trp, int instanceno, float ampscale)
Updates the amplitude of one or all instances of a KS_TRAP sequence object
Definition: KSFoundation_tgt.c:199
float fov
Definition: KSFoundation.h:1677

◆ ks_scan_phaser_toline()

void ks_scan_phaser_toline ( KS_PHASER phaser,
int  instanceno,
int  view 
)

Updates the amplitude of a KS_PHASER sequence object to move from the k-space center to a desired k-space line

This function sets the amplitude of the gradient for a KS_PHASER object to produce a k-space shift from the center of k-space to the view number (3rd arg). The view number must be an integer in range [0, .res-1], and the number refers to a fully sampled k-space (without parallel imaging)

Parameters
[in,out]phaserPointer to KS_PHASER
[in]instancenoInstance of KS_TRAP to change (INSTRALL changes all instances)
[in]viewPhase encoding line to acquire with index corresponding to a fully sampled k-space [0, res-1]
Return values
STATUSSUCCESS or FAILURE
340  {
341  double kmax;
342 
343  if (phaser == NULL)
344  return;
345 
346  if (view < 0 || view >= phaser->res) {
347  ks_scan_phaser_kmove(phaser, instanceno, 0.0);
348  return;
349  }
350 
351  /* KSFoundation 'view' is always 0-based: e.g. res = 256: view = 0->255. */
352  kmax = ((phaser->res - 1.0) / 2.0);
353  ks_scan_phaser_kmove(phaser, instanceno, kmax - view);
354 
355 } /* ks_scan_phaser_toline */
int res
Definition: KSFoundation.h:1678
void ks_scan_phaser_kmove(KS_PHASER *phaser, int instanceno, double pixelunits)
Updates the amplitude of a KS_PHASER sequence object to move some arbitrary distance in k-space...
Definition: KSFoundation_tgt.c:318
int view
Definition: GERequired.e:2642

◆ ks_scan_phaser_fromline()

void ks_scan_phaser_fromline ( KS_PHASER phaser,
int  instanceno,
int  view 
)

Updates the amplitude of a KS_PHASER sequence object to move from a k-space line to the k-space center

This function sets the amplitude of the gradient for a KS_PHASER object to produce a k-space shift from the view number (3rd arg) to the k-space center. The view number must be an integer in range [0, .res-1], and the number refers to a fully sampled k-space (without parallel imaging)

Parameters
[in,out]phaserPointer to KS_PHASER
[in]instancenoInstance of KS_TRAP to change (INSTRALL changes all instances)
[in]viewPhase encoding line to acquire with index corresponding to a fully sampled k-space [0, res-1]
Return values
STATUSSUCCESS or FAILURE
359  {
360  double kmax;
361 
362  if (phaser == NULL)
363  return;
364 
365  if (view < 0 || view >= phaser->res) {
366  ks_scan_phaser_kmove(phaser, instanceno, 0.0);
367  return;
368  }
369 
370  /* KSFoundation 'view' is always 0-based: e.g. res = 256: view = 0->255. */
371  kmax = ((phaser->res - 1.0) / 2.0);
372  ks_scan_phaser_kmove(phaser, instanceno, view - kmax);
373 
374 } /* ks_scan_phaser_fromline */
int res
Definition: KSFoundation.h:1678
void ks_scan_phaser_kmove(KS_PHASER *phaser, int instanceno, double pixelunits)
Updates the amplitude of a KS_PHASER sequence object to move some arbitrary distance in k-space...
Definition: KSFoundation_tgt.c:318
int view
Definition: GERequired.e:2642

◆ ks_scan_getsliceloc()

int ks_scan_getsliceloc ( const KS_SLICE_PLAN slice_plan,
int  passindx,
int  sltimeinpass 
)

Returns the spatially sorted slice index from a DATA_ACQ_ORDER struct array

This function finds the spatially sorted slice index (.slloc) in range [0, nslices-1] given the sequence's DATA_ACQ_ORDER struct array (in slice_plan.acq_order)

Parameters
[in]slice_planPointer to the slice plan (KS_SLICE_PLAN) for the sequence
[in]passindxCurrent pass index ([0, acqs-1])
[in]sltimeinpassTemporal index of the n slices acquired in each pass ([0, n-1])
Return values
sllocSpatially sorted slice index
1023  {
1024  int i;
1025  for (i = 0; i < slice_plan->nslices; i++) {
1026  if (slice_plan->acq_order[i].slpass == passindx && slice_plan->acq_order[i].sltime == sltimeinpass) {
1027  return slice_plan->acq_order[i].slloc;
1028  }
1029  }
1030 
1031  return KS_NOTSET;
1032 
1033 }
DATA_ACQ_ORDER acq_order[SLICE_FACTOR *DATA_ACQ_MAX]
Definition: KSFoundation.h:1354
#define KS_NOTSET
Definition: KSFoundation.h:103
int passindx
Definition: ksfse_implementation.e:2849
int nslices
Definition: KSFoundation.h:1351

◆ ks_scan_getslicetime()

int ks_scan_getslicetime ( const KS_SLICE_PLAN slice_plan,
int  passindx,
int  slloc 
)

Returns the temporally sorted slice index from a DATA_ACQ_ORDER struct array

This function finds the temporally sorted slice index (.sltime) in range [0, nslices-1] given the sequence's DATA_ACQ_ORDER struct array (in slice_plan.acq_order)

Parameters
[in]slice_planPointer to the slice plan (KS_SLICE_PLAN) for the sequence
[in]passindxCurrent pass index ([0, acqs-1])
[in]sllocSpatially sorted slice index [0, nslices-1]
Return values
sltimeinpassTemporal index of the n slices acquired in each pass ([0, n-1])
1036  {
1037  int i;
1038  for (i = 0; i < slice_plan->nslices; i++) {
1039  if (slice_plan->acq_order[i].slpass == passindx && slice_plan->acq_order[i].slloc == slloc) {
1040  return slice_plan->acq_order[i].sltime;
1041  }
1042  }
1043 
1044  return KS_NOTSET;
1045 
1046 }
DATA_ACQ_ORDER acq_order[SLICE_FACTOR *DATA_ACQ_MAX]
Definition: KSFoundation.h:1354
#define KS_NOTSET
Definition: KSFoundation.h:103
int passindx
Definition: ksfse_implementation.e:2849
int nslices
Definition: KSFoundation.h:1351

◆ ks_scan_epi_verify_phaseenc_plan()

ks_enum_epiblipsign ks_scan_epi_verify_phaseenc_plan ( KS_EPI epi,
KS_PHASEENCODING_PLAN phaseenc_plan,
int  shot 
)
1050  {
1051  int i;
1052  int kystep[epi->etl];
1055 
1056  if (phaseenc_plan == NULL) {
1057  return blipsign;
1058  }
1059 
1060  /* verify phase encoding plan has equidistant ky step size */
1061  /* first store ky coords */
1062  for (i = 0; i < epi->etl; i++) {
1063  coord = ks_phaseencoding_get(phaseenc_plan, i, shot);
1064  kystep[i] = coord.ky;
1065  }
1066 
1067  for (i = 0; i < epi->etl-1; i++) {
1068  kystep[i] = kystep[i+1] - kystep[i]; /* calculate ky step */
1069  if (i > 0) {
1070  /* check that kystep is consistent */
1071  if (kystep[i] != kystep[0]) {
1072  ks_error("%s(%s): KS_EPI does not support varying ky step size", __FUNCTION__, phaseenc_plan->description);
1073  return KS_EPI_NOBLIPS;
1074  }
1075  }
1076  }
1077 
1078  if (kystep[0] < 0) {
1079  blipsign = KS_EPI_POSBLIPS;
1080  } else if (kystep[0] > 0) {
1081  blipsign = KS_EPI_NEGBLIPS;
1082  }
1083 
1084  return blipsign;
1085 }
#define KS_INIT_PHASEENCODING_COORD
Definition: KSFoundation.h:219
STATUS ks_error(const char *format,...) __attribute__((format(printf
Common error message function for HOST and TGT
int ky
Definition: KSFoundation.h:1705
int etl
Definition: KSFoundation.h:1843
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
KS_DESCRIPTION description
Definition: KSFoundation.h:1741
Definition: KSFoundation.h:1929
ks_enum_epiblipsign
Definition: KSFoundation.h:1929
Definition: KSFoundation.h:1929
Definition: KSFoundation.h:1929

◆ ks_scan_epi_shotcontrol()

void ks_scan_epi_shotcontrol ( KS_EPI epi,
int  echo,
SCAN_INFO  sliceinfo,
KS_PHASEENCODING_PLAN phaseenc_plan,
int  shot,
int  exc,
float  rcvphase 
)

Changes the gradient state of a KS_EPI object for the given slice information

This function has two different tasks. First, it controls the EPI blips and sets the EPI dephaser and rephaser amplitude given the current phaseshot and blipsign. If phaseshot is outside the valid range ([0, .blipphaser.R-1], all gradient amplitudes on the blip (phase encoding) axis will be zero. This can be useful to acquire a refscan for Nyquist ghost correction. Second, it sets up the proper frequency and phase modulation per readout lobe to produce the desired FOV offset in both the frequency and phase encoding directions

Parameters
[in,out]epiPointer to KS_EPI
[in]echoEPI echo index (up to 16 EPI trains supported)
[in]sliceinfoSCAN_INFO struct for the current slice to be played out
[in]phaseenc_planPointer to the phase encoding plan
[in]shotLinear ky kz shot index in range [0, (phaseenc_plan.num_shots-1)]
[in]excZero-based NEX index
[in]rcvphaseReceiver phase in degrees, for RF spoiling.
Return values
STATUSSUCCESS or FAILURE
1089  {
1090  float blipampscale = 1.0;
1091  float yfovratio, zfovratio;
1092  int i, readindx, blipindx, kyview, kzview;
1093  int numblips = (epi->etl - 1);
1094  int isrefscan = (exc < 0); /* exc < 0 => data acquisition ON, EPI blips OFF (to make RefScan for Nyquist ghost correction) */
1096 
1097  ks_enum_epiblipsign blipsign = ks_scan_epi_verify_phaseenc_plan(epi, phaseenc_plan, shot);
1098 
1099  if (phaseenc_plan != NULL && (epi->etl != phaseenc_plan->etl)) {
1100  ks_error("%s(%s): ETL of KS_EPI (%d) and KS_PHASEENCODING_PLAN (%d) do not match", __FUNCTION__, phaseenc_plan->description, epi->etl, phaseenc_plan->etl);
1101  blipsign = KS_EPI_NOBLIPS;
1102  }
1103 
1104  if (isrefscan) {
1105  blipsign = KS_EPI_NOBLIPS;
1106  }
1107 
1108  /* EPI read & blips for current 'echo' = EPI train. */
1109  for (i = 0; i < epi->etl; i++) {
1110  coord = ks_phaseencoding_get(phaseenc_plan, i, shot);
1111  kyview = (blipsign != KS_EPI_NOBLIPS) ? coord.ky : KS_NOTSET;
1112  kzview = (blipsign != KS_EPI_NOBLIPS) ? coord.kz : KS_NOTSET;
1113 
1114  if (i == 0) { /* EPI dephaser. From k-space center to this line */
1115  ks_scan_phaser_toline(&epi->blipphaser, 0 + 2 * echo, kyview); /* KS_NOTSET => zero amplitude */
1116  if (epi->zphaser.grad.duration > 0) {
1117  /* 3D epi z dephaser(s) for even instances '2*echo' */
1118  ks_scan_phaser_toline(&epi->zphaser, 0 + 2 * echo, kzview);
1119  }
1120  }
1121 
1122  if (i == epi->etl-1) { /* EPI rephaser. From this line to k-space center */
1123  ks_scan_phaser_fromline(&epi->blipphaser, 1 + 2 * echo, kyview); /* KS_NOTSET => zero amplitude */
1124  if (epi->zphaser.grad.duration > 0) {
1125  /* 3D epi z rephaser(s) for odd instances '1+2*echo' */
1126  ks_scan_phaser_fromline(&epi->zphaser, 1 + 2 * echo, kzview);
1127  }
1128  }
1129 
1130  readindx = i + echo * epi->etl; /* ETL# of readouts */
1131  blipindx = i + echo * numblips; /* (ETL-1)# of blips */
1132 
1133  if (i < numblips) {
1134  /* FUTURE: at this point, we could increase/decrease blipampscale for odd/even blips for oblique ghost correction */
1135  blipampscale = blipsign / epi->blipoversize;
1136  ks_scan_trap_ampscale(&epi->blip, blipindx, blipampscale);
1137  }
1138 
1139  /* FOV offsets (by changing freq/phase of epi.read) */
1140  float ky_centred, kz_centred;
1141  if (kyview < 0) {
1142  ky_centred = 0.0f;
1143  } else {
1144  ky_centred = (float)kyview - ((epi->blipphaser.res - 1) / 2.0f);
1145  }
1146  yfovratio = epi->blipphaser.fov / epi->read.fov;
1147  if (epi->zphaser.grad.duration > 0) {
1148  if (kzview < 0) {
1149  kz_centred = 0.0f;
1150  } else {
1151  kz_centred = (float)kzview - ((epi->zphaser.res - 1) / 2.0f);
1152  }
1153  zfovratio = epi->zphaser.fov / epi->read.fov;
1154  ks_scan_offsetfov3D(&epi->read, readindx, sliceinfo, ky_centred, yfovratio, kz_centred, zfovratio, rcvphase);
1155  } else {
1156  ks_scan_offsetfov(&epi->read, readindx, sliceinfo, ky_centred, yfovratio, rcvphase);
1157  }
1158 
1159  } /* for etl */
1160 
1161 } /* ks_scan_epi_shotcontrol */
float blipoversize
Definition: KSFoundation.h:1847
KS_TRAP blip
Definition: KSFoundation.h:1839
#define KS_INIT_PHASEENCODING_COORD
Definition: KSFoundation.h:219
void ks_scan_phaser_toline(KS_PHASER *phaser, int instanceno, int view)
Updates the amplitude of a KS_PHASER sequence object to move from the k-space center to a desired k-s...
Definition: KSFoundation_tgt.c:340
int res
Definition: KSFoundation.h:1678
void ks_scan_offsetfov3D(KS_READTRAP *readtrap, int instanceno, SCAN_INFO sliceinfo, float ky, float phasefovratio, float kz, float zphasefovratio, float rcvphase)
Updates the frequency and phase of one or all instances of a KS_READTRAP to create a FOV shift...
Definition: KSFoundation_tgt.c:974
KS_TRAP grad
Definition: KSFoundation.h:1676
#define KS_NOTSET
Definition: KSFoundation.h:103
STATUS ks_error(const char *format,...) __attribute__((format(printf
Common error message function for HOST and TGT
float fov
Definition: KSFoundation.h:1576
KS_PHASER blipphaser
Definition: KSFoundation.h:1840
int ky
Definition: KSFoundation.h:1705
KS_PHASER zphaser
Definition: KSFoundation.h:1841
ks_enum_epiblipsign ks_scan_epi_verify_phaseenc_plan(KS_EPI *epi, KS_PHASEENCODING_PLAN *phaseenc_plan, int shot)
Definition: KSFoundation_tgt.c:1050
int kz
Definition: KSFoundation.h:1706
int etl
Definition: KSFoundation.h:1843
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
void ks_scan_offsetfov(KS_READTRAP *readtrap, int instanceno, SCAN_INFO sliceinfo, float ky, float phasefovratio, float rcvphase)
Updates the frequency and phase of one or all instances of a KS_READTRAP to create a FOV shift...
Definition: KSFoundation_tgt.c:968
KS_DESCRIPTION description
Definition: KSFoundation.h:1741
int etl
Definition: KSFoundation.h:1740
KS_READTRAP read
Definition: KSFoundation.h:1837
ks_enum_epiblipsign
Definition: KSFoundation.h:1929
void ks_scan_trap_ampscale(KS_TRAP *trp, int instanceno, float ampscale)
Updates the amplitude of one or all instances of a KS_TRAP sequence object
Definition: KSFoundation_tgt.c:199
void ks_scan_phaser_fromline(KS_PHASER *phaser, int instanceno, int view)
Updates the amplitude of a KS_PHASER sequence object to move from a k-space line to the k-space cente...
Definition: KSFoundation_tgt.c:359
int duration
Definition: KSFoundation.h:585
Definition: KSFoundation.h:1929
float fov
Definition: KSFoundation.h:1677

◆ ks_scan_epi_loadecho_with_tag()

void ks_scan_epi_loadecho_with_tag ( KS_EPI epi,
int  echo,
int  storeecho,
int  slice,
KS_PHASEENCODING_PLAN phaseenc_plan,
int  shot,
KSEPI_DATATAG datatag,
KS_DATASTORETAG datastoretag 
)

Loads the data storage information to hardware for the acquisition windows in a KS_EPI sequence object

This function specifies where to store the data acquired for each readout lobe in the KS_EPI train given current:

  • echo: First instance of the KS_EPI train is 0
  • slice: Slice index, 0-based
  • shot: Linear ky kz shot index in range [0, (phaseenc_plan.num_shots-1)]
  • phaseenc_plan: phaseenc_plan Pointer to the phase encoding plan

If shot is outside the valid range, a baseline view (view 0 in loaddab()) will be acquired.

If slice is < 0, data acquisition is turned off in loaddab()

Parameters
[in,out]epiPointer to KS_EPI
[in]echoEPI echo index to work on (up to 16 EPI trains supported)
[in]storeechoEPI echo index for storing (usually the same as echo)
[in]sliceSlice index where to store the data (0-based)
[in]phaseenc_planPointer to the phase encoding plan
[in]shotLinear ky kz shot index in range [0, (phaseenc_plan.num_shots-1)]
[in]datastoretagTag (unsigned int) passed to loaddabwithangle(). This can be filled with whatever content to inform recon. Will be a part of each control packet on the recon side.
Return values
STATUSSUCCESS or FAILURE
1165  {
1166  if ((datastoretag != NULL) && (datatag != NULL)) {
1167  ks_error("%s: either datastoretag or custom_dab_array must be NULL, but both were supplied.", __FUNCTION__);
1168  return;
1169  }
1170 
1171  int i, readindx;
1172 #ifdef IPG
1173  int validsliceindx = (slice >= 0 && slice < 2048);
1174  int validshotindx = (shot >= 0 && (phaseenc_plan == NULL || shot < phaseenc_plan->num_shots));
1176  ks_enum_epiblipsign blipsign = ks_scan_epi_verify_phaseenc_plan(epi, phaseenc_plan, shot);
1177  int centerecho = epi->time2center / epi->read.grad.duration; /* etl index corresponding to k-space center */
1178  int numplaced = epi->read.acq.base.ngenerated;
1179 
1180  /* 'pscR1' is the R1 receive gain made by prescan */
1181  int currentR1 = (epi->read.acq.override_R1 > 0 && epi->read.acq.override_R1 <= 11) ? epi->read.acq.override_R1 : pscR1;
1182 
1183  TYPDAB_PACKETS dabacqctrl;
1184  if (validsliceindx && validshotindx) {
1185  dabacqctrl = DABON; /* turn receiver on */
1186  } else {
1187  dabacqctrl = DABOFF; /* turn receiver off */
1188  slice = 0; /* necessary? */
1189  }
1190 #else
1191  int numplaced = epi->read.acq.base.ninst;
1192 #endif
1193 
1194  if (numplaced == 0) {
1195  return;
1196  }
1197  if (storeecho < 0 || storeecho > 15) {
1198  ks_error("%s: storeecho must be in range [0:15]", __FUNCTION__);
1199  return;
1200  }
1201  if (echo < 0 || (echo + 1) * epi->etl > numplaced) {
1202  ks_error("%s: echo (%d) must be in range [0:%d]", __FUNCTION__, echo, numplaced/epi->etl-1);
1203  return;
1204  }
1205 
1206  for (i = 0; i < epi->etl; i++) {
1207 
1208  readindx = i + echo * epi->etl; /* N.B.: 'echo' is # of EPI trains, not EPI readout lobes (up to 16 possible, set by opnecho) */
1209 
1210  if (readindx >= numplaced) {
1211  ks_error("%s(%s): Readout lobe index %d has not been placed in PG", __FUNCTION__, epi->read.grad.description, readindx);
1212  return;
1213  }
1214 
1215 #ifdef IPG
1216  int kyview = KS_NOTSET;
1217  if (validshotindx && (blipsign != KS_EPI_NOBLIPS)) {
1218  coord = ks_phaseencoding_get(phaseenc_plan, i, shot);
1219  kyview = coord.ky;
1220  if (rhasset == ASSET_SCAN && epi->blipphaser.R > 1 && epi->blipphaser.nacslines == 0) {
1221  /* Auto-detect ASSET scan, which uses compressed storage of accelerated data, i.e. only
1222  storing the acquired lines, without inteleaving with blank unacquired lines */
1223  if (blipsign == KS_EPI_POSBLIPS) {
1224  kyview = epi->etl-1 - i;
1225  } else if (blipsign == KS_EPI_NEGBLIPS) {
1226  kyview = i;
1227  }
1228  }
1229  }
1230 
1231  if (rspent == L_APS2 || rspent == L_MPS2) {
1232  /* Prescan (R1 & R2) */
1233  if (readindx == centerecho && echo == 0) {
1234  loaddab(&epi->read.acq.echo[readindx], slice, storeecho, DABSTORE, 0, dabacqctrl, PSD_LOAD_DAB_ALL); /* turn on acquisition for center echo of 1st EPI train */
1235  } else {
1236  loaddab(&epi->read.acq.echo[readindx], slice, storeecho, DABSTORE, 0, DABOFF, PSD_LOAD_DAB_ALL); /* turn off acquisition for all other lobes (and all later EPI trains) */
1237  }
1238  } else {
1239  if (datastoretag != NULL) {
1240  /* Storing additional data in the dab pulse, in datastoretag.dab_array is space for up to KS_CUSTOM_DATASTORE_LENGTH byte */
1241  ks_loaddab_datastoretag(&epi->read.acq.echo[readindx], datastoretag);
1242  }
1243  if (datatag != NULL) {
1244  datatag->readout = i;
1245  ks_loaddab(&epi->read.acq.echo[readindx], (char*) datatag);
1246  }
1247  /* N.B.: kyview = KS_NOTSET (-1) if shot = KS_NOTSET. Hence kyview + 1 = dabview = 0 without phase encodes */
1248  loaddab_hub_r1(&epi->read.acq.echo[readindx], slice, storeecho, DABSTORE, kyview + 1 /* GE's views are 1-based */, 0, currentR1, dabacqctrl, PSD_LOAD_DAB_ALL_WITH_HUB_R1);
1249  }
1250 #endif
1251 
1252  } /* per acq window (readout lobe) */
1253 
1254 } /* ks_scan_epi_loadecho_with_tag */
#define KS_INIT_PHASEENCODING_COORD
Definition: KSFoundation.h:219
int R
Definition: KSFoundation.h:1680
WF_PULSE * echo
Definition: KSFoundation.h:738
int num_shots
Definition: KSFoundation.h:1739
int nacslines
Definition: KSFoundation.h:1681
#define KS_NOTSET
Definition: KSFoundation.h:103
LONG override_R1
Definition: KSFoundation.h:737
uint16_t readout
Definition: KSFoundation.h:280
STATUS ks_error(const char *format,...) __attribute__((format(printf
Common error message function for HOST and TGT
int ninst
Definition: KSFoundation.h:409
KS_PHASER blipphaser
Definition: KSFoundation.h:1840
int ky
Definition: KSFoundation.h:1705
int rspent
Definition: GERequired.e:2626
ks_enum_epiblipsign ks_scan_epi_verify_phaseenc_plan(KS_EPI *epi, KS_PHASEENCODING_PLAN *phaseenc_plan, int shot)
Definition: KSFoundation_tgt.c:1050
KS_TRAP grad
Definition: KSFoundation.h:1586
KS_BASE base
Definition: KSFoundation.h:731
int etl
Definition: KSFoundation.h:1843
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 pscR1
KS_READTRAP read
Definition: KSFoundation.h:1837
void ks_loaddab_datastoretag(WF_PULSE_ADDR echo, KS_DATASTORETAG *datastoretag)
Definition: KSFoundation_tgt.c:1293
int rhasset
void ks_loaddab(WF_PULSE_ADDR echo, char *custom_dab_array)
Definition: KSFoundation_tgt.c:1276
Definition: KSFoundation.h:1929
KS_DESCRIPTION description
Definition: KSFoundation.h:580
ks_enum_epiblipsign
Definition: KSFoundation.h:1929
int time2center
Definition: KSFoundation.h:1846
int ngenerated
Definition: KSFoundation.h:410
KS_READ acq
Definition: KSFoundation.h:1574
int duration
Definition: KSFoundation.h:585
Definition: KSFoundation.h:1929
Definition: KSFoundation.h:1929

◆ ks_scan_epi_loadecho_with_datastoretag()

void ks_scan_epi_loadecho_with_datastoretag ( KS_EPI epi,
int  echo,
int  storeecho,
int  slice,
KS_PHASEENCODING_PLAN phaseenc_plan,
int  shot,
KS_DATASTORETAG datastoretag 
)

Wrappers around ks_scan_epi_loadecho_withtag_or_dabarray.

1258  {
1259  ks_scan_epi_loadecho_with_tag(epi, echo, storeecho, slice, phaseenc_plan, shot, NULL, datastoretag);
1260 }
void ks_scan_epi_loadecho_with_tag(KS_EPI *epi, int echo, int storeecho, int slice, KS_PHASEENCODING_PLAN *phaseenc_plan, int shot, KSEPI_DATATAG *datatag, KS_DATASTORETAG *datastoretag)
Loads the data storage information to hardware for the acquisition windows in a KS_EPI sequence objec...
Definition: KSFoundation_tgt.c:1165

◆ ks_scan_epi_loadecho_with_epidatatag()

void ks_scan_epi_loadecho_with_epidatatag ( KS_EPI epi,
int  echo,
int  storeecho,
int  slice,
KS_PHASEENCODING_PLAN phaseenc_plan,
int  shot,
KSEPI_DATATAG datatag 
)
1264  {
1265  ks_scan_epi_loadecho_with_tag(epi, echo, storeecho, slice, phaseenc_plan, shot, datatag, NULL);
1266 }
void ks_scan_epi_loadecho_with_tag(KS_EPI *epi, int echo, int storeecho, int slice, KS_PHASEENCODING_PLAN *phaseenc_plan, int shot, KSEPI_DATATAG *datatag, KS_DATASTORETAG *datastoretag)
Loads the data storage information to hardware for the acquisition windows in a KS_EPI sequence objec...
Definition: KSFoundation_tgt.c:1165

◆ ks_scan_epi_loadecho()

void ks_scan_epi_loadecho ( KS_EPI epi,
int  echo,
int  storeecho,
int  slice,
KS_PHASEENCODING_PLAN phaseenc_plan,
int  shot 
)
1270  {
1271  ks_scan_epi_loadecho_with_tag(epi, echo, storeecho, slice, phaseenc_plan, shot, NULL, NULL);
1272 }
void ks_scan_epi_loadecho_with_tag(KS_EPI *epi, int echo, int storeecho, int slice, KS_PHASEENCODING_PLAN *phaseenc_plan, int shot, KSEPI_DATATAG *datatag, KS_DATASTORETAG *datastoretag)
Loads the data storage information to hardware for the acquisition windows in a KS_EPI sequence objec...
Definition: KSFoundation_tgt.c:1165

◆ ks_loaddab()

void ks_loaddab ( WF_PULSE_ADDR  echo,
char *  dab_array 
)
1276  {
1277  int i;
1278  static const int dab_indices[KS_CUSTOM_DATASTORE_LENGTH] = {2,3,4,5,6,7,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29};
1279 
1280  for (i=0; i<KS_CUSTOM_DATASTORE_LENGTH; i++){
1281  short dabbyte = custom_dab_array[i];
1282 #ifdef IPG
1283  sspload(&dabbyte, echo, dab_indices[i], 1, (HW_DIRECTION)TOHARDWARE, (SSP_S_ATTRIB)SSPS1);
1284 #else
1285  (void) dabbyte;
1286  (void) dab_indices;
1287 #endif
1288  }
1289 }
#define KS_CUSTOM_DATASTORE_LENGTH
Definition: KSFoundation.h:267

◆ ks_loaddab_datastoretag()

void ks_loaddab_datastoretag ( WF_PULSE_ADDR  echo,
KS_DATASTORETAG datastoretag 
)
1293  {
1294  if (datastoretag == NULL) {
1295  return;
1296  }
1297 
1298  if (datastoretag->info.load_custom_dab_array == TRUE) {
1299  /* if the psd provides */
1300  if (datastoretag->fill_dab_array_fct != NULL){
1301  datastoretag->fill_dab_array_fct(datastoretag);
1302  }
1303  ks_loaddab(echo, datastoretag->dab_array);
1304  }
1305 }
char dab_array[KS_CUSTOM_DATASTORE_LENGTH]
Definition: KSFoundation.h:316
void ks_loaddab(WF_PULSE_ADDR echo, char *custom_dab_array)
Definition: KSFoundation_tgt.c:1276
KS_DATASTORE_STRUCT_INFO info
Definition: KSFoundation.h:315
void(* fill_dab_array_fct)(struct _KS_DATASTORETAG *datastoretag)
Definition: KSFoundation.h:318
int load_custom_dab_array
Definition: KSFoundation.h:306

◆ ks_scan_acq_to_rtp()

void ks_scan_acq_to_rtp ( KS_READ read,
TYPDAB_PACKETS  dabacqctrl,
float  fatoffset 
)

Data routing control for RTP

Parameters
[in,out]readPointer to KS_READ
[in]dabacqctrlDABON or DABOFF
[in]fatoffsetFrequency offset in [Hz] for the current data acquisition window
Returns
void
1314  {
1315 #ifdef IPG
1316 
1317  WF_PULSE *echo_ptr;
1318  WF_PULSE *xtr_ptr;
1319  WF_PULSE *rba_ptr;
1320 
1321  int i;
1322  for (i = 0; i < acq->base.ngenerated; ++i) {
1323  echo_ptr = &acq->echo[i];
1324  setrfltrs((int) acq->filt.fslot, echo_ptr);
1325 
1326  getssppulse(&rba_ptr, echo_ptr, "rba", 0);
1327  getssppulse(&xtr_ptr, echo_ptr, "xtr", 0);
1328  acqctrl(dabacqctrl, (RECEIVER_SPEED_E)0, rba_ptr);
1329 
1330  setfreqphase((int)(fatoffset / TARDIS_FREQ_RES), /* freq ctrl */
1331  0, /* phase ctrl */
1332  xtr_ptr);
1333 
1334  /* TODO: Add r1 scaling here! */
1335  /*if (r1_scale != 1.0 && (rspent==L_SCAN)) {
1336  newR1 = ceil(pscR1 * r1_scale);
1337  if (newR1 > 13)
1338  newR1 = 13;
1339  else if (newR1 < 1)
1340  newR1 = 1;
1341 
1342  loaddab_hub_r1(echo_ptr, dabslice, dabecho, DABSTORE, dabview, 0, newR1, dabacqctrl, PSD_LOAD_DAB_ALL | PSD_LOAD_DAB_R1);
1343  }*/ /* if R1 change */
1344  routeDataFrameDab(echo_ptr, ROUTE_TO_RTP, cfcoilswitchmethod);
1345  }
1346 
1347 #endif
1348 
1349 }
int cfcoilswitchmethod

◆ ks_scan_switch_to_sequence()

void ks_scan_switch_to_sequence ( KS_SEQ_CONTROL ctrl)
1356  {
1357 #ifdef IPG
1358  if (ctrl->duration > 0) {
1359  boffset(ctrl->handle.offset);
1360  }
1361 #endif
1362 }
STATUS boffset(long *offsets)
long * offset
Definition: KSFoundation.h:986
int duration
Definition: KSFoundation.h:1135
KS_SEQ_HANDLE handle
Definition: KSFoundation.h:1142

◆ ks_scan_playsequence()

int ks_scan_playsequence ( KS_SEQ_CONTROL ctrl)
1368  {
1369 
1370  if (ctrl->duration > 0) {
1371 
1372 #ifdef IPG
1373  {
1375  setssitime(ctrl->ssi_time / cfhwgut);
1376  startseq(0, (short) MAY_PAUSE); /* play out the sequence in real time */
1377  }
1378 #else
1379  /* HOST */
1380  ctrl->nseqinstances++;
1381 #endif
1382  } /* ctrl->duration > 0 */
1383 
1384  return ctrl->duration;
1385 }
STATUS startseq(s16, s16)
STATUS setssitime(long)
int cfhwgut
int duration
Definition: KSFoundation.h:1135
int nseqinstances
Definition: KSFoundation.h:1133
void ks_scan_switch_to_sequence(KS_SEQ_CONTROL *ctrl)
Definition: KSFoundation_tgt.c:1356
int ssi_time
Definition: KSFoundation.h:1134

◆ ks_scan_loaddabwithindices()

STATUS ks_scan_loaddabwithindices ( WF_PULSE_ADDR  pulse,
LONG  slice,
LONG  echo,
LONG  view,
uint8_t  acq,
uint8_t  vol,
TYPDAB_PACKETS  acqon_flag 
)

loaddab() with extra arguments for current acquisition and image volume

Parameters
[in]pulsePointer to WF_PULSE in a KS_READ
[in]slice0-based temporal slice index (2D) or kz encoding step typically (3D)
[in]echo0-based echo in the echo train
[in]view1-based ky view number
[in]acq0-based acquisition index (always 0 if all slices fit in one TR)
[in]vol0-based volume index (always 0 if only one volume)
[in]acqon_flagDABON or DABOFF
Return values
STATUSSUCCESS or FAILURE
1413  {
1414  return ks_scan_loaddabwithindices_nex(pulse, slice, echo, view, acq, vol, DABSTORE, acqon_flag);
1415 }
STATUS ks_scan_loaddabwithindices_nex(WF_PULSE_ADDR pulse, LONG slice, LONG echo, LONG view, uint8_t acq, uint8_t vol, LONG operation, TYPDAB_PACKETS acqon_flag)
loaddab() with extra arguments for current acquisition and image volume and current excitation (NEX)...
Definition: KSFoundation_tgt.c:1392
int view
Definition: GERequired.e:2642

◆ ks_scan_loaddabwithindices_nex()

STATUS ks_scan_loaddabwithindices_nex ( WF_PULSE_ADDR  pulse,
LONG  slice,
LONG  echo,
LONG  view,
uint8_t  acq,
uint8_t  vol,
LONG  operation,
TYPDAB_PACKETS  acqon_flag 
)

loaddab() with extra arguments for current acquisition and image volume and current excitation (NEX)

Parameters
[in]pulsePointer to WF_PULSE in a KS_READ
[in]slice0-based temporal slice index (2D) or kz encoding step typically (3D)
[in]echo0-based echo in the echo train
[in]view1-based ky view number
[in]acq0-based acquisition index (always 0 if all slices fit in one TR)
[in]vol0-based volume index (always 0 if only one volume)
[in]operationDABSTORE, DABADD (for 2nd to last excitation)
[in]acqon_flagDABON or DABOFF
Return values
STATUSSUCCESS or FAILURE
1399  {
1400 
1401  union Indices idx;
1402  idx.c[0] = acq;
1403  idx.c[1] = vol;
1404  return loaddabwithangle(pulse, idx.i, slice, echo, operation, view, acqon_flag, PSD_LOAD_DAB_ALL);
1405 }
Definition: KSFoundation_tgt.c:1387
int view
Definition: GERequired.e:2642
uint8_t c[4]
Definition: KSFoundation_tgt.c:1388

◆ ks_scan_wait_for_rtp()

int ks_scan_wait_for_rtp ( void *  rtpmsg,
int  maxmsgsize,
int  maxwait,
KS_SEQ_CONTROL waitctrl 
)

play a wait sequence in a loop untill a RTP message is received.

Parameters
[out]rtpmsgpointer to memory that will be filled by the RTP message
[in]maxmsgsizesize in bytes of the memory pointed by rtpmsg
[in]maxwaittiming out period in [ms]
[in]waitctrlpointer to the KS_SEQ_CONTROL of a wait sequence. If NULL, the provided maxwait is ignored and a value of zero used instead.
Return values
sizeof the received message, zero if timed out
1417  {
1418  int maxplayouts;
1419  int nBytes = 0;
1420  int i;
1421  n32 packed = 1;
1422 #ifdef IPG
1423  setscantimestop();
1424 #endif
1425 
1426  maxplayouts = waitctrl ? maxwait / waitctrl->duration : 0;
1427  if (maxplayouts < 1) {
1428  waitctrl = NULL;
1429  maxplayouts = 0;
1430  }
1431 
1432  for (i = 0; i <= maxplayouts; i++) {
1433 
1434 #ifdef IPG
1435  if (i != 0 && waitctrl) {
1436  ks_scan_playsequence(waitctrl);
1437  }
1438 #endif
1439 
1440  nBytes = 0;
1441 
1442 #if defined(MGD_TGT) && defined(PSD_HW)
1443 
1444 
1445  nBytes = rtp_get_feedback_data(rtpmsg, maxmsgsize, &packed, 2, RTP_QUEUE_NEWEST); /*according to RTP_RESULT_PROMO=2, defined in lx/include/PromoCommon.h*/
1446 
1447 
1448 #endif
1449 
1450  if (nBytes>0 && !packed) {
1451  break;
1452  }
1453  }
1454 
1455 #ifdef IPG
1456  setscantimestart();
1457 #endif
1458 
1459  return nBytes;
1460 }
int32_t i
Definition: KSFoundation_tgt.c:1389
int duration
Definition: KSFoundation.h:1135
int ks_scan_playsequence(KS_SEQ_CONTROL *ctrl)
Definition: KSFoundation_tgt.c:1368

◆ ks_copy_and_reset_obj()

void ks_copy_and_reset_obj ( void *  pobj)

Create a copy of any sequence object with a KS_BASE as first member

Create a copy of the object pointed by pobj and insert it into the linked list in second position. finally reset the (base part of) original object.

It is required that pobj can be casted to (KS_BASE *), which is the pointed object should have a KS_BASE as first member. Note that other, object-specific, actions may need to be performed to finalize the reset. This includes calls to ks_instancereset_***() functions on each member of the current pobj

Parameters
[in]pobjPointer to a sequence object of some kind
Returns
void
1463  {
1464 
1465  KS_BASE *head = (KS_BASE*) pobj;
1466 
1467  /* create a copy */
1468  KS_BASE *copy = (KS_BASE*) AllocNode(head->size);
1469  memcpy(copy, head, head->size);
1470 
1471  /* link to the copy */
1472  head->next = copy;
1473 
1474  /* reset the count of generated instances */
1475  head->ngenerated = 0;
1476 }
void * next
Definition: KSFoundation.h:411
(Internal use) Structure being a part of various sequence object to keep count of instances on HOST a...
Definition: KSFoundation.h:408
int ngenerated
Definition: KSFoundation.h:410
int size
Definition: KSFoundation.h:412

◆ ks_show_clock()

void ks_show_clock ( FLOAT  scantime)

Show the clock in the UI with the remaining scan time

1502  {
1503 #ifdef IPG
1504  setscantimestop();
1505  setscantimeimm(PSD_CLOCK_NORM, (float)scantime, piviews, (float)pitslice, opslicecnt);
1506  setscantimestart();
1507 #endif
1508 }
int piviews
int opslicecnt

◆ ks_scan_rf_phase_spoiling()

float ks_scan_rf_phase_spoiling ( int  counter)

Returns spoiling phase for a given RF counter

Parameters
[in]counter[RF number]
Returns
phase [Phase in degrees]
3883  {
3884  double phase = 117.0 * ((double) counter * ((double) counter + 1.0)) / 2.0;
3885 
3886  /* fmod() does not seem to work on TGT on hardware (MR scanner) */
3887 
3888  int phase_fulllaps = (int) (phase / 360.0);
3889 
3890  phase = phase - (phase_fulllaps * 360.0);
3891 
3892  return ((float) phase);
3893 }

◆ ks_tgt_reset_gradrfctrl()

void ks_tgt_reset_gradrfctrl ( KS_GRADRFCTRL gradrfctrl)

Clears KS_GRADRFCTRL on TGT. Called in ks_pg_trap, ks_pg_wave, and ks_pg_rf if the field is_cleared_on_tgt is false.

Parameters
gradrfctrl[pointer to KS_GRADRFCTRL]
1481  {
1482  int i = 0;
1483  for (i = 0; i < gradrfctrl->numrf; ++i) {
1484  gradrfctrl->rfptr[i] = NULL;
1485  }
1486  for (i = 0; i < gradrfctrl->numtrap; ++i) {
1487  gradrfctrl->trapptr[i] = NULL;
1488  }
1489  for (i = 0; i < gradrfctrl->numwave; ++i) {
1490  gradrfctrl->waveptr[i] = NULL;
1491  }
1492  for (i = 0; i < gradrfctrl->numacq; ++i) {
1493  gradrfctrl->readptr[i] = NULL;
1494  }
1495  gradrfctrl->numrf = 0;
1496  gradrfctrl->numtrap = 0;
1497  gradrfctrl->numwave = 0;
1498  gradrfctrl->numacq = 0;
1499  gradrfctrl->is_cleared_on_tgt = TRUE;
1500 }
KS_RF * rfptr[KS_MAXUNIQUE_RF]
Definition: KSFoundation.h:966
int numtrap
Definition: KSFoundation.h:969
int32_t i
Definition: KSFoundation_tgt.c:1389
int numrf
Definition: KSFoundation.h:967
KS_WAVE * waveptr[KS_MAXUNIQUE_WAVE]
Definition: KSFoundation.h:970
int is_cleared_on_tgt
Definition: KSFoundation.h:974
KS_TRAP * trapptr[KS_MAXUNIQUE_TRAP]
Definition: KSFoundation.h:968
int numwave
Definition: KSFoundation.h:971
KS_READ * readptr[KS_MAXUNIQUE_READ]
Definition: KSFoundation.h:972
int numacq
Definition: KSFoundation.h:973

◆ 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_polyval()

void ks_polyval ( const double *  coeffs,
const int  order,
const float *  x,
const int  numx,
float *  values 
)

Variable Documentation

◆ abort_on_kserror

int abort_on_kserror

◆ ks_rhoboard

int ks_rhoboard

◆ loggrd

LOG_GRAD loggrd

◆ phygrd

PHYS_GRAD phygrd

◆ pscR1

int pscR1

◆ rspent

int rspent