KSFoundation  [October2024]
A platform for structured EPIC programming on GE MR systems
KSFoundation.h File Reference

Data Structures

struct  KS_SEQLOC
 
struct  KS_RT_SCALE_LOG
 
struct  KS_WFINSTANCE
 
struct  KS_BASE
 
struct  KS_WAIT
 
struct  KS_ISIROT
 
struct  KS_TRAP
 
struct  KS_WAVE
 
struct  KS_READ_RESAMPLER
 
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  kscommon_wave_params
 
struct  KSCOMMON_RESAMPLER
 
struct  KS_PHASER
 
struct  KS_PHASEENCODING_COORD
 
struct  KS_PHASEENCODING_SHOTCOORDS
 
struct  KS_PHASEENCODING_PLAN
 
struct  KS_DYNAMIC_STATE
 
struct  KS_EPI
 
struct  KS_READCONTROL_STATE
 
struct  KS_READCONTROL_PULSEGEN
 
struct  KS_READCONTROL
 
struct  KS_ECHOTRAIN
 
struct  KS_ECHOTRAIN_READOUT_TIMING
 
struct  KS_DIXON_ASYMREADWAVE
 
struct  KS_PEPLAN_SHOT_DISTRIBUTION
 
struct  KS_KCOORD
 
struct  KS_VIEW
 
struct  KS_KSPACE_ACQ
 
struct  KS_PHASEENCODING_REPEAT_DESIGN
 
struct  KS_KACQ_RECONSHEDULE_ENTRY
 
struct  ks_crusher_constraints
 

Macros

#define WARN_UNUSED_RESULT
 
#define KS_GOLDEN_MEANS_1D   (1.0+sqrt(5.0))/2.0 /* approx. 1.618033988749895 */
 
#define KS_GOLDEN_MEANS_2D_1   1.0/pow((1.0/pow((sqrt(3.1E+1)*sqrt(1.08E+2))/1.08E+2+2.9E+1/5.4E+1,1.0/3.0)/9.0+pow((sqrt(3.1E+1)*sqrt(1.08E+2))/1.08E+2+2.9E+1/5.4E+1,1.0/3.0)+1.0/3.0),2.0) /* approx. 0.4655712319 */
 
#define KS_GOLDEN_MEANS_2D_2   1.0/(1.0/pow((sqrt(3.1E+1)*sqrt(1.08E+2))/1.08E+2+2.9E+1/5.4E+1,1.0/3.0)/9.0+pow((sqrt(3.1E+1)*sqrt(1.08E+2))/1.08E+2+2.9E+1/5.4E+1,1.0/3.0)+1.0/3.0) /* approx. 0.6823278038 */
 
#define KS_ANGLE_PER_INT_16_MAX_180   (180.0 / 0x8000)
 
#define KS_ANGLE_PER_UINT_16_MAX_180   (180.0 / 0xF000)
 
#define KS_SIGN(x)   ((x < 0) ? -1 : 1)
 
#define DEFAULT_SCAN_INFO_HEAD   0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0
 
#define INT_MAX   2147483647
 
#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 KS_EXPAND_OPUSER(number)   number, &_opuser ## number
 
#define KS_EXPAND_CVFLOAT(number)   &_opuser ## number
 
#define KS_OPUSER_FROM_NUM(number)   opuser_from_number(KS_EXPAND_CVFLOAT(number));
 
#define KS_OPUSER_VALUE_FROM_NUM(number)   *opuser_from_number(KS_EXPAND_CVFLOAT(number))->addr;
 
#define areSame(a, b)   (fabs((a) - (b)) < 1e-5)
 
#define areWithinTol(a, b, tol)   (fabs((a) - (b)) < tol)
 
#define areSameRelative(a, b, r)   (fabs((a) - (b)) < r * (fabs((a)) + fabs((b))))
 
#define isNotSet(a)   areSame((a), KS_NOTSET)
 
#define isSet(a)   !isNotSet(a)
 
#define ks_abs(x)   (x)<0 ? -(x) : (x)
 
#define ks_round(x)   ((x)>=0?(int)((x)+0.5):(int)((x)-0.5))
 
#define KS_RUP_GRD_FLOAT(X)   ( fmod((X), GRAD_UPDATE_TIME) > 0 ? RUP_GRD( (X) + 1 ) : RUP_GRD( (X) ) )
 
#define KS_SRC_FILE   (__builtin_strrchr (__FILE__, '/') ? __builtin_strrchr (__FILE__, '/') + 1 : __FILE__)
 
#define KS_THROW(format, ...)   ks_error("%s (%s:%d): " format, __FUNCTION__, KS_SRC_FILE, __LINE__, ##__VA_ARGS__)
 
#define KS_RAISE_TRACE_FN   ks_error
 
#define KS_RAISE(status)   if ((status) != SUCCESS) { KS_RAISE_TRACE_FN("===> %s (%s:%d)", __FUNCTION__, KS_SRC_FILE, __LINE__); return ADVISORY_FAILURE; }
 
#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_EPI_MIN_INTERTRAIN_SSPGAP   80
 
#define L_REF   10
 
#define KS_SSP_PRE_READ_TIME   256
 
#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_PLANE_AX   ((exist(opplane) == PSD_AXIAL) || (exist(opplane) == PSD_OBL && exist(opobplane) == PSD_AXIAL))
 
#define KS_PLANE_COR   ((exist(opplane) == PSD_COR) || (exist(opplane) == PSD_OBL && exist(opobplane) == PSD_COR))
 
#define KS_PLANE_SAG   ((exist(opplane) == PSD_SAG) || (exist(opplane) == PSD_OBL && exist(opobplane) == PSD_SAG))
 
#define KS_DESCRIPTION_LENGTH   128
 
#define KS_MAXWAVELEN   10000
 
#define KS_MAXUNIQUE_RF   30
 
#define KS_MAXUNIQUE_TRAP   200
 
#define KS_MAXUNIQUE_WAVE   100
 
#define KS_MAXUNIQUE_WAIT   10
 
#define KS_READCONTROL_MAXSTATE   2
 
#define KS_MAXUNIQUE_READWAVE   2
 
#define KS_MAXUNIQUE_SEQUENCES   200
 
#define KS_MAX_SEQUENCE_PLAYOUTS   200
 
#define KS_MAX_PHASEDYN   2049
 
#define KS_MAXUNIQUE_READ   2048
 
#define KS_MAX_NUM_KSPACES   16
 
#define KS_MAX_NUM_USERDATA   10
 
#define KS_EPI_MINBLIPAREA   100
 
#define KS_ECHOTRAIN_MAX_TRAPS   4
 
#define KS_ECHOTRAIN_MAX_WAVES   2
 
#define KS_INITZEROS(n)   {[0 ... (n-1)] = 0}
 
#define KS_INITVALUE(n, a)   {[0 ... (n-1)] = a}
 
#define KS_INIT_BASE(OBJECT)   {0,0,0,NULL,sizeof(OBJECT)}
 
#define KS_INIT_FINFO   {0.0, 0, 0.0, 0.0, KS_NOTSET, 0, 0, 0}
 
#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_RT_SCALE_LOG   {NULL, NULL, 0, 0, 0, 0}
 
#define KS_INIT_WAVEVALUE   0
 
#define KS_WAVE_MAXNSTATES   16
 
#define KS_INIT_WAVEFORM   KS_INITVALUE(KS_MAXWAVELEN, KS_INIT_WAVEVALUE)
 
#define KS_INIT_WAVE   {KS_INIT_BASE(KS_WAVE), KS_INIT_DESC, 0, 0, KS_INIT_WAVEFORM, {NULL}, {0,0,0}, KS_NOTSET, NULL, NULL, NULL, KS_INIT_RT_SCALE_LOG, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, NULL, NULL}
 
#define KS_INIT_WAIT   {KS_INIT_BASE(KS_WAIT), KS_INIT_DESC, 0, KS_NOTSET, NULL, NULL, NULL, KS_INIT_RT_SCALE_LOG }
 
#define KS_INIT_ISIROT   {KS_INIT_WAIT, KS_INIT_WAIT, NULL, 0, 0, 0, 0}
 
#define KS_INIT_TRAP   {KS_INIT_BASE(KS_TRAP), KS_INIT_DESC, 0.0, 0.0, 0, 0, 0, {0, 0, 0}, {0, 0, 0}, {1, 1, 1}, 1.0f, NULL, NULL, NULL, KS_INIT_RT_SCALE_LOG}
 
#define KS_INIT_READ_RESAMPLER   {NULL, NULL, NULL, 0, 0, {0}, 0, 0}
 
#define KS_INIT_READ   {KS_INIT_BASE(KS_READ), KS_INIT_DESC, 0, KS_NOTSET, KS_INIT_FINFO, NULL, 0, NULL, KS_INIT_READ_RESAMPLER}
 
#define KS_INIT_READ_EPI   {KS_INIT_BASE(KS_READ), KS_INIT_DESC, 0, 250.0, KS_INIT_FINFO, NULL, 0, NULL, KS_INIT_READ_RESAMPLER}
 
#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_WAVE, 0}
 
#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_WAVE, 0}
 
#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, FALSE}
 
#define KS_INIT_PHASEENCODING_COORD   {KS_NOTSET, KS_NOTSET}
 
#define KS_INIT_PHASEENCODING_PLAN   {KS_NOTSET, KS_NOTSET, 0, KS_INIT_DESC, 0, {CARTESIAN_COORD, CARTESIAN_COORD}}
 
#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_TRAP, KS_INIT_TRAP, KS_INIT_PHASER, KS_INIT_PHASER, KS_EPI_BIPOLAR, 1, 0, 0, 0, 0, 1.0, KS_EPI_MINBLIPAREA, 0}
 
#define KS_INIT_READWAVE   {KS_INIT_WAVE, KS_INIT_WAVE, KS_INIT_READ, 0, 0, 0.0, 0.0, 0, 0, 0.0, 0, 0, {KS_INIT_WAVEFORM}, {KS_INIT_WAVEFORM}}
 
#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, 0}
 
#define KS_INIT_SELRF   {KS_INIT_RF, KS_NOTSET, 2.0, 0, 0.0, 0.0, KS_INIT_TRAP, KS_INIT_TRAP, KS_INIT_TRAP, KS_INIT_WAVE, KS_NOTSET, KS_NOTSET, 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, KS_INITZEROS(KS_MAXUNIQUE_WAIT), 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_DISCRETE_REPRESENTATION_SYMMETRIC, KS_INIT_DESC, KS_INIT_SEQ_HANDLE, KS_INIT_GRADRFCTRL, NULL, 0}
 
#define KS_INIT_SEQ_COLLECTION   {0, KS_SEQ_COLLECTION_ALLOWNEW, FALSE, KS_INITVALUE(KS_MAXUNIQUE_SEQUENCES, NULL), 0, KS_INITVALUE(KS_MAX_SEQUENCE_PLAYOUTS, 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_SPOIL_X   1
 
#define KS_SPOIL_Y   2
 
#define KS_SPOIL_Z   4
 
#define ANYINVERSION   (opirprep || opt1flair || opt2flair)
 
#define KSCOMMON_INIT_GRAD_PARAMS   {0, 0, 0, 0}
 
#define KSCOMMON_INIT_RESAMPLER   {0, 0, 0.0, KSCOMMON_INIT_GRAD_PARAMS, KSCOMMON_INIT_GRAD_PARAMS, KSCOMMON_INIT_GRAD_PARAMS}
 
#define KS_INIT_PHASEENCODING_SHOTCOORDS   {0, NULL}
 
#define KS_INIT_DYNAMIC_STATE   {0, 0, 0, 0, 0, 0, KS_INIT_PHASEENCODING_SHOTCOORDS, 0, 0, 0, 0, 0, 0, 0, 0, {0.0}, 0, 0, KS_MAT4x4_IDENTITY, KS_MAT4x4_IDENTITY}
 
#define KS_DABARRAY_KS_RECON_RESERVED   2
 
#define KS_DABARRAY_ONLINE_RECON_RESERVED   6
 
#define KS_DABARRAY_FULL_LENGTH   28
 
#define KS_DABARRAY_AVAILABLE_BYTES   (KS_DABARRAY_FULL_LENGTH - KS_DABARRAY_ONLINE_RECON_RESERVED - KS_DABARRAY_KS_RECON_RESERVED)
 
#define KS_DEFAULT_READ_TYPE   KS_READ_NOTSET
 
#define KS_INIT_READCONTROL_STATE   {KS_NOTSET, KS_NOTSET}
 
#define KS_INIT_READCONTROL_PULSEGEN   {KS_DEFAULT_READ_TYPE, 0, 0, 1.0, KS_NOTSET}
 
#define KS_INIT_READ_CONTROL   {{KS_INIT_READCONTROL_STATE}, KS_INIT_READCONTROL_PULSEGEN}
 
#define KS_INIT_ECHOTRAIN   {KS_INITVALUE(KS_MAXUNIQUE_READ,KS_INIT_READ_CONTROL), KS_INITVALUE(KS_ECHOTRAIN_MAX_TRAPS,KS_INIT_READTRAP), KS_INITVALUE(KS_ECHOTRAIN_MAX_WAVES,KS_INIT_READWAVE), 0, 0, 0, {0}, 1}
 
#define KS_INIT_KSPACE_ACQ   {NULL, KS_NOTSET, {KS_NOTSET, KS_NOTSET, KS_NOTSET}}
 
#define KS_INIT_PHASEENCODING_REPEAT_DESIGN   {NO_REPEAT, KS_NOTSET}
 
#define KS_DEFAULT_KSACQ_RECONSHEDULE_ENTRY   {0, 0, -1, -1}
 
#define KS_INIT_CRUSHER_CONSTRAINT   {0, 0, 0, 0, KS_MINPLATEAUTIME, KS_CRUSHER_STRATEGY_FASTEST}
 
#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 float KS_MAT3x3f[9]
 
typedef double KS_MAT4x4[16]
 
typedef float KS_MAT4x4f[16]
 

Enumerations

enum  KS_PF_EARLYLATE { KS_PF_EARLY = -1, KS_PF_NO = 0, KS_PF_LATE = 1 }
 
enum  KS_PLOT_FILEFORMATS { KS_PLOT_OFF = 0, KS_PLOT_MAKEPDF, KS_PLOT_MAKESVG, KS_PLOT_MAKEPNG }
 
enum  KS_PLOT_EXCITATION_MODE { KS_PLOT_STANDARD = 0, KS_PLOT_NO_EXCITATION }
 
enum  KS_PLOT_PASS_MODE { KS_PLOT_PASS_WAS_DUMMY = 0, KS_PLOT_PASS_WAS_CALIBRATION, KS_PLOT_PASS_WAS_STANDARD }
 
enum  KS_PLOT_SLICEGROUP_MODE { KS_PLOT_SG_DUMMY = 0, KS_PLOT_SG_CALIBRATION, KS_PLOT_SG_ACQUISITION, KS_PLOT_SG_NOTSET }
 
enum  KS_DISCRETE_REPRESENTATION { KS_DISCRETE_REPRESENTATION_SYMMETRIC = 0, KS_DISCRETE_REPRESENTATION_GE }
 
enum  KS_COORDINATE_TYPE { CARTESIAN_COORD = 0, RADIAL_COORD = 1 }
 
enum  KS_READ_TYPE { KS_READ_NOTSET = 0, KS_READ_TRAP, KS_READ_WAVE }
 
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_COVERAGE { ELLIPTICAL = 0, RECTANGULAR = 1 }
 
enum  KS_MTF_DIRECTION { MTF_Y = 0, MTF_Z = 1, MTF_R = 2, MTF_T = 3 }
 
enum  KS_VIEW_SORT_ORDER {
  Y_Z = 0, Z_Y = 1, R_T = 2, T_R = 3,
  Y_R = 4, Z_R = 5, SEC_T = 6
}
 
enum  KS_VIEW_MTF_PROFILE {
  LINEAR_SWEEP = -1, PIVOT_CENTER_OUTER_LINEAR = 0, LCPO = 1, SPLIT_SHOTS_LCPO = 2,
  LINEAR_ROLL = 3, PIVOT_ROLL = 4, SPLIT_SHOTS_AMERICAN = 5, SPLIT_SHOTS_DUTCH = 6
}
 
enum  RADIUS_PATTERN { LINEAR = 0, EXPONENTIAL = 1 }
 
enum  KS_REPEAT_MODE {
  NO_REPEAT = 0, INTERLEAVED_LONGER_SHOTS = 1, SEQUENTIAL_LONGER_SHOTS = 2, INTERLEAVED_EXTRA_SHOTS = 3,
  SEQUENTIAL_EXTRA_SHOTS = 4
}
 
enum  KS_RADIAL_SAMPLING_MODE { CARTESIAN = 0, RADIAL_THROUGH_CENTER = 1, RADIAL_SOS = 2 }
 
enum  KS_RADIAL_SAMPLING_COVERAGE { SAMPLE_180 = 0, SAMPLE_360 = 1 }
 
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_epitrain_mode { KS_EPI_BIPOLAR = 0, KS_EPI_SPLITODDEVEN = 1, KS_EPI_FLYBACK = 2 }
 
enum  ks_enum_epiblipsign { KS_EPI_POSBLIPS = 1, KS_EPI_NOBLIPS = 0, KS_EPI_NEGBLIPS = -1 }
 
enum  ks_enum_diffusion { KS_EPI_DIFFUSION_OFF, KS_EPI_DIFFUSION_ON }
 
enum  ks_enum_imsize { KS_IMSIZE_NATIVE = 0, 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_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 = 0, 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 = 0, KS_GRADWAVE_RELATIVE = 1 }
 
enum  ks_enum_crusher_strategy {
  KS_CRUSHER_STRATEGY_FASTEST = 0, KS_CRUSHER_STRATEGY_EXACT, KS_CRUSHER_STRATEGY_MIN_SLEW, KS_CRUSHER_STRATEGY_MAX_AREA,
  KS_CRUSHER_STRATEGY_PLATEAU
}
 

Functions

_cvfloat * opuser_from_number (_cvfloat *cv)
 
int isNaN (float a)
 
STATUS ks_bitmask_set (unsigned int out, const unsigned int in, const unsigned int size, const unsigned int offset)
 
unsigned int ks_bitmask_get (const unsigned int in, const unsigned int size, const unsigned int offset)
 
int ks_isIceHardware ()
 
int ks_default_ssitime ()
 
KS_PEPLAN_SHOT_DISTRIBUTION ks_peplan_distribute_shots (const int etl, const int num_coords, const int center)
 
void ks_init_read (KS_READ *read)
 
void ks_init_trap (KS_TRAP *trap)
 
void ks_init_rtscalelog (KS_RT_SCALE_LOG *const rtscale)
 
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_readwave (KS_READWAVE *readwave)
 
void ks_init_phaser (KS_PHASER *phaser)
 
void ks_init_epi (KS_EPI *epi)
 
void ks_init_gradrfctrl (KS_GRADRFCTRL *gradrfctrl)
 
void ks_init_seqcontrol (KS_SEQ_CONTROL *seqcontrol)
 
void ks_init_seqcollection (KS_SEQ_COLLECTION *seqcollection)
 
void ks_init_echotrain (KS_ECHOTRAIN *const echotrain)
 
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_addwaittogradrfctrl (KS_GRADRFCTRL *gradrfctrl, KS_WAIT *wait) WARN_UNUSED_RESULT
 
STATUS ks_eval_wait (KS_WAIT *wait, const char *const desc, int pg_duration, 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_trap_rotation_invariant_75_max_slewrate (KS_TRAP *trap, const char *const desc)
 
STATUS ks_eval_crusher (KS_WAVE *crusher, const float end_amp, const ks_crusher_constraints crusher_constraints)
 
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_readwave_constrained (KS_READWAVE *readwave, KS_WAVE *acq_waves, const int numstates, int flag_symmetric_padding, ks_crusher_constraints pre_crusher_constraints, ks_crusher_constraints post_crusher_constraints)
 
STATUS ks_eval_readwave (KS_READWAVE *readwave, KS_WAVE *acq_wave, float pre_crusher_area, float post_crusher_area, int flag_symmetric_padding, ks_enum_crusher_strategy pre_strategy, ks_enum_crusher_strategy post_strategy)
 
STATUS ks_eval_readwave_multistated (KS_READWAVE *readwave, KS_WAVE *acq_waves, const int num_waves, float pre_crusher_area, float post_crusher_area, int flag_symmetric_padding, ks_enum_crusher_strategy pre_strategy, ks_enum_crusher_strategy post_strategy)
 
STATUS ks_eval_readtrap1p (KS_READTRAP *readtrap, const char *const desc) WARN_UNUSED_RESULT
 
int ks_eval_kynover_fromnex (int yres, float partial_ky, 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, const int res, const int duration, const KS_WAVEFORM waveform) WARN_UNUSED_RESULT
 
STATUS ks_eval_mirrorwave (KS_WAVE *wave)
 
STATUS ks_eval_mirrorwaveform (KS_WAVEFORM waveform, int res)
 
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, int MTreduction) WARN_UNUSED_RESULT
 
STATUS ks_eval_mt_rf_binomial (KS_RF *rf, const char *const desc, int sub_pulse_dur, int nPulses, int prp)
 
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, int nslices, float slthick, float slspace)
 
float ks_eval_sms_calc_slice_gap_from_scan_info (int sms_multiband_factor, int nslices, const SCAN_INFO *org_slice_positions)
 
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_coords2wave (KS_WAVE *wave, float *t, float *G, int num_coords, int dwell, const char *desc) WARN_UNUSED_RESULT
 
STATUS ks_eval_append_two_waves (KS_WAVE *first_wave, KS_WAVE *second_wave) WARN_UNUSED_RESULT
 
STATUS ks_eval_append_two_waveforms (KS_WAVEFORM first_waveform, KS_WAVEFORM second_waveform, int res1, int res2) 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
 
STATUS ks_eval_echotrain (KS_ECHOTRAIN *const echotrain)
 
int ks_echotrain_get_max_kspace_index (KS_ECHOTRAIN *echotrain)
 
PSD_FILTER_GEN ks_get_maxsize_finfo (PSD_FILTER_GEN my_psd_filter_spec[])
 
void ks_grad_heat_reset ()
 
void ks_grad_heat_init (KS_SEQ_COLLECTION *seqcollection_p, STATUS(*play)(const INT max_encode_mode, int nargs, void **args), int nargs, void **args)
 
int ks_eval_rflimits (KS_SAR *sar, KS_SEQ_COLLECTION *seqcollection)
 
STATUS ks_eval_gradlimits (int *newtime, KS_SEQ_COLLECTION *seqcollection, const LOG_GRAD *log_grad, STATUS(*play)(const INT max_encode_mode, int nargs, void **args), int nargs, void **args)
 
STATUS ks_eval_hwlimits (int *newtime, KS_SEQ_COLLECTION *seqcollection, const LOG_GRAD *log_grad, STATUS(*play)(const INT max_encode_mode, int nargs, void **args), int nargs, void **args)
 
s64 ks_eval_getduration (KS_SEQ_COLLECTION *seqcollection, STATUS(*play)(const INT max_encode_mode, int nargs, void **args), int nargs, void **args)
 
int ks_eval_mintr (int nslices, KS_SEQ_COLLECTION *seqcollection, int(*play_loop)(int, int, void **), int nargs, void **args)
 
int ks_eval_maxslicespertr (int TR, KS_SEQ_COLLECTION *seqcollection, 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)
 
s64 ks_eval_seqcollection_gettotalduration (KS_SEQ_COLLECTION *seqcollection)
 
s64 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_create_suffixed_description (char *const out, const char *const prefix, const char *suffix,...) __attribute__((format(printf
 
void void ks_read_header_pool (int *exam_number, int *series_number, int *run_number)
 
float ks_rt_scale_log_get (const KS_RT_SCALE_LOG *log, int instance_idx, int playout_idx)
 
int ks_rt_scale_log_get_state (const KS_RT_SCALE_LOG *log, int instance_idx, int playout_idx)
 
void ks_rt_scale_log_set (KS_RT_SCALE_LOG *log, int instance_idx, float ampscale)
 
void ks_rt_scale_log_set_state (KS_RT_SCALE_LOG *log, int instance_idx, int state)
 
void ks_rt_scale_log_next (KS_RT_SCALE_LOG *log)
 
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)
 
STATUS ks_phaseencoding_memorypool_init (KS_PHASEENCODING_COORD *entries, uint64_t size)
 
void ks_init_dynamic (KS_DYNAMIC_STATE *dynamic)
 
int ks_phaseencoding_isvalid (const KS_PHASEENCODING_PLAN *phaseenc_plan_ptr)
 
KS_PHASEENCODING_COORD ks_phaseencoding_get (const KS_PHASEENCODING_PLAN *phaseenc_plan_ptr, int encode, int shot)
 
KS_PHASEENCODING_SHOTCOORDS ks_phaseencoding_get_shot (const KS_PHASEENCODING_PLAN *phaseenc_plan_ptr, int shot)
 
STATUS ks_repeat_peplan (KS_PHASEENCODING_PLAN *peplan, const KS_PHASEENCODING_REPEAT_DESIGN *const repeat)
 
void ks_phaseencoding_set (KS_PHASEENCODING_PLAN *phaseenc_plan_ptr, int encode, int shot, int ky, int kz)
 
void ks_phaseencoding_print (const KS_PHASEENCODING_PLAN *phaseenc_plan_ptr)
 
STATUS ks_phaseencoding_alloc (KS_PHASEENCODING_PLAN *phaseenc_plan_ptr, int etl, int num_shots) WARN_UNUSED_RESULT
 
STATUS ks_phaseencoding_generate_simple (KS_PHASEENCODING_PLAN *phaseenc_plan_ptr, const char *const desc, KS_PHASER *phaser, KS_PHASER *zphaser, int encodes_per_shot) WARN_UNUSED_RESULT
 
STATUS ks_phaseencoding_generate_simple_ellipse (KS_PHASEENCODING_PLAN *phaseenc_plan_ptr, const char *const desc, KS_PHASER *phaser, KS_PHASER *zphaser) WARN_UNUSED_RESULT
 
STATUS ks_phaseencoding_generate_2d_singleshot_cal (KS_PHASEENCODING_PLAN *phaseenc_plan_ptr, int yres) WARN_UNUSED_RESULT
 
int ks_comp_kcoord_r_t (const void *a, const void *b)
 
int ks_comp_kcoord_z_y (const void *a, const void *b)
 
int ks_comp_kcoord_y_z (const void *a, const void *b)
 
int ks_comp_kview_r_t (const void *a, const void *b)
 
int ks_comp_kview_y_z (const void *a, const void *b)
 
int ks_comp_kview_e_t (const void *a, const void *b)
 
int ks_comp_kview_e_z (const void *a, const void *b)
 
int ks_cal_from_nacslines (int R, int nacslines)
 
int ks_get_ry_from_kcoords (KS_KCOORD *coord, const int num_coords)
 
int ks_get_rz_from_kcoords (KS_KCOORD *coord, const int num_coords)
 
void ks_get_matrix_from_kcoords (KS_KCOORD *K, const int num_coords, int *y, int *z)
 
STATUS ks_generate_3d_coords_caipi (KS_KSPACE_ACQ *kacq, int Ny, int Nz, int nover_y, int nover_z, int Ry, int Rz, int cal_y, int cal_z, KS_COVERAGE cal_coverage, KS_COVERAGE acq_coverage, int dy, int dz, int step_y, int step_z) WARN_UNUSED_RESULT
 
STATUS ks_generate_3d_coords_radial (KS_KSPACE_ACQ *kacq, int tiny_level, int Ny, int Nz, int Ry, int Rz, KS_RADIAL_SAMPLING_MODE radial_mode, KS_RADIAL_SAMPLING_COVERAGE radial_sampling_coverage) WARN_UNUSED_RESULT
 
STATUS ks_generate_3d_coords_simple (KS_KSPACE_ACQ *kacq, int Ny, int Nz, int nover_y, int nover_z, int Ry, int Rz, int cal_y, int cal_z, KS_COVERAGE cal_coverage, KS_COVERAGE acq_coverage) WARN_UNUSED_RESULT
 
STATUS ks_generate_2d_coords_cal (KS_KSPACE_ACQ *kacq, int num_desired_coords, int yres) WARN_UNUSED_RESULT
 
STATUS ks_generate_3d_coords_poisson_disc (KS_KSPACE_ACQ *kacq, int Ny, int Nz, float min_spacing, float max_spacing, KS_COVERAGE cal_coverage, int cal_y, int cal_z, RADIUS_PATTERN pattern) WARN_UNUSED_RESULT
 
STATUS ks_generate_3d_coords_poisson_disc_R (KS_KSPACE_ACQ *kacq, int Ny, int Nz, float R, KS_COVERAGE cal_coverage, int cal_y, int cal_z, RADIUS_PATTERN pattern) WARN_UNUSED_RESULT
 
STATUS ks_generate_3d_coords_epi (KS_KSPACE_ACQ *kacq, const KS_EPI *epitrain, const ks_enum_epiblipsign blipsign, const int Ry, const int caipi_delta) WARN_UNUSED_RESULT
 
void ks_pe_linear (int *view_order, int ETL)
 
void ks_pe_linear_roll (int *view_order, int ETL, int c)
 
void ks_pe_pivot_roll (int *view_order, int ETL, int c)
 
void ks_pivot_linear_center_symk (int *view_order, int ETL, int c)
 
STATUS ks_pe_permute_pclo_rad (int *view_order, const int etl, const int center)
 
void ks_pivot_specific_center_symk (int *view_order, int ETL, int c)
 
void ks_pivot_specific_center_radk (int *view_order, int ETL, int c)
 
STATUS ks_generate_peplan_from_kcoords (const KS_KSPACE_ACQ *kacq, KS_PHASEENCODING_PLAN *peplan, KS_MTF_DIRECTION mtf_direction, KS_VIEW_SORT_ORDER encode_shot_assignment_order, KS_VIEW_MTF_PROFILE mtf_profile, const int etl, int center, const KS_COORDINATE_TYPE *const coord_types) WARN_UNUSED_RESULT
 
STATUS ks_generate_peplan_from_kcoords_american (const KS_KSPACE_ACQ *kacq, KS_PHASEENCODING_PLAN *peplan, const KS_MTF_DIRECTION mtf_direction, const int etl, const int center) WARN_UNUSED_RESULT
 
STATUS ks_fse_calcecho (double *bestecho, double *optecho, int *nacqlines_to_kspacecenter, const KS_PHASER *const pe, KS_PF_EARLYLATE 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_PF_EARLYLATE pf_direction, const int numileavestoacq, ks_enum_sweep_order sweep_order, int numsegments, const int caipi_delta) WARN_UNUSED_RESULT
 
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)
 
STATUS ks_sms_check_divisibility (int *inpass_interleaves, const int sms_factor, const int nslices_per_pass, const int nslices)
 
int ks_calc_slice_acquisition_order_smssingleacq (DATA_ACQ_ORDER *dacq, int nslices)
 
float ks_calc_integrate_coords (float *t, float *G, int num_coords)
 
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)
 
STATUS ks_print_acqwaves (KS_SEQ_COLLECTION *seqcollection)
 
void ks_print_readwave (const KS_READWAVE *const readwave, const char *filename)
 
void ks_print_wave_part (const KS_WAVE *const wave, const char *filename, int startidx, int stopidx)
 
void ks_print_wave (const KS_WAVE *const wave, const char *filename)
 
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_readwaves (KS_ECHOTRAIN *const echotrain, const char *suffix, int rhkacq_uid)
 
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 ()
 
unsigned short ks_cycles_to_iphase (double cycles)
 
unsigned short ks_degrees_to_iphase (double degrees)
 
unsigned short ks_radians_to_iphase (double radians)
 
float ks_calc_selgradamp (float rfbw, float slthick)
 
void ks_wave_compute_params (KS_WAVE *const wave)
 
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, int *index)
 
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)
 
int ks_wave_time2area (const KS_WAVE *wave, float area_in)
 
float ks_waveform_area (const KS_WAVEFORM waveform, int start, int end, int dwelltime)
 
long ks_iwave_area (const KS_IWAVE iwave, int start, int end, int dwelltime)
 
float ks_wave_area (const KS_WAVE *wave, int start, int end)
 
float ks_wave_full_area (const KS_WAVE *wave)
 
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_eval_readwave_negatestate (KS_READWAVE *readwave, int state)
 
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, float max, float fs_factor) 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_addwaveformstate (KS_WAVE *wave, KS_WAVEFORM waveform, int state_index)
 
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
 
KS_SEQLOC ks_pg_get_trap_loc (KS_TRAP *trap, int instance)
 
KS_SEQLOC ks_pg_get_wave_loc (KS_WAVE *wave, int instance)
 
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_double_to_float (KS_MAT4x4f out, const KS_MAT4x4 in)
 
void ks_mat4_float_to_double (KS_MAT4x4 out, const KS_MAT4x4f in)
 
void ks_mat4_setgeometry (KS_MAT4x4 lhs, float x, float y, float z, float xr, float yr, float zr)
 
void ks_mat4f_setgeometry (KS_MAT4x4f 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_mat3f_apply (float *w, const KS_MAT3x3f R, const float *v)
 
void ks_mat3_invapply (double *w, const KS_MAT3x3 R, const double *v)
 
void ks_mat3f_invapply (float *w, const KS_MAT3x3f R, const float *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_update_slice_location_float (SCAN_INFO *new_loc, const SCAN_INFO orig_loc, const KS_MAT4x4f M_physical, const KS_MAT4x4f M_logical)
 
void ks_scan_rotate (SCAN_INFO slice_pos)
 
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_loc_by_timeboard (KS_SEQLOC *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 (const 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 pass_mode)
 
void ks_plot_slicetime_end ()
 
void ks_plot_write_peplans (const KS_PHASEENCODING_PLAN *const *plans, const int num_plans, FILE *fp)
 
void ks_plot_host_slicetime_begin ()
 
void ks_plot_host_slicetime (const 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 pass_mode)
 
void ks_plot_host_slicetime_end ()
 
void ks_plot_host (const KS_SEQ_COLLECTION *seqcollection, const KS_PHASEENCODING_PLAN *plan)
 
void ks_plot_host_seqctrl (const KS_SEQ_CONTROL *ctrl, const KS_PHASEENCODING_PLAN *plan)
 
void ks_plot_host_seqctrl_manyplans (const KS_SEQ_CONTROL *ctrl, const KS_PHASEENCODING_PLAN **plan, const int num_plans)
 
void ks_plot_tgt_reset (KS_SEQ_CONTROL *ctrl)
 
void ks_plot_filename (char *fname, char *path, char *outputdir, char *outputdir_uid, const char *const seq_desc)
 
void ks_plot_saveconfig (KS_SEQ_CONTROL *ctrl)
 
void ks_plot_tgt_addframe (KS_SEQ_CONTROL *ctrl)
 
int ks_get_center_encode (const KS_PHASEENCODING_PLAN *const peplan, int shot, int yres, int zres)
 
int ks_peplan_find_center_from_linear_sweep (KS_KCOORD *const K, KS_VIEW *view, const int num_coords, const KS_MTF_DIRECTION mtf_direction, const int sweep_sign, const int segment_size, const int start_encode)
 
int ks_get_center_encode_linearsweep (const KS_KSPACE_ACQ *kacq, const int etl, const KS_PF_EARLYLATE pf_earlylate_te)
 
float ks_calc_caipi_scale (const int ky, const int R, const int caipi_factor)
 
float ks_calc_caipi_phase (const float sms_slice_gap, const int sms_multiband_factor, const SCAN_INFO *slice_info, const float caipi_blip_area, const int slice_encode_dir)
 
float ks_calc_caipi_offset_phase (const float sms_slice_gap, const int sms_multiband_factor, const SCAN_INFO *slice_info, const int caipi_factor, const float caipi_blip_area, const int ky, const int R, const int slice_encode_dir)
 
STATUS ks_eval_caipiblip (KS_TRAP *caipiblip, const int caipi_factor, const float sms_slice_gap, const KS_DESCRIPTION seq_desc)
 
int ks_eval_sms_rf_scan_info (SCAN_INFO *sms_slice_positions, const SCAN_INFO *org_slice_positions, int sms_multiband_factor, int nslices)
 
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 newwaveform, int state)
 
void ks_scan_setwavestate (KS_WAVE *wave, int state, int instance)
 
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_offsetfov3D_readwave (KS_READWAVE *readwave, int instanceno, SCAN_INFO sliceinfo, float ky, float phasefovratio, float kz, float zphasefovratio, float rcvphase)
 
void ks_scan_offsetfov_iso_readwave (KS_READWAVE *readwave, int instanceno, SCAN_INFO sliceinfo, double ky, double kz, double rcvphase)
 
void ks_scan_offsetfov_iso_readwave_oneread (KS_READWAVE *readwave, KS_READ *read, int gradinstanceno, int readinstanceno, SCAN_INFO sliceinfo, double ky, double kz, double rcvphase)
 
void ks_scan_offsetfov_readwave (KS_READWAVE *readwave, int instanceno, SCAN_INFO sliceinfo, float ky, float phasefovratio, float rcvphase)
 
int ks_eval_append_blip (KS_WAVE *wave, const float s, const int max_points, const float area)
 
unsigned short ks_scan_omegatrap_hz (KS_TRAP *trap, int instanceno, float Hz, int omega_iwave_phase_at_null)
 
unsigned short ks_scan_omegawave_hz (KS_WAVE *wave, int instanceno, float Hz, int omega_iwave_phase_at_null)
 
void ks_scan_wait (KS_WAIT *wait, int waitperiod)
 
void ks_scan_trap_ampscale (KS_TRAP *trap, int instanceno, float ampscale)
 
void ks_scan_wave_ampscale (KS_WAVE *wave, 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)
 
void ks_scan_phaser_average (KS_PHASER *phaser, int instanceno)
 
void ks_scan_phaser_max (KS_PHASER *phaser, int instanceno)
 
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_sms (KS_EPI *epi, int echo, SCAN_INFO sliceinfo, KS_PHASEENCODING_COORD starting_coord, ks_enum_epiblipsign blipsign, float rcvphase, int sms_factor, float sms_slice_gap, int caipi_factor, int sms_slice_encoding_direction)
 
void ks_scan_epi_shotcontrol (KS_EPI *epi, int echo, SCAN_INFO sliceinfo, KS_PHASEENCODING_COORD starting_coord, ks_enum_epiblipsign blipsign, float rcvphase)
 
void ks_scan_epi_loadecho (KS_EPI *epi, int echo, int storeecho, int slice, KS_PHASEENCODING_COORD starting_coord, ks_enum_epiblipsign blipsign, KS_DYNAMIC_STATE *dynamic)
 
void ks_loaddab_reserved (WF_PULSE_ADDR echo, short state, short resampler_index)
 
void ks_loaddab (WF_PULSE_ADDR echo, const char *dab_array)
 
void ks_loaddab_full (WF_PULSE_ADDR echo, const char *custom_dab_array)
 
const KS_READks_get_read_in_echotrain (const KS_ECHOTRAIN *echotrain, const int readout_index)
 
WF_PULSE_ADDR ks_get_wf_pulse (KS_ECHOTRAIN *echotrain, const int readout_index)
 
LONG ks_get_override_R1 (KS_ECHOTRAIN *echotrain, const int readout_index)
 
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)
 
int ks_scan_gettherightshot (const KS_DYNAMIC_STATE *dynamic)
 
float ks_get_trapamp_instance (const KS_TRAP *trap, int instance)
 
void ks_tgt_reset_gradrfctrl (KS_GRADRFCTRL *gradrfctrl)
 
int ks_eval_clear_readwave (KS_READWAVE *readwave)
 
void ks_polyder (const double *coeffs, const int order, double *der_coeffs)
 
void ks_polyval (const double *coeffs, const int order, const double *x, const int numx, double *values)
 
void ks_polyval_f (const double *coeffs, const int order, const float *x, const int numx, float *values)
 
double ks_Newtons_method (const double *coeffs, const int order, const double x0, const int num_iter)
 
int ks_pg_readwave (KS_READWAVE *readwave, KS_SEQLOC loc, KS_SEQ_CONTROL *ctrl)
 
STATUS ks_pg_echotrain_readout (KS_ECHOTRAIN *echotrain, int readout_index, int echo_time, KS_ECHOTRAIN_READOUT_TIMING *timing, KS_SEQ_CONTROL *ctrl)
 
int ks_numplaced (KS_BASE *base)
 
void ks_set_opcode (WF_PULSE_ADDR echo, short opcode)
 
int ks_write_vector_bf (float *vec, uint32_t numel, const char *fname)
 
FILE * ks_open_file_in_embed (const char *fname, const char *write_mode)
 

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

◆ KS_GOLDEN_MEANS_1D

#define KS_GOLDEN_MEANS_1D   (1.0+sqrt(5.0))/2.0 /* approx. 1.618033988749895 */

◆ KS_GOLDEN_MEANS_2D_1

#define KS_GOLDEN_MEANS_2D_1   1.0/pow((1.0/pow((sqrt(3.1E+1)*sqrt(1.08E+2))/1.08E+2+2.9E+1/5.4E+1,1.0/3.0)/9.0+pow((sqrt(3.1E+1)*sqrt(1.08E+2))/1.08E+2+2.9E+1/5.4E+1,1.0/3.0)+1.0/3.0),2.0) /* approx. 0.4655712319 */

◆ KS_GOLDEN_MEANS_2D_2

#define KS_GOLDEN_MEANS_2D_2   1.0/(1.0/pow((sqrt(3.1E+1)*sqrt(1.08E+2))/1.08E+2+2.9E+1/5.4E+1,1.0/3.0)/9.0+pow((sqrt(3.1E+1)*sqrt(1.08E+2))/1.08E+2+2.9E+1/5.4E+1,1.0/3.0)+1.0/3.0) /* approx. 0.6823278038 */

◆ KS_ANGLE_PER_INT_16_MAX_180

#define KS_ANGLE_PER_INT_16_MAX_180   (180.0 / 0x8000)

◆ KS_ANGLE_PER_UINT_16_MAX_180

#define KS_ANGLE_PER_UINT_16_MAX_180   (180.0 / 0xF000)

◆ KS_SIGN

#define KS_SIGN (   x)    ((x < 0) ? -1 : 1)

◆ 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

◆ INT_MAX

#define INT_MAX   2147483647

◆ 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:77
#define DEFAULT_SCAN_INFO_HEAD
Definition: KSFoundation.h:69

◆ 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:77
#define DEFAULT_SCAN_INFO_HEAD
Definition: KSFoundation.h:69

◆ 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:77
#define DEFAULT_SCAN_INFO_HEAD
Definition: KSFoundation.h:69

◆ 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:77
#define DEFAULT_SCAN_INFO_HEAD
Definition: KSFoundation.h:69

◆ 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:77
#define DEFAULT_SCAN_INFO_HEAD
Definition: KSFoundation.h:69

◆ 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:77
#define DEFAULT_SCAN_INFO_HEAD
Definition: KSFoundation.h:69

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

◆ KS_EXPAND_OPUSER

#define KS_EXPAND_OPUSER (   number)    number, &_opuser ## number

expand opuser number. For instance KS_EXPAND_OPUSER(5) becomes 5 and &_opuser5

◆ KS_EXPAND_CVFLOAT

#define KS_EXPAND_CVFLOAT (   number)    &_opuser ## number

◆ KS_OPUSER_FROM_NUM

#define KS_OPUSER_FROM_NUM (   number)    opuser_from_number(KS_EXPAND_CVFLOAT(number));

◆ KS_OPUSER_VALUE_FROM_NUM

#define KS_OPUSER_VALUE_FROM_NUM (   number)    *opuser_from_number(KS_EXPAND_CVFLOAT(number))->addr;

◆ areSame

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

◆ areWithinTol

#define areWithinTol (   a,
  b,
  tol 
)    (fabs((a) - (b)) < tol)

◆ areSameRelative

#define areSameRelative (   a,
  b,
 
)    (fabs((a) - (b)) < r * (fabs((a)) + fabs((b))))

◆ isNotSet

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

◆ isSet

#define isSet (   a)    !isNotSet(a)

◆ ks_abs

#define ks_abs (   x)    (x)<0 ? -(x) : (x)

◆ ks_round

#define ks_round (   x)    ((x)>=0?(int)((x)+0.5):(int)((x)-0.5))

◆ KS_RUP_GRD_FLOAT

#define KS_RUP_GRD_FLOAT (   X)    ( fmod((X), GRAD_UPDATE_TIME) > 0 ? RUP_GRD( (X) + 1 ) : RUP_GRD( (X) ) )

◆ KS_SRC_FILE

#define KS_SRC_FILE   (__builtin_strrchr (__FILE__, '/') ? __builtin_strrchr (__FILE__, '/') + 1 : __FILE__)

◆ KS_THROW

#define KS_THROW (   format,
  ... 
)    ks_error("%s (%s:%d): " format, __FUNCTION__, KS_SRC_FILE, __LINE__, ##__VA_ARGS__)

◆ KS_RAISE_TRACE_FN

#define KS_RAISE_TRACE_FN   ks_error

◆ KS_RAISE

#define KS_RAISE (   status)    if ((status) != SUCCESS) { KS_RAISE_TRACE_FN("===> %s (%s:%d)", __FUNCTION__, KS_SRC_FILE, __LINE__); return ADVISORY_FAILURE; }

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

◆ KS_SSP_PRE_READ_TIME

#define KS_SSP_PRE_READ_TIME   256

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

#define KS_PLANE_AX   ((exist(opplane) == PSD_AXIAL) || (exist(opplane) == PSD_OBL && exist(opobplane) == PSD_AXIAL))

◆ KS_PLANE_COR

#define KS_PLANE_COR   ((exist(opplane) == PSD_COR) || (exist(opplane) == PSD_OBL && exist(opobplane) == PSD_COR))

◆ KS_PLANE_SAG

#define KS_PLANE_SAG   ((exist(opplane) == PSD_SAG) || (exist(opplane) == PSD_OBL && exist(opobplane) == PSD_SAG))

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

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   100

Maximum number of different KS_WAVEs in the sequence

◆ KS_MAXUNIQUE_WAIT

#define KS_MAXUNIQUE_WAIT   10

Maximum number of different KS_WAITs in the sequence

◆ KS_READCONTROL_MAXSTATE

#define KS_READCONTROL_MAXSTATE   2

Maximum number of states in a KS_ECHOTRAIN object

◆ KS_MAXUNIQUE_READWAVE

#define KS_MAXUNIQUE_READWAVE   2

Maximum number of different KS_READWAVEs 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_SEQUENCE_PLAYOUTS

#define KS_MAX_SEQUENCE_PLAYOUTS   200

Maximum number of pulses sequence modules, played during a TR

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

Maximum number of readout instances

◆ KS_MAX_NUM_KSPACES

#define KS_MAX_NUM_KSPACES   16

◆ KS_MAX_NUM_USERDATA

#define KS_MAX_NUM_USERDATA   10

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

#define KS_ECHOTRAIN_MAX_TRAPS   4

Maximum number of different KS_READTRAP objects in a KS_ECHOTRAIN

◆ KS_ECHOTRAIN_MAX_WAVES

#define KS_ECHOTRAIN_MAX_WAVES   2

Maximum number of different KS_READWAVE objects in a KS_ECHOTRAIN

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

#define KS_INIT_BASE (   OBJECT)    {0,0,0,NULL,sizeof(OBJECT)}

◆ KS_INIT_FINFO

#define KS_INIT_FINFO   {0.0, 0, 0.0, 0.0, KS_NOTSET, 0, 0, 0}

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

#define KS_INIT_RT_SCALE_LOG   {NULL, NULL, 0, 0, 0, 0}

◆ KS_INIT_WAVEVALUE

#define KS_INIT_WAVEVALUE   0

Default value for one element in a KS_WAVEFORM

◆ KS_WAVE_MAXNSTATES

#define KS_WAVE_MAXNSTATES   16

◆ 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   {KS_INIT_BASE(KS_WAVE), KS_INIT_DESC, 0, 0, KS_INIT_WAVEFORM, {NULL}, {0,0,0}, KS_NOTSET, NULL, NULL, NULL, KS_INIT_RT_SCALE_LOG, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, NULL, NULL}

Default values for KS_WAVE sequence objects

◆ KS_INIT_WAIT

#define KS_INIT_WAIT   {KS_INIT_BASE(KS_WAIT), KS_INIT_DESC, 0, KS_NOTSET, NULL, NULL, NULL, KS_INIT_RT_SCALE_LOG }

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   {KS_INIT_BASE(KS_TRAP), KS_INIT_DESC, 0.0, 0.0, 0, 0, 0, {0, 0, 0}, {0, 0, 0}, {1, 1, 1}, 1.0f, NULL, NULL, NULL, KS_INIT_RT_SCALE_LOG}

Default values for KS_TRAP sequence objects

◆ KS_INIT_READ_RESAMPLER

#define KS_INIT_READ_RESAMPLER   {NULL, NULL, NULL, 0, 0, {0}, 0, 0}

◆ KS_INIT_READ

#define KS_INIT_READ   {KS_INIT_BASE(KS_READ), KS_INIT_DESC, 0, KS_NOTSET, KS_INIT_FINFO, NULL, 0, NULL, KS_INIT_READ_RESAMPLER}

Default values for KS_READ sequence objects

◆ KS_INIT_READ_EPI

#define KS_INIT_READ_EPI   {KS_INIT_BASE(KS_READ), KS_INIT_DESC, 0, 250.0, KS_INIT_FINFO, NULL, 0, NULL, KS_INIT_READ_RESAMPLER}

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_WAVE, 0}

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_WAVE, 0}

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, FALSE}

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, 0, KS_INIT_DESC, 0, {CARTESIAN_COORD, CARTESIAN_COORD}}

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

#define KS_INIT_READWAVE   {KS_INIT_WAVE, KS_INIT_WAVE, KS_INIT_READ, 0, 0, 0.0, 0.0, 0, 0, 0.0, 0, 0, {KS_INIT_WAVEFORM}, {KS_INIT_WAVEFORM}}

◆ 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, KS_INITZEROS(KS_MAXUNIQUE_WAIT), 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

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), 0, KS_INITVALUE(KS_MAX_SEQUENCE_PLAYOUTS, 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_SPOIL_X

#define KS_SPOIL_X   1

Toggle spoiling on X

◆ KS_SPOIL_Y

#define KS_SPOIL_Y   2

Toggle spoiling on Y

◆ KS_SPOIL_Z

#define KS_SPOIL_Z   4

Toggle spoiling on Z

◆ ANYINVERSION

#define ANYINVERSION   (opirprep || opt1flair || opt2flair)

◆ KSCOMMON_INIT_GRAD_PARAMS

#define KSCOMMON_INIT_GRAD_PARAMS   {0, 0, 0, 0}

◆ KSCOMMON_INIT_RESAMPLER

#define KSCOMMON_INIT_RESAMPLER   {0, 0, 0.0, KSCOMMON_INIT_GRAD_PARAMS, KSCOMMON_INIT_GRAD_PARAMS, KSCOMMON_INIT_GRAD_PARAMS}

◆ KS_INIT_PHASEENCODING_SHOTCOORDS

#define KS_INIT_PHASEENCODING_SHOTCOORDS   {0, NULL}

◆ KS_INIT_DYNAMIC_STATE

#define KS_INIT_DYNAMIC_STATE   {0, 0, 0, 0, 0, 0, KS_INIT_PHASEENCODING_SHOTCOORDS, 0, 0, 0, 0, 0, 0, 0, 0, {0.0}, 0, 0, KS_MAT4x4_IDENTITY, KS_MAT4x4_IDENTITY}

◆ KS_DABARRAY_KS_RECON_RESERVED

#define KS_DABARRAY_KS_RECON_RESERVED   2

◆ KS_DABARRAY_ONLINE_RECON_RESERVED

#define KS_DABARRAY_ONLINE_RECON_RESERVED   6

◆ KS_DABARRAY_FULL_LENGTH

#define KS_DABARRAY_FULL_LENGTH   28

◆ KS_DABARRAY_AVAILABLE_BYTES

◆ KS_DEFAULT_READ_TYPE

#define KS_DEFAULT_READ_TYPE   KS_READ_NOTSET

◆ KS_INIT_READCONTROL_STATE

#define KS_INIT_READCONTROL_STATE   {KS_NOTSET, KS_NOTSET}

◆ KS_INIT_READCONTROL_PULSEGEN

#define KS_INIT_READCONTROL_PULSEGEN   {KS_DEFAULT_READ_TYPE, 0, 0, 1.0, KS_NOTSET}

◆ KS_INIT_READ_CONTROL

#define KS_INIT_READ_CONTROL   {{KS_INIT_READCONTROL_STATE}, KS_INIT_READCONTROL_PULSEGEN}

◆ KS_INIT_ECHOTRAIN

◆ KS_INIT_KSPACE_ACQ

#define KS_INIT_KSPACE_ACQ   {NULL, KS_NOTSET, {KS_NOTSET, KS_NOTSET, KS_NOTSET}}

◆ KS_INIT_PHASEENCODING_REPEAT_DESIGN

#define KS_INIT_PHASEENCODING_REPEAT_DESIGN   {NO_REPEAT, KS_NOTSET}

◆ KS_DEFAULT_KSACQ_RECONSHEDULE_ENTRY

#define KS_DEFAULT_KSACQ_RECONSHEDULE_ENTRY   {0, 0, -1, -1}

◆ KS_INIT_CRUSHER_CONSTRAINT

#define KS_INIT_CRUSHER_CONSTRAINT   {0, 0, 0, 0, KS_MINPLATEAUTIME, KS_CRUSHER_STRATEGY_FASTEST}

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

typedef float KS_MAT3x3f[9]

Array to store a row major 3x3 matrix (float)

◆ KS_MAT4x4

typedef double KS_MAT4x4[16]

Array to store a row major 4x4 matrix

◆ KS_MAT4x4f

typedef float KS_MAT4x4f[16]

Array to store a row major 4x4 matrix (float)

Enumeration Type Documentation

◆ KS_PF_EARLYLATE

Enumerator
KS_PF_EARLY 
KS_PF_NO 
KS_PF_LATE 
386  {KS_PF_EARLY = -1,
387  KS_PF_NO = 0,
Definition: KSFoundation.h:388
Definition: KSFoundation.h:386
KS_PF_EARLYLATE
Definition: KSFoundation.h:386
Definition: KSFoundation.h:387

◆ KS_PLOT_FILEFORMATS

Enumerator
KS_PLOT_OFF 
KS_PLOT_MAKEPDF 
KS_PLOT_MAKESVG 
KS_PLOT_MAKEPNG 
396  {
397  KS_PLOT_OFF = 0,
Definition: KSFoundation.h:397
Definition: KSFoundation.h:400
KS_PLOT_FILEFORMATS
Definition: KSFoundation.h:396
Definition: KSFoundation.h:398
Definition: KSFoundation.h:399

◆ KS_PLOT_EXCITATION_MODE

Enumerator
KS_PLOT_STANDARD 
KS_PLOT_NO_EXCITATION 
402  {
403  KS_PLOT_STANDARD = 0,
KS_PLOT_EXCITATION_MODE
Definition: KSFoundation.h:402
Definition: KSFoundation.h:404
Definition: KSFoundation.h:403

◆ KS_PLOT_PASS_MODE

Enumerator
KS_PLOT_PASS_WAS_DUMMY 
KS_PLOT_PASS_WAS_CALIBRATION 
KS_PLOT_PASS_WAS_STANDARD 
406  {
Definition: KSFoundation.h:407
KS_PLOT_PASS_MODE
Definition: KSFoundation.h:406
Definition: KSFoundation.h:409
Definition: KSFoundation.h:408

◆ KS_PLOT_SLICEGROUP_MODE

Enumerator
KS_PLOT_SG_DUMMY 
KS_PLOT_SG_CALIBRATION 
KS_PLOT_SG_ACQUISITION 
KS_PLOT_SG_NOTSET 
411  {
412  KS_PLOT_SG_DUMMY = 0,
KS_PLOT_SLICEGROUP_MODE
Definition: KSFoundation.h:411
Definition: KSFoundation.h:415
Definition: KSFoundation.h:413
Definition: KSFoundation.h:412
Definition: KSFoundation.h:414

◆ KS_DISCRETE_REPRESENTATION

Enumerator
KS_DISCRETE_REPRESENTATION_SYMMETRIC 
KS_DISCRETE_REPRESENTATION_GE 
418  {
Definition: KSFoundation.h:420
KS_DISCRETE_REPRESENTATION
Definition: KSFoundation.h:418
Definition: KSFoundation.h:419

◆ KS_COORDINATE_TYPE

ADDTITLEHERE

Enumerator
CARTESIAN_COORD 
RADIAL_COORD 
1761  {
1762  CARTESIAN_COORD = 0,
1763  RADIAL_COORD = 1
Definition: KSFoundation.h:1762
KS_COORDINATE_TYPE
ADDTITLEHERE
Definition: KSFoundation.h:1761
Definition: KSFoundation.h:1763

◆ KS_READ_TYPE

The read object type, used in KS_READCONTROL_PULSEGEN

Enumerator
KS_READ_NOTSET 
KS_READ_TRAP 
KS_READ_WAVE 
1957  {
1958  KS_READ_NOTSET = 0,
1959  KS_READ_TRAP,
1960  KS_READ_WAVE
1961 } KS_READ_TYPE;
Definition: KSFoundation.h:1958
KS_READ_TYPE
The read object type, used in KS_READCONTROL_PULSEGEN
Definition: KSFoundation.h:1957
Definition: KSFoundation.h:1960
Definition: KSFoundation.h:1959

◆ ks_enum_sweep_order

k-space 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:2083
ks_enum_sweep_order
k-space sweep order
Definition: KSFoundation.h:2081
Definition: KSFoundation.h:2084
Definition: KSFoundation.h:2081
Definition: KSFoundation.h:2082

◆ KS_COVERAGE

ADDTITLEHERE

Enumerator
ELLIPTICAL 
RECTANGULAR 
2093  {
2094  ELLIPTICAL = 0,
2095  RECTANGULAR = 1
2096 } KS_COVERAGE;
KS_COVERAGE
ADDTITLEHERE
Definition: KSFoundation.h:2093
Definition: KSFoundation.h:2094
Definition: KSFoundation.h:2095

◆ KS_MTF_DIRECTION

ADDTITLEHERE

Enumerator
MTF_Y 
MTF_Z 
MTF_R 
MTF_T 
2104  {
2105  MTF_Y = 0,
2106  MTF_Z = 1,
2107  MTF_R = 2, /* Radius in ky/kz plane. For isotropic point spread function */
2108  MTF_T = 3, /* Azimuthal in ky/kz plane. You would rarely want this */
KS_MTF_DIRECTION
ADDTITLEHERE
Definition: KSFoundation.h:2104
Definition: KSFoundation.h:2105
Definition: KSFoundation.h:2108
Definition: KSFoundation.h:2107
Definition: KSFoundation.h:2106

◆ KS_VIEW_SORT_ORDER

ADDTITLEHERE

Enumerator
Y_Z 
Z_Y 
R_T 
T_R 
Y_R 
Z_R 
SEC_T 
2116  {
2117  /* Cartesian coordinates */
2118  Y_Z = 0,
2119  Z_Y = 1,
2120  /* Polar coordinates R = radius T = theta*/
2121  R_T = 2,
2122  T_R = 3,
2123  /* Mixing coords */
2124  Y_R = 4,
2125  Z_R = 5,
2126  /* Sectors for continuous time */
2127  SEC_T = 6
2128 
KS_VIEW_SORT_ORDER
ADDTITLEHERE
Definition: KSFoundation.h:2116
Definition: KSFoundation.h:2124
Definition: KSFoundation.h:2118
Definition: KSFoundation.h:2127
Definition: KSFoundation.h:2121
Definition: KSFoundation.h:2122
Definition: KSFoundation.h:2125
Definition: KSFoundation.h:2119

◆ KS_VIEW_MTF_PROFILE

ADDTITLEHERE

Enumerator
LINEAR_SWEEP 
PIVOT_CENTER_OUTER_LINEAR 
LCPO 
SPLIT_SHOTS_LCPO 
LINEAR_ROLL 
PIVOT_ROLL 
SPLIT_SHOTS_AMERICAN 
SPLIT_SHOTS_DUTCH 
2137  {
2138  LINEAR_SWEEP = -1,
2140  LCPO = 1,
2141  SPLIT_SHOTS_LCPO = 2,
2142  LINEAR_ROLL = 3,
2143  PIVOT_ROLL = 4,
2145  SPLIT_SHOTS_DUTCH = 6,
Definition: KSFoundation.h:2140
Definition: KSFoundation.h:2138
Definition: KSFoundation.h:2139
Definition: KSFoundation.h:2142
Definition: KSFoundation.h:2143
Definition: KSFoundation.h:2145
Definition: KSFoundation.h:2141
KS_VIEW_MTF_PROFILE
ADDTITLEHERE
Definition: KSFoundation.h:2137
Definition: KSFoundation.h:2144

◆ RADIUS_PATTERN

ADDTITLEHERE

Enumerator
LINEAR 
EXPONENTIAL 
2154  {
2155  LINEAR = 0,
2156  EXPONENTIAL = 1,
2157 } RADIUS_PATTERN;
RADIUS_PATTERN
ADDTITLEHERE
Definition: KSFoundation.h:2154
Definition: KSFoundation.h:2156
Definition: KSFoundation.h:2155

◆ KS_REPEAT_MODE

KS_REPEAT_MODE - Controls how the phase encoding shots are repeated

Enumerator
NO_REPEAT 
INTERLEAVED_LONGER_SHOTS 
SEQUENTIAL_LONGER_SHOTS 
INTERLEAVED_EXTRA_SHOTS 
SEQUENTIAL_EXTRA_SHOTS 
2242  {
2243  NO_REPEAT = 0,
2244  INTERLEAVED_LONGER_SHOTS = 1, /* For two repeats: [0, 1, 3, 2] -> [0, 0, 1, 1, 3, 3, 2, 2] */
2245  SEQUENTIAL_LONGER_SHOTS = 2, /* For two repeats: [0, 1, 3, 2] -> [0, 1, 3, 2, 0, 1, 3, 2] */
2246  INTERLEAVED_EXTRA_SHOTS = 3, /* For two repeats: [0, 1, 3, 2] -> [0, 1, 3, 2], next shot same encodes [0, 1, 3, 2] */
2247  SEQUENTIAL_EXTRA_SHOTS = 4 /* Repeat all shots after they have been played out once */
2248 } KS_REPEAT_MODE;
Definition: KSFoundation.h:2245
KS_REPEAT_MODE
KS_REPEAT_MODE - Controls how the phase encoding shots are repeated
Definition: KSFoundation.h:2242
Definition: KSFoundation.h:2247
Definition: KSFoundation.h:2246
Definition: KSFoundation.h:2244
Definition: KSFoundation.h:2243

◆ KS_RADIAL_SAMPLING_MODE

ADDTITLEHERE

Enumerator
CARTESIAN 
RADIAL_THROUGH_CENTER 
RADIAL_SOS 
2270  {
2271  CARTESIAN = 0,
2273  RADIAL_SOS = 2,
Definition: KSFoundation.h:2271
KS_RADIAL_SAMPLING_MODE
ADDTITLEHERE
Definition: KSFoundation.h:2270
Definition: KSFoundation.h:2272
Definition: KSFoundation.h:2273

◆ KS_RADIAL_SAMPLING_COVERAGE

ADDTITLEHERE

Enumerator
SAMPLE_180 
SAMPLE_360 
2283  {
2284  SAMPLE_180 = 0,
2285  SAMPLE_360 = 1,
KS_RADIAL_SAMPLING_COVERAGE
ADDTITLEHERE
Definition: KSFoundation.h:2283
Definition: KSFoundation.h:2284
Definition: KSFoundation.h:2285

◆ ks_snr_mode

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

◆ ks_enum_trapparts

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

◆ 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 
2311  {KS_X = TYPXGRAD,
2312  KS_Y = TYPYGRAD,
2313  KS_Z = TYPZGRAD,
2314  KS_RHO = TYPRHO1,
2315  KS_RHO2 = TYPRHO2,
2316  KS_THETA = TYPTHETA,
2317  KS_OMEGA = TYPOMEGA,
2318  KS_SSP = TYPSSP,
2325  KS_XYZ,
2326  KS_ALL};
Definition: KSFoundation.h:2319
Definition: KSFoundation.h:2312
Definition: KSFoundation.h:2321
Definition: KSFoundation.h:2315
Definition: KSFoundation.h:2317
Definition: KSFoundation.h:2314
Definition: KSFoundation.h:2311
Definition: KSFoundation.h:2316
Definition: KSFoundation.h:2325
Definition: KSFoundation.h:2324
Definition: KSFoundation.h:2313
Definition: KSFoundation.h:2326
Definition: KSFoundation.h:2322
Definition: KSFoundation.h:2320
Definition: KSFoundation.h:2318
Definition: KSFoundation.h:2323

◆ ks_enum_epitrain_mode

Enumerator
KS_EPI_BIPOLAR 
KS_EPI_SPLITODDEVEN 
KS_EPI_FLYBACK 
ks_enum_epitrain_mode
Definition: KSFoundation.h:2329
Definition: KSFoundation.h:2329
Definition: KSFoundation.h:2329
Definition: KSFoundation.h:2329

◆ ks_enum_epiblipsign

Enumerator
KS_EPI_POSBLIPS 
KS_EPI_NOBLIPS 
KS_EPI_NEGBLIPS 
2330 {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:2330
ks_enum_epiblipsign
Definition: KSFoundation.h:2330
Definition: KSFoundation.h:2330
Definition: KSFoundation.h:2330

◆ ks_enum_diffusion

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

◆ ks_enum_imsize

Enumerator
KS_IMSIZE_NATIVE 
KS_IMSIZE_POW2 
KS_IMSIZE_MIN256 
Definition: KSFoundation.h:2335
ks_enum_imsize
Definition: KSFoundation.h:2335
Definition: KSFoundation.h:2335
Definition: KSFoundation.h:2335

◆ ks_enum_datadestination

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

◆ ks_enum_accelmenu

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

◆ 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:2340
Definition: KSFoundation.h:2340
Definition: KSFoundation.h:2340
Definition: KSFoundation.h:2340
Definition: KSFoundation.h:2340
Definition: KSFoundation.h:2340

◆ 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:2341
Definition: KSFoundation.h:2341
Definition: KSFoundation.h:2341
Definition: KSFoundation.h:2341
Definition: KSFoundation.h:2341

◆ 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 
2344  {
Definition: KSFoundation.h:2347
Definition: KSFoundation.h:2349
Definition: KSFoundation.h:2348
Definition: KSFoundation.h:2345
Definition: KSFoundation.h:2346

◆ 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 
2351  {
KS_ENUM_SMS_PHASE_MOD
Definition: KSFoundation.h:2351
Definition: KSFoundation.h:2355
Definition: KSFoundation.h:2356
Definition: KSFoundation.h:2354
Definition: KSFoundation.h:2352
Definition: KSFoundation.h:2353

◆ anonymous enum

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

◆ ks_enum_grad_scaling_policy

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

◆ ks_enum_crusher_strategy

Enumerator
KS_CRUSHER_STRATEGY_FASTEST 
KS_CRUSHER_STRATEGY_EXACT 
KS_CRUSHER_STRATEGY_MIN_SLEW 
KS_CRUSHER_STRATEGY_MAX_AREA 
KS_CRUSHER_STRATEGY_PLATEAU 
2361  {
2362  KS_CRUSHER_STRATEGY_FASTEST = 0, /* Achieve area as fast as possible (batwing) */
2363  KS_CRUSHER_STRATEGY_EXACT, /* Add negative blip if necessary. Guarantees area */
2364  KS_CRUSHER_STRATEGY_MIN_SLEW, /* Prolong ramp */
2365  KS_CRUSHER_STRATEGY_MAX_AREA, /* Use entire min_duration to get max area */
2366  KS_CRUSHER_STRATEGY_PLATEAU /* Same as FASTEST but plateau amp = end amp */
Definition: KSFoundation.h:2364
Definition: KSFoundation.h:2365
Definition: KSFoundation.h:2363
ks_enum_crusher_strategy
Definition: KSFoundation.h:2361
Definition: KSFoundation.h:2366
Definition: KSFoundation.h:2362

Function Documentation

◆ opuser_from_number()

_cvfloat* opuser_from_number ( _cvfloat *  cv)
6121  {
6122  return cv;
6123 }

◆ isNaN()

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

◆ ks_bitmask_set()

STATUS ks_bitmask_set ( unsigned int  out,
const unsigned int  in,
const unsigned int  size,
const unsigned int  offset 
)

Tool to set certain bits in an integer

6081  {
6082 
6083  if (size + offset >= sizeof(unsigned int)) {
6084  return ks_error("%s: the sum of size (%d) and offset (%d) must be less than %d", __FUNCTION__, size, offset, sizeof(unsigned int));
6085  }
6086 
6087  /* we need to copy an amount of bits equal to size from the input */
6088  const unsigned int mask_in = (1ULL << size) - 1ULL;
6089 
6090  /* shift the input mask */
6091  const unsigned int mask_out = mask_in << offset;
6092 
6093  out = ((in & mask_in) << offset) | (out & ~mask_out);
6094 
6095  return SUCCESS;
6096 
6097 } /* ks_bitmask_set() */
STATUS ks_error(const char *format,...) __attribute__((format(printf
Common error message function for HOST and TGT

◆ ks_bitmask_get()

unsigned int ks_bitmask_get ( const unsigned int  in,
const unsigned int  size,
const unsigned int  offset 
)

Tool to get certain bits in an integer

Tool to get certain bits in an integer

6107  {
6108  if (size + offset >= sizeof(unsigned int)) {
6109  ks_error("%s: the sum of size (%d) and offset (%d) must be less than %d", __FUNCTION__, size, offset, sizeof(unsigned int));
6110  return 0;
6111  }
6112 
6113  const unsigned int mask_in = ((1ULL << size) - 1ULL) << offset;
6114 
6115  return (in & mask_in) >> offset;
6116 }
STATUS ks_error(const char *format,...) __attribute__((format(printf
Common error message function for HOST and TGT

◆ ks_isIceHardware()

int ks_isIceHardware ( )

Function that returns 1 if it is an ICE realtime computer and 0 if not

6044  {
6045 #if EPIC_RELEASE >= 27
6046  if (isIceHardware() == TRUE) {
6047  return TRUE;
6048  }
6049 #endif
6050 return FALSE;
6051 }

◆ ks_default_ssitime()

int ks_default_ssitime ( )

Function that returns the default SSI time (to be improved)

6056  {
6057 
6058  if (ks_isIceHardware() == TRUE) {
6059  return KS_DEFAULT_SSI_TIME_ICE;
6060  }
6061 
6062  return KS_DEFAULT_SSI_TIME;
6063 /*
6064 #define KS_DEFAULT_SSI_TIME 1500
6065 #define KS_DEFAULT_SSI_TIME_MR750w 500
6066 #define KS_DEFAULT_SSI_TIME_MR750 1000
6067 #define KS_DEFAULT_SSI_TIME_MR450W 500
6068 #define KS_DEFAULT_SSI_TIME_MR450 1000
6069 #define KS_DEFAULT_SSI_TIME_PREMIER 500
6070 #define KS_DEFAULT_SSI_TIME_OTHERWISE 1500
6071 */
6072 
6073 }
#define KS_DEFAULT_SSI_TIME_ICE
Definition: KSFoundation.h:213
int ks_isIceHardware()
Function that returns 1 if it is an ICE realtime computer and 0 if not
Definition: KSFoundation_host.c:6044
#define KS_DEFAULT_SSI_TIME
Definition: KSFoundation.h:207

◆ ks_peplan_distribute_shots()

KS_PEPLAN_SHOT_DISTRIBUTION ks_peplan_distribute_shots ( const int  etl,
const int  num_coords,
const int  center 
)

ADDTITLEHERE

ADDDESCHERE

Parameters
[in]etlADDTEXTHERE
[in]num_coordsADDTEXTHERE
[in]centerADDTEXTHERE
Return values
KS_PEPLAN_SHOT_DISTRIBUTIONADDTEXTHERE
8955  {
8956  KS_PEPLAN_SHOT_DISTRIBUTION shot_distribution;
8957 
8958  shot_distribution.shots = CEIL_DIV(num_coords, etl);
8959 
8960  const int num_echoes = shot_distribution.shots * etl;
8961  const int num_extra_per_train = FLOOR_DIV(num_echoes - num_coords, shot_distribution.shots);
8962 
8963  shot_distribution.encodes_per_shot = etl - num_extra_per_train;
8964 
8965  shot_distribution.readout_start = center == KS_NOTSET ? KS_NOTSET
8966  : ks_readout_start_index(center, etl, shot_distribution.encodes_per_shot);
8967 
8968  const int second_blocks = shot_distribution.encodes_per_shot * shot_distribution.shots - num_coords;
8969  shot_distribution.first_blocks = shot_distribution.shots - second_blocks;
8970 
8971  return shot_distribution;
8972 }
int shots
Definition: KSFoundation.h:2166
#define KS_NOTSET
Definition: KSFoundation.h:115
int readout_start
Definition: KSFoundation.h:2169
int ks_readout_start_index(int center, int etl, int num_encodes_per_train)
Definition: KSFoundation_host.c:8945
int encodes_per_shot
Definition: KSFoundation.h:2168
int first_blocks
Definition: KSFoundation.h:2170
ADDTITLEHERE
Definition: KSFoundation.h:2165

◆ 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:838
#define KS_INIT_READ
Definition: KSFoundation.h:300

◆ 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
62  {
63  KS_TRAP deftrap = KS_INIT_TRAP;
64  *trap = deftrap;
65 }
Core sequence object for making trapezoids on X,Y,Z, and OMEGA boards
Definition: KSFoundation.h:666
#define KS_INIT_TRAP
Definition: KSFoundation.h:298

◆ ks_init_rtscalelog()

void ks_init_rtscalelog ( KS_RT_SCALE_LOG *const  rtscale)

Resets a KS_RT_SCALE_LOG instance

Parameters
[in,out]rtscalePointer to a KS_RT_SCALE_LOG instance
Returns
void
3506  {
3507  if (!rtscale) {return; }
3508 
3509  float *ampscales = rtscale->ampscales;
3510  int *states = rtscale->states;
3511  int num_instances = rtscale->num_instances;
3512 
3513  /* Use a temp object to be consistent with the default values */
3514  KS_RT_SCALE_LOG tmp_rtscale = KS_INIT_RT_SCALE_LOG;
3515  *rtscale = tmp_rtscale;
3516 
3517  rtscale->ampscales = ampscales;
3518  rtscale->states = states;
3519  rtscale->buffer_size = ampscales ? num_instances : 0;
3520  rtscale->buffer_size_states = states ? num_instances : 0;
3521 }
int buffer_size
Definition: KSFoundation.h:474
int num_instances
Definition: KSFoundation.h:476
int * states
Definition: KSFoundation.h:473
int buffer_size_states
Definition: KSFoundation.h:475
float * ampscales
Definition: KSFoundation.h:472
Log of real-time scaling amplitudes.
Definition: KSFoundation.h:471
#define KS_INIT_RT_SCALE_LOG
Definition: KSFoundation.h:290

◆ 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
70  {
71  KS_WAIT defwait = KS_INIT_WAIT;
72  *wait = defwait;
73 }
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:542
#define KS_INIT_WAIT
Definition: KSFoundation.h:296

◆ 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
78  {
79  KS_WAVE defwave = KS_INIT_WAVE;
80  *wave = defwave;
81 }
Core sequence object making arbitrary waveforms on any board (using float data format)
Definition: KSFoundation.h:743
#define KS_INIT_WAVE
Definition: KSFoundation.h:295

◆ 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
86  {
87  KS_RF defrf = KS_INIT_RF;
88  *rf = defrf;
89 }
#define KS_INIT_RF
Definition: KSFoundation.h:320
Composite sequence object for RF (with optional OMEGA & THETA pulses)
Definition: KSFoundation.h:1026

◆ 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
94  {
95  KS_SMS_INFO defsms_info = KS_INIT_SMS_INFO;
96  *sms_info = defsms_info;
97 }
Internal typedef struct that is a part of the KS_SELRF typedef struct, used to store information abou...
Definition: KSFoundation.h:1335
#define KS_INIT_SMS_INFO
Definition: KSFoundation.h:330

◆ 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
102  {
103  KS_SELRF defselrf = KS_INIT_SELRF;
104  *selrf = defselrf;
105 }
Composite sequence object for slice-selective RF
Definition: KSFoundation.h:1453
#define KS_INIT_SELRF
Definition: KSFoundation.h:321

◆ 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
110  {
111  KS_READTRAP defread = KS_INIT_READTRAP;
112  *readtrap = defread;
113 }
#define KS_INIT_READTRAP
Definition: KSFoundation.h:302
Composite sequence object for data readout using a trapezoid gradient
Definition: KSFoundation.h:1548

◆ ks_init_readwave()

void ks_init_readwave ( KS_READWAVE readwave)

Resets a KS_READWAVE sequence object to its default value (KS_INIT_READWAVE)

Parameters
[out]readwavePointer to KS_READWAVE
Returns
void
134  {
135  *readwave = (KS_READWAVE)KS_INIT_READWAVE;
136 }
Composite sequence object for data readout during a wave (1-D resampling)
Definition: KSFoundation.h:1603
#define KS_INIT_READWAVE
Definition: KSFoundation.h:312

◆ 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
118  {
119  KS_PHASER defphaser = KS_INIT_PHASER;
120  *phaser = defphaser;
121 }
#define KS_INIT_PHASER
Definition: KSFoundation.h:305
Composite sequence object for phase encoding using a trapezoid gradient
Definition: KSFoundation.h:1718

◆ 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
126  {
127  KS_EPI defepi = KS_INIT_EPI;
128  *epi = defepi;
129 }
#define KS_INIT_EPI
Definition: KSFoundation.h:310
Composite sequence object for EPI readout
Definition: KSFoundation.h:1931

◆ 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
141  {
142  *gradrfctrl = (KS_GRADRFCTRL)KS_INIT_GRADRFCTRL;
143 }
typedef struct that is a part of the KS_SEQ_CONTROL typedef struct, used internally to collect gradie...
Definition: KSFoundation.h:1054
#define KS_INIT_GRADRFCTRL
Definition: KSFoundation.h:323

◆ 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
148  {
149  KS_SEQ_CONTROL defseqcontrol = KS_INIT_SEQ_CONTROL;
150  *seqcontrol = defseqcontrol;
151 }
#define KS_INIT_SEQ_CONTROL
Definition: KSFoundation.h:325
Definition: KSFoundation.h:1223

◆ 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
156  {
157  KS_SEQ_COLLECTION defseqcollection = KS_INIT_SEQ_COLLECTION;
158  *seqcollection = defseqcollection;
159 }
#define KS_INIT_SEQ_COLLECTION
Definition: KSFoundation.h:327
KS_SEQ_COLLECTION seqcollection
Definition: ksepi.e:82
Collection handle of all sequence modules involved in the pulse sequence, used for RF scaling...
Definition: KSFoundation.h:1263

◆ ks_init_echotrain()

void ks_init_echotrain ( KS_ECHOTRAIN *const  echotrain)

Reinitialises an echotrain structure

Parameters
[in,out]echotrainpointer to the structure you want to initialise with (KS_INIT_ECHOTRAIN).
Returns
void
188  {
189  *echotrain = (KS_ECHOTRAIN)KS_INIT_ECHOTRAIN;
190  int readout;
191  int state;
192  for (state = 0; state < KS_READCONTROL_MAXSTATE; state++) {
193  for (readout = 0; readout < KS_MAXUNIQUE_READ; readout++) {
194  echotrain->controls[readout] = (KS_READCONTROL)KS_INIT_READ_CONTROL;
195  echotrain->controls[readout].state[state] = (KS_READCONTROL_STATE)KS_INIT_READCONTROL_STATE;
196  }
197  }
198 }
Wraps pulsegen information and state of each readout in an echotrain
Definition: KSFoundation.h:1998
#define KS_INIT_READ_CONTROL
Definition: KSFoundation.h:2004
KS_READCONTROL_STATE state[KS_READCONTROL_MAXSTATE]
Definition: KSFoundation.h:2000
#define KS_INIT_ECHOTRAIN
Definition: KSFoundation.h:2025
#define KS_INIT_READCONTROL_STATE
Definition: KSFoundation.h:1975
#define KS_MAXUNIQUE_READ
Definition: KSFoundation.h:264
A collection of readout controls that define an echotrain
Definition: KSFoundation.h:2014
Stores information used to adapt an array of readouts (KS_REATRAP/KS_READWAVE) during the scan...
Definition: KSFoundation.h:1970
KS_READCONTROL controls[KS_MAXUNIQUE_READ]
Definition: KSFoundation.h:2015
#define KS_READCONTROL_MAXSTATE
Definition: KSFoundation.h:259

◆ 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
164  {
165 
166  if (fabs(srfact) < FLT_EPSILON) {
167  return ks_error("%s: slewrate factor can not be zero", __FUNCTION__);
168  }
169 
170  if (srfact < 1.0 && phygrd != NULL) {
171  phygrd->xrt = (int) ((float) phygrd->xrt / srfact);
172  phygrd->yrt = (int) ((float) phygrd->yrt / srfact);
173  phygrd->zrt = (int) ((float) phygrd->zrt / srfact);
174  }
175 
176  if (loggrd != NULL) {
177  loggrd->xrt = (int) ((float) loggrd->xrt / srfact);
178  loggrd->yrt = (int) ((float) loggrd->yrt / srfact);
179  loggrd->zrt = (int) ((float) loggrd->zrt / srfact);
180  }
181 
182  return SUCCESS;
183 }
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
207  {
208  int i;
209 
210  if (seqcollection == NULL) {
211  return ks_error("%s: seqcollection is NULL", __FUNCTION__);
212  }
213 
214  seqctrl->collection = seqcollection;
215 
216  if (seqctrl->duration == 0) {
217  /* Return early if seqctrl.duration = 0 so we are not adding sequence modules with zero duration to the collection.
218  This is in concert with:
219  - not performing createseq() in KS_SEQLENGTH if seqctrl.duration = 0
220  - not playing sequence modules in scan() using ks_scan_playsequence() if seqctrl.duration = 0
221 
222  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
223  and <seqmodule>_pg() should therefore be called before this function.
224  Preferred method is to add the following to the end of <seqmodule>_pg():
225  #ifndef IPG
226  // HOST only:
227  ks_eval_seqctrl_setminduration(&seqctrl, tmploc.pos); // tmploc.pos now corresponds to the end of last gradient in the sequence
228  #endif
229  */
230  return SUCCESS;
231  }
232 
234  return ks_error("%s: Number of sequence modules has been exceeded (max: %d)", __FUNCTION__, KS_MAXUNIQUE_SEQUENCES);
235  }
236 
238  return ks_error("%s - (%s): seqcollection.mode = KS_SEQ_COLLECTION_LOCKED. Module must be added before GEReq_eval_rfscaling() and GEReq_eval_checkTR_SAR_calcs()", __FUNCTION__, seqctrl->description);
239  }
240 
241  /* check for previous registrations of this sequence module. If found, return */
242  if (seqcollection->numseq > 0) {
243  for (i = 0; i < seqcollection->numseq; i++) {
244  if (seqcollection->seqctrlptr[i] == seqctrl)
245  return SUCCESS;
246  }
247  }
248 
249  /* register the sequence module */
251 
252  return SUCCESS;
253 }
STATUS ks_error(const char *format,...) __attribute__((format(printf
Common error message function for HOST and TGT
int mode
Definition: KSFoundation.h:1265
int duration
Definition: KSFoundation.h:1227
KS_SEQ_CONTROL * seqctrlptr[KS_MAXUNIQUE_SEQUENCES]
Definition: KSFoundation.h:1268
#define KS_MAXUNIQUE_SEQUENCES
Definition: KSFoundation.h:261
KS_SEQ_COLLECTION seqcollection
Definition: ksepi.e:82
KS_DESCRIPTION description
Definition: KSFoundation.h:1234
Definition: KSFoundation.h:2358
int numseq
Definition: KSFoundation.h:1264
struct _seq_collection_s * collection
Definition: KSFoundation.h:1237

◆ 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
258  {
259  int i;
260 
261  if (seqctrl->duration == 0) {
262  return SUCCESS; /* it is not added, but we should still not complain as .duration = 0
263  means that it won't be used anyway */
264  }
265 
266  /* check for previous registrations of this sequence module. If found, return */
267  if (seqcollection->numseq > 0) {
268  for (i = 0; i < seqcollection->numseq; i++) {
269  if (seqcollection->seqctrlptr[i] == seqctrl)
270  return SUCCESS;
271  }
272  }
273 
274  return FAILURE;
275 
276 }
int duration
Definition: KSFoundation.h:1227
KS_SEQ_CONTROL * seqctrlptr[KS_MAXUNIQUE_SEQUENCES]
Definition: KSFoundation.h:1268
KS_SEQ_COLLECTION seqcollection
Definition: ksepi.e:82
int numseq
Definition: KSFoundation.h:1264

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

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
4656  {
4657  int i;
4658  if (trap == NULL) return SUCCESS;
4659 
4660  if (gradrfctrl == NULL) {
4661  return ks_error("%s: gradrfctrl is NULL", __FUNCTION__);
4662  }
4663 
4664  if (gradrfctrl->numtrap >= KS_MAXUNIQUE_TRAP) {
4665  return ks_error("%s: ERROR - too many unique KS_TRAP in sequence", __FUNCTION__);
4666  }
4667 
4668  /* check for previous registrations of this trap's memory address. If found, return */
4669  if (gradrfctrl->numtrap > 0) {
4670  for (i = 0; i < gradrfctrl->numtrap; i++) {
4671  if (gradrfctrl->trapptr[i] == trap)
4672  return SUCCESS;
4673  }
4674  }
4675 
4676  gradrfctrl->trapptr[gradrfctrl->numtrap++] = trap;
4677 
4678  return SUCCESS;
4679 }
int numtrap
Definition: KSFoundation.h:1058
STATUS ks_error(const char *format,...)
Definition: KSFoundation_common.c:78
#define KS_MAXUNIQUE_TRAP
Definition: KSFoundation.h:256
KS_TRAP * trapptr[KS_MAXUNIQUE_TRAP]
Definition: KSFoundation.h:1057

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

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
4735  {
4736  int i;
4737  if (wave == NULL) return SUCCESS;
4738 
4739  if (gradrfctrl == NULL) {
4740  return ks_error("%s: gradrfctrl is NULL", __FUNCTION__);
4741  }
4742 
4743  if (gradrfctrl->numwave >= KS_MAXUNIQUE_WAVE) {
4744  return ks_error("%s: ERROR - too many unique KS_WAVE in sequence", __FUNCTION__);
4745  }
4746 
4747  /* check for previous registrations of this trap's memory address. If found, return */
4748  if (gradrfctrl->numwave > 0) {
4749  for (i = 0; i < gradrfctrl->numwave; i++) {
4750  if (gradrfctrl->waveptr[i] == wave)
4751  return SUCCESS;
4752  }
4753  }
4754 
4755  /* check also for previous waves in KS_RF (rfwave, omegawave, thetawave). If found, return */
4756  if (gradrfctrl->numrf > 0) {
4757  for (i = 0; i < gradrfctrl->numrf; i++) {
4758  if (&(gradrfctrl->rfptr[i]->rfwave) == wave || &(gradrfctrl->rfptr[i]->omegawave) == wave || &(gradrfctrl->rfptr[i]->thetawave) == wave)
4759  return SUCCESS;
4760  }
4761  }
4762 
4763  gradrfctrl->waveptr[gradrfctrl->numwave++] = wave;
4764 
4765  return SUCCESS;
4766 }
KS_RF * rfptr[KS_MAXUNIQUE_RF]
Definition: KSFoundation.h:1055
STATUS ks_error(const char *format,...)
Definition: KSFoundation_common.c:78
int numrf
Definition: KSFoundation.h:1056
KS_WAVE thetawave
Definition: KSFoundation.h:1039
KS_WAVE * waveptr[KS_MAXUNIQUE_WAVE]
Definition: KSFoundation.h:1059
KS_WAVE rfwave
Definition: KSFoundation.h:1037
#define KS_MAXUNIQUE_WAVE
Definition: KSFoundation.h:257
int numwave
Definition: KSFoundation.h:1060
KS_WAVE omegawave
Definition: KSFoundation.h:1038

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

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
4771  {
4772  int i;
4773  if (rf == NULL) return SUCCESS;
4774 
4775  if (gradrfctrl == NULL) {
4776  return ks_error("%s: gradrfctrl is NULL", __FUNCTION__);
4777  }
4778 
4779  if (gradrfctrl->numrf >= KS_MAXUNIQUE_RF) {
4780  return ks_error("%s: ERROR - too many unique KS_RF in sequence", __FUNCTION__);
4781  }
4782 
4783  /* check for previous registrations of this rf's memory address. If found, return */
4784  if (gradrfctrl->numrf > 0) {
4785  for (i = 0; i < gradrfctrl->numrf; i++) {
4786  if (gradrfctrl->rfptr[i] == rf)
4787  return SUCCESS;
4788  }
4789  }
4790 
4791  gradrfctrl->rfptr[gradrfctrl->numrf++] = rf;
4792 
4793  return SUCCESS;
4794 }
#define KS_MAXUNIQUE_RF
Definition: KSFoundation.h:255
KS_RF * rfptr[KS_MAXUNIQUE_RF]
Definition: KSFoundation.h:1055
STATUS ks_error(const char *format,...)
Definition: KSFoundation_common.c:78
int numrf
Definition: KSFoundation.h:1056

◆ 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
4799  {
4800  int i;
4801  if (read == NULL) return SUCCESS;
4802 
4803  if (gradrfctrl == NULL) {
4804  return ks_error("%s: gradrfctrl is NULL", __FUNCTION__);
4805  }
4806 
4807  if (gradrfctrl->numacq >= KS_MAXUNIQUE_READ) {
4808  return ks_error("%s: ERROR - too many readouts in sequence", __FUNCTION__);
4809  }
4810 
4811  /* check for previous registrations of the pointer. If found, return */
4812  if (gradrfctrl->numacq > 0) {
4813  for (i = 0; i < gradrfctrl->numacq; i++) {
4814  if (gradrfctrl->readptr[i] == read)
4815  return SUCCESS;
4816  }
4817  }
4818 
4819  gradrfctrl->readptr[gradrfctrl->numacq++] = read;
4820 
4821  return SUCCESS;
4822 }
STATUS ks_error(const char *format,...)
Definition: KSFoundation_common.c:78
#define KS_MAXUNIQUE_READ
Definition: KSFoundation.h:264
KS_READ * readptr[KS_MAXUNIQUE_READ]
Definition: KSFoundation.h:1061
int numacq
Definition: KSFoundation.h:1062

◆ ks_eval_addwaittogradrfctrl()

STATUS ks_eval_addwaittogradrfctrl ( KS_GRADRFCTRL gradrfctrl,
KS_WAIT wait 
)

*Internal use*. Adds a wait pulse to the KS_GRADRFCTRL struct. Used for plotting

Parameters
[in,out]gradrfctrlPointer to the KS_GRADRFCTRL for the sequence
[in]waitPointer to the wait object.
Return values
STATUSSUCCESS or FAILURE
4684  {
4685  int i;
4686  if (wait == NULL) {
4687  return SUCCESS;
4688  }
4689 
4690  if (gradrfctrl == NULL) {
4691  return ks_error("%s: gradrfctrl is NULL", __FUNCTION__);
4692  }
4693 
4694  if (gradrfctrl->numwait >= KS_MAXUNIQUE_WAIT) {
4695  return ks_error("%s: ERROR - too many unique KS_WAIT in sequence (max %d)", __FUNCTION__, KS_MAXUNIQUE_WAIT);
4696  }
4697 
4698  /* check for previous registrations of this wait's memory address. If found, return */
4699  if (gradrfctrl->numwait > 0) {
4700  for (i = 0; i < gradrfctrl->numwait; i++) {
4701  if (gradrfctrl->waitptr[i] == wait)
4702  return SUCCESS;
4703  }
4704  }
4705 
4706  gradrfctrl->waitptr[gradrfctrl->numwait++] = wait;
4707 
4708  return SUCCESS;
4709 
4710 }
STATUS ks_error(const char *format,...)
Definition: KSFoundation_common.c:78
int numwait
Definition: KSFoundation.h:1064
KS_WAIT * waitptr[KS_MAXUNIQUE_WAIT]
Definition: KSFoundation.h:1063
#define KS_MAXUNIQUE_WAIT
Definition: KSFoundation.h:258

◆ ks_eval_wait()

STATUS ks_eval_wait ( KS_WAIT wait,
const char *const  desc,
int  pg_duration,
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]pg_durationADDTEXTHERE
[in]maxdurationThe desired maximum duration in [us] of the KS_WAIT object
Return values
STATUSSUCCESS or FAILURE
281  {
282 
283  ks_init_wait(wait);
284 
285  if (desc == NULL || desc[0] == ' ') {
286  return ks_error("%s: desc (2nd arg) cannot be NULL or begin with a space", __FUNCTION__);
287  } else {
288  strncpy(wait->description, desc, KS_DESCRIPTION_LENGTH - 1);
289  }
290 
291 
292  wait->max_duration = max_duration;
293  wait->pg_duration = pg_duration;
294 
295  /* initialize counters to 0 */
296  wait->base.ninst = 0;
297  wait->base.ngenerated = 0;
298  wait->base.next = NULL;
299 
300  return SUCCESS;
301 }
void ks_init_wait(KS_WAIT *wait)
Resets a KS_WAIT sequence object to its default value (KS_INIT_WAIT)
Definition: KSFoundation_host.c:70
STATUS ks_error(const char *format,...) __attribute__((format(printf
Common error message function for HOST and TGT
int ninst
Definition: KSFoundation.h:494
KS_DESCRIPTION description
Definition: KSFoundation.h:544
#define KS_DESCRIPTION_LENGTH
Definition: KSFoundation.h:253
void * next
Definition: KSFoundation.h:497
KS_BASE base
Definition: KSFoundation.h:543
int pg_duration
Definition: KSFoundation.h:545
int max_duration
Definition: KSFoundation.h:546
int ngenerated
Definition: KSFoundation.h:496

◆ 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
306  {
307  STATUS status;
308  char tmpdesc[KS_DESCRIPTION_LENGTH];
309 
310  if ((isinumber < 4) || (isinumber > 7)) {
311  return ks_error("%s: ISI number out of range (should be 4-7)", __FUNCTION__);
312  }
313 
314  ks_create_suffixed_description(tmpdesc, desc, "_isirotfun");
315  status = ks_eval_wait(&isirot->waitfun, tmpdesc, RUP_GRD(KS_ISI_time), RUP_GRD(KS_ISI_time));
316  KS_RAISE(status);
317 
318  ks_create_suffixed_description(tmpdesc, desc, "_isirotupdate");
319  status = ks_eval_wait(&isirot->waitrot, tmpdesc, RUP_GRD(KS_ISI_rotupdatetime), RUP_GRD(KS_ISI_time));
320  KS_RAISE(status);
321 
322  isirot->isinumber = isinumber;
323  isirot->duration = isirot->waitfun.max_duration + isirot->waitrot.max_duration;
324  isirot->counter = 0;
325  isirot->numinstances = 0;
326 
327  return SUCCESS;
328 }
int isinumber
Definition: KSFoundation.h:561
STATUS ks_error(const char *format,...) __attribute__((format(printf
Common error message function for HOST and TGT
#define KS_DESCRIPTION_LENGTH
Definition: KSFoundation.h:253
STATUS ks_eval_wait(KS_WAIT *wait, const char *const desc, int pg_duration, int max_duration)
Sets up a wait pulse using a KS_WAIT sequence object
Definition: KSFoundation_host.c:281
#define KS_ISI_rotupdatetime
Definition: KSFoundation.h:198
KS_WAIT waitfun
Definition: KSFoundation.h:558
void ks_create_suffixed_description(char *const out, const char *const prefix, const char *suffix,...) __attribute__((format(printf
int counter
Definition: KSFoundation.h:563
#define KS_RAISE(status)
Definition: KSFoundation.h:190
int numinstances
Definition: KSFoundation.h:564
KS_WAIT waitrot
Definition: KSFoundation.h:559
int max_duration
Definition: KSFoundation.h:546
#define KS_ISI_time
Definition: KSFoundation.h:197
int duration
Definition: KSFoundation.h:562

◆ 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
333  {
334  char tmpdesc[KS_DESCRIPTION_LENGTH];
335  int duration;
336  float rbw;
337  int tsp;
338  int override_R1;
339  int noutputs;
340 
341  /* store desired inputs before resetting */
342  duration = read->duration;
343  rbw = read->rbw;
344  override_R1 = read->override_R1;
345 
346  strcpy(tmpdesc, desc); /* copy in case the read->description has been passed in before ks_init_read wipes it */
347 
348  /* Reset all fields (after saving the desired info) */
349  ks_init_read(read);
350 
351  /* put back the input fields */
352  read->duration = duration;
353  read->rbw = ks_calc_nearestbw(rbw); /* round to nearest valid rBW */
354  read->override_R1 = override_R1;
355  tsp = ks_calc_bw2tsp(read->rbw);
356  strncpy(read->description, tmpdesc, KS_DESCRIPTION_LENGTH - 1);
357  read->description[KS_DESCRIPTION_LENGTH - 1] = 0;
358 
359  /* don't allow empty description or a description with leading space */
360  if (read->description == NULL || read->description[0] == ' ') {
361  return ks_error("ks_eval_read: read name (2nd arg) cannot be NULL or leading space");
362  }
363 
364  if (read->rbw <= 0) {
365  return ks_error("ks_eval_read: field 'rbw' (1st arg) is not set");
366  }
367  if (read->duration <= 2) {
368  return ks_error("ks_eval_read: field 'duration' (1st arg) must be a positive number in [us]");
369  }
370 
371  noutputs = CEIL_DIV(read->duration, tsp);
372  read->duration = noutputs * tsp;
373 
374  /* calculate read->filter (FILTER_INFO) */
375  STATUS status = ks_calc_filter(&read->filt, tsp, read->duration);
376  KS_RAISE(status);
377 
378  return SUCCESS;
379 }
float ks_calc_nearestbw(float bw)
Round receiver bandwidth to nearest valid value
Definition: KSFoundation_host.c:6221
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:6149
LONG override_R1
Definition: KSFoundation.h:845
STATUS ks_error(const char *format,...) __attribute__((format(printf
Common error message function for HOST and TGT
float rbw
Definition: KSFoundation.h:842
KS_DESCRIPTION description
Definition: KSFoundation.h:840
#define KS_DESCRIPTION_LENGTH
Definition: KSFoundation.h:253
int ks_calc_bw2tsp(float bw)
Convert receiver bandwidth to dwell time
Definition: KSFoundation_host.c:6197
int duration
Definition: KSFoundation.h:841
#define KS_RAISE(status)
Definition: KSFoundation.h:190
FILTER_INFO filt
Definition: KSFoundation.h:843

◆ 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
384  {
385  double triangletrapezoid_area;
386  double maxramp_area;
387  double requested_area;
388  int maxramp_duration;
389  int sign_reqarea;
390  int minplateautime = KS_MINPLATEAUTIME;
391  char tmpdesc[KS_DESCRIPTION_LENGTH];
392 
393  /* store desired inputs before resetting the trap object */
394  requested_area = (double) trap->area;
395  strcpy(tmpdesc, desc); /* copy in case the trap->description has been passed in before ks_init_trap wipes it */
396 
397  /* Reset all fields and waveforms (after saving the desired area) */
398  ks_init_trap(trap);
399 
400  /* put back the input fields */
401  trap->area = requested_area;
402 
403  strncpy(trap->description, tmpdesc, KS_DESCRIPTION_LENGTH - 1);
404  trap->description[KS_DESCRIPTION_LENGTH - 1] = 0;
405 
406  /* don't allow empty description or a description with leading space */
407  if (trap->description == NULL || trap->description[0] == ' ') {
408  return ks_error("ks_eval_trap: trap name (2nd arg) cannot be NULL or leading space");
409  }
410 
411  if (areSame(requested_area, 0)) {
412  return SUCCESS;
413  }
414 
415  /* sign control: Store the sign of the requested area. Ignore all other signs */
416  sign_reqarea = (requested_area < 0) ? -1 : 1;
417  requested_area = fabs(requested_area);
418  slewrate = fabs(slewrate);
419  ampmax = fabs(ampmax);
420 
421  /* make sure the minimum duration is divisible by 8us */
422  minduration = RUP_FACTOR(minduration, 2 * GRAD_UPDATE_TIME);
423 
424 
425  /* area for one ramp from zero to max amplitude ('ampmax') */
426  maxramp_duration = RUP_GRD(ceil((double) ampmax / (double) slewrate)); /* [usec] */
427  maxramp_area = (double) ampmax * ((double) maxramp_duration) / 2.0; /* [G/cm * usec] */
428 
429  /* area for a trapezoid with a plateau time of 'minplateautime' and maximum amplitude ('ampmax') */
430  triangletrapezoid_area = ((double) minplateautime * (double) ampmax) + (2.0 * maxramp_area);
431 
432  if (requested_area > triangletrapezoid_area) {
433  /* ________......ampmax
434  / \
435  / \
436  / \
437  __/ \__
438 
439  We need max target amplitude and use a plateau time > minplateautime to reach required area. [amp = ampmax] */
440 
441  trap->ramptime = maxramp_duration;
442  trap->plateautime = (int) ceil((requested_area - 2.0 * maxramp_area) / (double) ampmax);
443 
444  /* Round *up* to nearest 8us to make the plateau contain an even # of points (which is always nice) */
445  trap->plateautime = RUP_FACTOR(trap->plateautime, 2 * GRAD_UPDATE_TIME);
446 
447 
448  } else {
449  /*
450  ......... ampmax
451 
452  _ amp
453  / \
454  / \
455  __/ \__
456 
457  We don't need max amplitude and will use a minimum plateau time of minplateautime
458  [amp = ramptime * slewrate] */
459 
460  trap->plateautime = minplateautime; /* Our minimum plateau time */
461 
462  /* Quadratic solution in 'ramptime':
463  slewrate*(ramptime)^2 + slewrate*minplateautime*(ramptime) - requested_area = 0
464  ...where: ramptime = unknownamp / slewrate <=> unknownamp = ramptime * slewrate */
465  trap->ramptime = ((int) ceil(pow(requested_area / (double) slewrate + ((double) minplateautime * (double) minplateautime) / 4.0, 0.5))) - minplateautime / 2;
466 
467  /* Round *up* to nearest GRAD_UPDATE_TIME (unit: usec) */
468  trap->ramptime = RUP_GRD(trap->ramptime);
469  }
470 
471 
472  /* amp (G/cm). Make the sign of the amp equal to that of the requested_area */
473  trap->amp = (float) (sign_reqarea * requested_area / ((double) (trap->ramptime + trap->plateautime)));
474 
475  /* amp watchdog */
476  if (fabs(trap->amp) > ampmax)
477  return ks_error("ks_eval_trap: amp (%.3f) of trap '%s' exceeds ampmax (%.3f)", trap->amp, trap->description, ampmax);
478 
479  /* store the duration */
480  trap->duration = trap->ramptime * 2 + trap->plateautime;
481 
482  /* if the duration is smaller than the minimum duration, increase the ramp times and reduce the gradient amp accordingly */
483  if (trap->duration < minduration) {
484  trap->ramptime += (minduration - trap->duration)/2;
485  trap->duration = trap->ramptime * 2 + trap->plateautime;
486  trap->amp = (float) (sign_reqarea * requested_area / ((double) (trap->ramptime + trap->plateautime)));
487  }
488 
489 
490  return SUCCESS;
491 
492 }
int plateautime
Definition: KSFoundation.h:672
#define areSame(a, b)
Definition: KSFoundation.h:144
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:62
#define KS_DESCRIPTION_LENGTH
Definition: KSFoundation.h:253
float area
Definition: KSFoundation.h:670
float amp
Definition: KSFoundation.h:669
KS_DESCRIPTION description
Definition: KSFoundation.h:668
#define KS_MINPLATEAUTIME
Definition: KSFoundation.h:202
int duration
Definition: KSFoundation.h:673
int ramptime
Definition: KSFoundation.h:671

◆ 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
497  {
498 
500 
501 }
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:235
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:384
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:305

◆ 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
506  {
507 
509 
510 }
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:384
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:242
float ks_syslimits_slewrate2(LOG_GRAD loggrd)
Returns the maximum slewrate that can be used on two gradient boards simultaneously
Definition: KSFoundation_common.c:320

◆ 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
515  {
516 
518 
519 }
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:384
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:335
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:249

◆ 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 
)
535  {
536  int requested_time;
537  int maxramp_duration;
538  char tmpdesc[KS_DESCRIPTION_LENGTH];
539  int sign = (maxarea < 0) ? -1 : 1;
540  strcpy(tmpdesc, desc); /* copy in case the trap->description has been passed in before ks_init_trap wipes it */
541 
542  /* make sure the minimum duration is divisible by 8us */
543  requested_time = RDN_FACTOR(trap->duration, 2 * GRAD_UPDATE_TIME);
544  /* Reset all fields and waveforms (after saving the desired area) */
545  ks_init_trap(trap);
546  strncpy(trap->description, tmpdesc, KS_DESCRIPTION_LENGTH - 1);
547  trap->description[KS_DESCRIPTION_LENGTH - 1] = 0;
548 
549  /* don't allow empty description or a description with leading space */
550  if (trap->description == NULL || trap->description[0] == ' ') {
551  return ks_error("%s: trap name (2nd arg) cannot be NULL or leading space", __FUNCTION__);
552  }
553  if (requested_time <= 0) {
554  return SUCCESS;
555  }
556 
557  slewrate = fabs(slewrate); /* [(G/cm) / us] */
558  ampmax = fabs(ampmax); /* [G/cm] */
559  maxramp_duration = RUP_GRD(ceil((double) ampmax / (double) slewrate)); /* [usec] */
560  if ((2*maxramp_duration + KS_MINPLATEAUTIME) > requested_time) {
561  /* Not enough time to make it to the plateau */
563  trap->ramptime = (requested_time - KS_MINPLATEAUTIME) / 2;
564  trap->amp = slewrate * trap->ramptime;
565  } else {
566  /* Enough time to reach plateau */
567  trap->amp = ampmax;
568  trap->ramptime = maxramp_duration;
569  trap->plateautime = (requested_time - 2 * trap->ramptime);
570  }
571 
572  trap->area = (trap->plateautime + trap->ramptime) * trap->amp * sign;
573  if (fabs(trap->area) > fabs(maxarea)) {
574  if (derate_slewrate == 1) {
575  trap->amp = maxarea / (trap->plateautime + trap->ramptime);
576  } else {
577  trap->ramptime = RUP_GRD( (int) ((requested_time - sqrtf(requested_time*requested_time - 4*maxarea/slewrate)) / 2.0));
578  trap->amp = trap->ramptime * slewrate;
579  trap->plateautime = (requested_time - 2 * trap->ramptime);
580  }
581  /* Adjust the amplitude to never go above maxarea */
582  trap->amp *= fabs(maxarea / ((trap->plateautime + trap->ramptime) * trap->amp));
583  trap->area = (trap->plateautime + trap->ramptime) * trap->amp * sign;
584  }
585 
586  trap->duration = trap->ramptime * 2 + trap->plateautime;
587  if (trap->duration != requested_time) {
588  ks_dbg("%s: Duration mismatch. this should not happen", __FUNCTION__);
589  }
590  return SUCCESS;
591 }
int plateautime
Definition: KSFoundation.h:672
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:62
#define KS_DESCRIPTION_LENGTH
Definition: KSFoundation.h:253
float area
Definition: KSFoundation.h:670
float amp
Definition: KSFoundation.h:669
KS_DESCRIPTION description
Definition: KSFoundation.h:668
#define KS_MINPLATEAUTIME
Definition: KSFoundation.h:202
STATUS STATUS ks_dbg(const char *format,...) __attribute__((format(printf
Common debug message function for HOST and TGT
int duration
Definition: KSFoundation.h:673
int ramptime
Definition: KSFoundation.h:671

◆ 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
624  {
625 
627 
628 }
float ks_syslimits_ampmax1p(LOG_GRAD loggrd)
Returns the maximum gradient amplitude on the physical gradient axes
Definition: KSFoundation_common.c:257
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:384
LOG_GRAD loggrd
float ks_syslimits_slewrate1p(LOG_GRAD loggrd)
Returns the maximum gradient amplitude on the physical gradient axes
Definition: KSFoundation_common.c:352

◆ ks_eval_trap_rotation_invariant_75_max_slewrate()

STATUS ks_eval_trap_rotation_invariant_75_max_slewrate ( 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)

and the slewrate limited to 75 T/m/s to avoid unecessary PNS

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 the gradient amplitude limited to 1/sqrt(3) of the weakest amplifier (rotation invariant even if played on all boards simultanously). The slewrate is limited to 1/sqrt(3) of the weakest amplifier or 75 T/m/s to avoid unessesary PNS. This trapezoid setup is a good choice for not so timing sensitive situations (spoiler, SPGR phasers, ...)

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
524  {
525  const float max_slewrate = 75.0/10000; /* 75 T/m/s converted to G/cm/us */
526  return ks_eval_trap_constrained(trap, desc,
527  ks_syslimits_ampmax_phys()/sqrt(3),
528  FMin(3, ks_syslimits_slewrate_phys()/sqrt(3), ks_syslimits_slewrate(loggrd), max_slewrate),
529  0);
530 }
float ks_syslimits_slewrate_phys()
ADDTITLEHERE
Definition: KSFoundation_common.c:298
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:384
LOG_GRAD loggrd
float ks_syslimits_ampmax_phys()
ADDTITLEHERE
Definition: KSFoundation_common.c:228
float ks_syslimits_slewrate(LOG_GRAD loggrd)
Returns the maximum slewrate that can be used on all gradient boards simultaneously
Definition: KSFoundation_common.c:305

◆ ks_eval_crusher()

STATUS ks_eval_crusher ( KS_WAVE crusher,
const float  end_amp,
const ks_crusher_constraints  crusher_constraints 
)

Sets up a KS_WAVE object that ramps from zero to the specified amplitude and ensures you obtain the minimum area.

The amplitude of the KS_WAVE object returned by this function has the same sign as the taget amplitude (end_amp).

Parameters
[out]crusherPointer to the KS_WAVE object to be set up
[in]end_ampADDTEXTHERE
[in]crusher_constraintsADDTEXTHERE
Return values
STATUSSUCCESS or FAILURE
964  {
965 
966  STATUS status;
967 
968  float G[6] = {0};
969  float t[6] = {0};
970  int num_points;
971 
972  const float amp_sign = end_amp >= 0 ? 1 : -1;
973 
974  switch(crusher_constraints.strategy) {
976  status = ks_calc_exact_transition(G, t, &num_points,
977  amp_sign*crusher_constraints.area, amp_sign*end_amp,
978  crusher_constraints.min_duration,
979  crusher_constraints.slew, crusher_constraints.ampmax,
980  crusher_constraints.min_plateau);
981  break;
983  status = ks_calc_minslew_transition(G, t, &num_points,
984  amp_sign*crusher_constraints.area, amp_sign*end_amp,
985  crusher_constraints.min_duration,
986  crusher_constraints.slew, crusher_constraints.ampmax,
987  crusher_constraints.min_plateau);
988  break;
990  status = ks_calc_maxarea_transition(G, t, &num_points,
991  amp_sign*crusher_constraints.area, amp_sign*end_amp,
992  crusher_constraints.min_duration,
993  crusher_constraints.slew, crusher_constraints.ampmax,
994  crusher_constraints.min_plateau);
995  break;
997  status = ks_calc_unconstrained_transition(G, t, &num_points,
998  amp_sign*crusher_constraints.area, amp_sign*end_amp,
999  crusher_constraints.slew, amp_sign*end_amp,
1000  crusher_constraints.min_plateau);
1001  break;
1002  default:
1003  status = ks_calc_unconstrained_transition(G, t, &num_points,
1004  amp_sign*crusher_constraints.area, amp_sign*end_amp,
1005  crusher_constraints.slew, crusher_constraints.ampmax,
1006  crusher_constraints.min_plateau);
1007  }
1008  KS_RAISE(status);
1009 
1010  /* Flip coordinates if the sign was negative */
1011  int i=0;
1012  for (; i<num_points; ++i) {
1013  G[i] *= amp_sign;
1014  }
1015 
1016  status = ks_eval_coords2wave(crusher, t, G, num_points, GRAD_UPDATE_TIME, "crusher");
1017  if (status != SUCCESS) {
1018  ks_dbg("strategy: %d", crusher_constraints.strategy);
1019  ks_dbg("area: %f", crusher_constraints.area);
1020  ks_dbg("end_amp: %f", end_amp);
1021  ks_dbg("min_duration: %d", crusher_constraints.min_duration);
1022  ks_dbg("min_plateau: %d", crusher_constraints.min_plateau);
1023  ks_dbg("slew: %f", crusher_constraints.slew);
1024  for (i=0; i<num_points; ++i) {
1025  ks_dbg("%f: %f", t[i], G[i]);
1026  }
1027  }
1028  KS_RAISE(status);
1029 
1030  return SUCCESS;
1031 }
float slew
Definition: KSFoundation.h:2372
Definition: KSFoundation.h:2364
Definition: KSFoundation.h:2365
int min_duration
Definition: KSFoundation.h:2373
STATUS ks_calc_exact_transition(float *G, float *t, int *num_points, float req_area, float end_amp, float min_duration, float slew, float ampmax, unsigned int min_plateau)
Definition: KSFoundation_host.c:741
STATUS ks_eval_coords2wave(KS_WAVE *wave, float *t, float *G, int num_coords, int dwell, const char *desc)
Definition: KSFoundation_host.c:4383
unsigned int min_plateau
Definition: KSFoundation.h:2374
Definition: KSFoundation.h:2363
STATUS ks_calc_maxarea_transition(float *G, float *t, int *num_points, float req_area, float end_amp, float max_duration, float slew, float ampmax, unsigned int min_plateau)
Definition: KSFoundation_host.c:887
float ampmax
Definition: KSFoundation.h:2371
ks_enum_crusher_strategy strategy
Definition: KSFoundation.h:2375
#define KS_RAISE(status)
Definition: KSFoundation.h:190
Definition: KSFoundation.h:2366
float area
Definition: KSFoundation.h:2370
STATUS STATUS ks_dbg(const char *format,...) __attribute__((format(printf
Common debug message function for HOST and TGT
STATUS ks_calc_unconstrained_transition(float *G, float *t, int *num_points, float req_area, float end_amp, float slew, float ampmax, unsigned int min_plateau)
Definition: KSFoundation_host.c:652
STATUS ks_calc_minslew_transition(float *G, float *t, int *num_points, float req_area, float end_amp, float min_duration, float slew, float ampmax, unsigned int min_plateau)
Definition: KSFoundation_host.c:844

◆ 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
1036  {
1037  float minfov;
1038  int tsp = 0;
1039  float readpixelarea;
1040  int nreadpixels;
1041  float nonacq_readarea;
1042  float ampmax_kspace_speedlimit;
1043  STATUS status;
1044 
1045  /* This function is for setting up readout trapezoid with fixed amplitude based on FOV and rBW */
1046 
1047  if (desc == NULL || desc[0] == ' ') {
1048  return ks_error("ks_eval_readtrap: desc (2nd arg) cannot be NULL");
1049  }
1050  if (areSame(readtrap->acq.rbw, 0) || isNotSet(readtrap->acq.rbw)) {
1051  return ks_error("ks_eval_readtrap: field 'acq.rbw' (1st arg) is not set");
1052  }
1053 
1054  /* reset KS_TRAP's */
1055  ks_init_trap(&readtrap->grad);
1056  ks_init_wave(&readtrap->omega);
1057 
1058 
1059  /* round rBW to nearest valid value */
1060  readtrap->acq.rbw = ks_calc_nearestbw(readtrap->acq.rbw);
1061  tsp = ks_calc_bw2tsp(readtrap->acq.rbw); /* us per sample point */
1062 
1063  minfov = ks_calc_minfov(ampmax, tsp);
1064 
1065  /* Programmer's error */
1066  if (readtrap->res % 2 || readtrap->res <= 0 || readtrap->res > 2048) {
1067  return ks_error("ks_eval_readtrap(%s): field 'res' must be even and in the range 2-2048", desc);
1068  }
1069  if (readtrap->fov < 10 || readtrap->fov > 600) {
1070  return ks_error("ks_eval_readtrap(%s): field 'fov' must be in the range 10-600 mm", desc);
1071  }
1072  if (abs(readtrap->nover) > readtrap->res / 2) {
1073  return ks_error("ks_eval_readtrap(%s): field 'nover' may not exceed res/2", desc);
1074  }
1075  if (readtrap->acq.rbw > 250) {
1076  return ks_error("ks_eval_readtrap(%s): rBW cannot exceed +/- 250 kHz/FOV", desc);
1077  }
1078  /* Operator's error */
1079  if (readtrap->fov < minfov && readtrap->rampsampling == 0) {
1080  return ks_error("%s: Please increase the FOV to %.1f [cm] or decrease the rBW", desc, minfov / 10.0);
1081  }
1082 
1083  if (abs(readtrap->nover)) {
1084  nreadpixels = readtrap->res / 2 + abs(readtrap->nover);
1085  } else {
1086  nreadpixels = readtrap->res;
1087  }
1088  if (nreadpixels % 2) {
1089  return ks_error("ks_eval_readtrap(%s): number of sampling points must be even (%d)", desc, nreadpixels);
1090  }
1091 
1092 
1093  if (readtrap->rampsampling == 1) { /* rampsampling */
1094 
1095  /* Gradient area necessary to move one pixel in k-space in the read direction */
1096  readpixelarea = ks_calc_fov2gradareapixel(readtrap->fov); /* [(G/cm)*usec] */
1097 
1098  /* Speed limit the readout (i.e. put a cap on the readout amplitude) based on the FOV and the chosen rBW */
1099  ampmax_kspace_speedlimit = FMin(2, readpixelarea / tsp, ampmax);
1100 
1101  /* delay of ACQ start relative to the beginning of the attack ramp.
1102  For rampsampled cases we need to ensure the XTR pulses of
1103  consecutive readouts don't overlap eg. EPI train */
1104  int min_acqdelay = (int)(XTRSETLNG + XTR_TAIL + 1) / 2;
1105  if (readtrap->acqdelay < min_acqdelay) {
1106  readtrap->acqdelay = min_acqdelay;
1107  }
1108  readtrap->acqdelay = RUP_FACTOR(readtrap->acqdelay, 2);
1109 
1110  /* gradient area before and after the acq window */
1111  nonacq_readarea = slewrate /* [G/cm/usec] */ * (readtrap->acqdelay) * (readtrap->acqdelay) /* [usec^2] */; /* from both sides of the readout */
1112 
1113  /* required gradient area: gradient area needed during the acq window + nonacq_readarea */
1114  readtrap->grad.area = (readpixelarea * nreadpixels) + nonacq_readarea;
1115 
1116  status =ks_eval_trap_constrained(&readtrap->grad, desc, ampmax_kspace_speedlimit, slewrate, 0);
1117  KS_RAISE(status);
1118 
1119 
1120  if (readtrap->grad.ramptime > readtrap->acqdelay) {
1121  /* rampsampling attempt ok, continue calculating area2center and time2center */
1122 
1123  if (readtrap->nover > 0) { /* partial Fourier on the first part of the readout */
1124  readtrap->area2center = readpixelarea * readtrap->nover + nonacq_readarea / 2;
1125  } else { /* Full Fourier, or partial Fourier on the last part of the readout */
1126  readtrap->area2center = readpixelarea * readtrap->res / 2 + nonacq_readarea / 2;
1127  }
1128 
1129  /* area = (s*t^2)/2 [G/cm/usec] * [usec^2] */
1130  if (readtrap->area2center < slewrate * (readtrap->grad.ramptime * readtrap->grad.ramptime / 2.0)) {
1131  /* k-space center is on the attack ramp */
1132  readtrap->time2center = (int) sqrt(readtrap->area2center * 2.0 / slewrate);
1133  } else {
1134  float arealeftonplateau = readtrap->area2center - (readtrap->grad.ramptime * readtrap->grad.amp / 2.0);
1135  readtrap->time2center = (int) readtrap->grad.ramptime + (arealeftonplateau / readtrap->grad.amp); /* whole ramp + some plateau time */
1136  }
1137 
1138  /* round up the readout window time to the nearest multiple of '2*tsp' to always make filt.outputs even */
1139  readtrap->acq.duration = RUP_FACTOR(readtrap->grad.duration - readtrap->acqdelay * 2, (int) (2 * tsp));
1140 
1141  /* update 'acqdelay' due to this potential roundoff */
1142  readtrap->acqdelay = (readtrap->grad.duration - readtrap->acq.duration) / 2;
1143 
1144  } else {
1145 
1146  readtrap->rampsampling = 0; /* let's skip ramp sampling then (and be caught by the next non-rampsampled case) */
1147 
1148  }
1149 
1150  } /* rampsampling */
1151 
1152 
1153 
1154 
1155  if (readtrap->rampsampling == 0) { /* No rampsampling */
1156 
1157  if (desc[0] == ' ') {
1158  ks_init_trap(&readtrap->grad);
1159  return ks_error("ks_eval_readtrap: desc (2nd arg) cannot begin with a space");
1160  } else {
1161  strncpy(readtrap->grad.description, desc, KS_DESCRIPTION_LENGTH - 1);
1162  readtrap->grad.description[KS_DESCRIPTION_LENGTH - 1] = 0;
1163  }
1164 
1165  /* Error if requested constraints cannot be fullfilled */
1166  if (readtrap->fov < minfov) {
1167  return ks_error("%s: %s - Please increase the FOV to %.1f [cm] or decrease the rBW", __FUNCTION__, desc, minfov / 10.0);
1168  }
1169 
1170  readtrap->grad.amp = ((float) (1.0 / (GAM * tsp * 1e-6) * (10.0 / readtrap->fov))); /* [G/cm] */
1171  readtrap->grad.ramptime = RUP_GRD(readtrap->grad.amp / slewrate); /* [usec] */
1172  readtrap->acq.duration = RUP_GRD(nreadpixels * tsp);
1173 
1174  /* adjust plateau time to account for padding */
1175  const float ramparea = readtrap->grad.ramptime * readtrap->grad.amp / 2.0;
1176  const int extradelay = RUP_GRD(FMax(2, readtrap->paddingarea - ramparea, 0.0) / readtrap->grad.amp);
1177  readtrap->grad.plateautime = readtrap->acq.duration + 2*extradelay; /* [usec] */ /* Nkx * dwell time */
1178 
1179  readtrap->grad.duration = readtrap->grad.plateautime + readtrap->grad.ramptime * 2; /* [usec] */
1180  readtrap->grad.area = (readtrap->grad.plateautime + readtrap->grad.ramptime) * readtrap->grad.amp; /* rounding effects */
1181 
1182  /* ACQ starts after the attack ramp */
1183  readtrap->acqdelay = readtrap->grad.ramptime + extradelay;
1184 
1185  const int pixelstocenter = readtrap->nover > 0 ?
1186  readtrap->nover : /* partial Fourier on the first part of the readout */
1187  readtrap->res/2; /* Full Fourier, or partial Fourier on the last part of the readout */
1188 
1189  readtrap->area2center = (readtrap->grad.ramptime/2 + tsp * pixelstocenter + extradelay) * readtrap->grad.amp;
1190  readtrap->time2center = readtrap->grad.ramptime + tsp * pixelstocenter + extradelay;
1191 
1192  } /* no rampsampling */
1193 
1194  /* round up to nearest multiple of GRAD_UPDATE_TIME (4us) */
1195  readtrap->time2center = RUP_GRD(readtrap->time2center);
1196 
1197  /* setup acq window */
1198  /* readtrap->acq.rbw is set > 0 by the calling function */
1199  KS_DESCRIPTION read_description;
1200  ks_create_suffixed_description(read_description, readtrap->grad.description, ".echo");
1201  status = ks_eval_read(&readtrap->acq, read_description);
1202  KS_RAISE(status);
1203 
1204  /* for rampsampling, create an omega waveform (for FOV shifts in the freq (read) direction for rampsampling) */
1205  if (readtrap->rampsampling) {
1206  float ramp_sample_period = readtrap->grad.ramptime - readtrap->acqdelay;
1207  float t[4] = {0.0f, ramp_sample_period, (float)readtrap->acq.duration - ramp_sample_period, (float)readtrap->acq.duration};
1208  float start_amp = readtrap->grad.amp / readtrap->grad.ramptime * readtrap->acqdelay;
1209  float G[4] = {start_amp, readtrap->grad.amp, readtrap->grad.amp, start_amp};
1210  int k;
1211  /*ensure 150 mm fov offset is possible*/
1212  for (k = 0; k < 4; k++) G[k] *= GAM / 10.0;
1213  KS_DESCRIPTION omega_description;
1214  ks_create_suffixed_description(omega_description, readtrap->grad.description, ".omega");
1215  STATUS status;
1216  status = ks_eval_coords2wave(&readtrap->omega, t, G, 4, GRAD_UPDATE_TIME, omega_description);
1217  KS_RAISE(status);
1218  readtrap->omega.fs_factor = 0.1; /* so 1 bit of iamp = 0.1 mm */
1219  } else {
1220  readtrap->omega.duration = 0;
1221  }
1222 
1223 
1224  return SUCCESS;
1225 } /* ks_eval_readtrap_constrained() */
int plateautime
Definition: KSFoundation.h:672
float fs_factor
Definition: KSFoundation.h:756
float ks_calc_nearestbw(float bw)
Round receiver bandwidth to nearest valid value
Definition: KSFoundation_host.c:6221
#define areSame(a, b)
Definition: KSFoundation.h:144
float ks_calc_fov2gradareapixel(float fov)
Calculates the gradient area needed to move one pixel in k-space
Definition: KSFoundation_common.c:492
float ks_calc_minfov(float ampmax, int tsp)
Calculates the minimum readout FOV
Definition: KSFoundation_common.c:459
STATUS ks_eval_coords2wave(KS_WAVE *wave, float *t, float *G, int num_coords, int dwell, const char *desc)
Definition: KSFoundation_host.c:4383
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:62
float fov
Definition: KSFoundation.h:1551
KS_WAVE omega
Definition: KSFoundation.h:1562
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:384
float rbw
Definition: KSFoundation.h:842
KS_TRAP grad
Definition: KSFoundation.h:1561
float paddingarea
Definition: KSFoundation.h:1556
#define KS_DESCRIPTION_LENGTH
Definition: KSFoundation.h:253
int res
Definition: KSFoundation.h:1552
int ks_calc_bw2tsp(float bw)
Convert receiver bandwidth to dwell time
Definition: KSFoundation_host.c:6197
void ks_create_suffixed_description(char *const out, const char *const prefix, const char *suffix,...) __attribute__((format(printf
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:333
int duration
Definition: KSFoundation.h:841
float area
Definition: KSFoundation.h:670
#define KS_RAISE(status)
Definition: KSFoundation.h:190
float amp
Definition: KSFoundation.h:669
void ks_init_wave(KS_WAVE *wave)
Resets a KS_WAVE sequence object to its default value (KS_INIT_WAVE)
Definition: KSFoundation_host.c:78
KS_DESCRIPTION description
Definition: KSFoundation.h:668
int nover
Definition: KSFoundation.h:1554
int rampsampling
Definition: KSFoundation.h:1553
KS_READ acq
Definition: KSFoundation.h:1549
int duration
Definition: KSFoundation.h:673
char KS_DESCRIPTION[KS_DESCRIPTION_LENGTH]
Definition: KSFoundation.h:351
int time2center
Definition: KSFoundation.h:1560
float area2center
Definition: KSFoundation.h:1559
#define isNotSet(a)
Definition: KSFoundation.h:164
int acqdelay
Definition: KSFoundation.h:1555
int ramptime
Definition: KSFoundation.h:671
int duration
Definition: KSFoundation.h:747

◆ 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
1230  {
1231 
1232  return ks_eval_readtrap_constrained(readtrap, desc,
1234 
1235 }
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:235
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:1036
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:305

◆ 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
1240  {
1241 
1242  return ks_eval_readtrap_constrained(readtrap, desc,
1244 
1245 }
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:1036
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:242
float ks_syslimits_slewrate2(LOG_GRAD loggrd)
Returns the maximum slewrate that can be used on two gradient boards simultaneously
Definition: KSFoundation_common.c:320

◆ 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
1250  {
1251 
1252  return ks_eval_readtrap_constrained(readtrap, desc,
1254 
1255 }
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:335
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:1036
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:249

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

STATUS ks_eval_readwave_constrained ( KS_READWAVE readwave,
KS_WAVE acq_waves,
const int  numstates,
int  flag_symmetric_padding,
ks_crusher_constraints  pre_crusher_constraints,
ks_crusher_constraints  post_crusher_constraints 
)

Sets up an readwave acquisition window with the maximum sampling bandwidth.

The following members of the readwave object are treated as inputs to this function: fov - Desired image Field-of-View (FOV) in [mm] along the sequence board one intends to place the KS_READWAVE on (typically XGRAD) res - Number of pixels within the FOV 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.

Parameters
[in,out]readwave- Pointer to KS_READWAVE
[in]acq_waves- an array of KS_WAVE objects describing the gradient amplitude while the ADC is on.
[in]numstates- number of KS_WAVES in acq_waves
[in]flag_symmetric_padding- match pre and post areas and duration
[in]pre_crusher_constraints- how to optimise the pre crusher waveform shape (fastest, exact area, maximum area)
[in]post_crusher_constraints- how to optimise the post crusher waveform shape (fastest, exact area, maximum area)
Return values
STATUSSUCCESS or FAILURE
1445  {
1446  STATUS status;
1447 
1448  typedef struct max {
1449  float amp;
1450  int index;
1451  } max;
1452 
1453  int i = 0;
1454  KS_WAVE *precrushers = (KS_WAVE*)alloca(numstates*sizeof(KS_WAVE));
1455  KS_WAVE *postcrushers = (KS_WAVE*)alloca(numstates*sizeof(KS_WAVE));
1456  int max_precrusher_res = 0;
1457  int max_postcrusher_res = 0;
1458 
1459  max acq_max = {0, -1};
1460  max acq_start_max = {0, -1};
1461  max acq_end_max = {0, -1};
1462 
1463  int acq_res = acq_waves[0].res;
1464  int acq_duration = acq_waves[0].duration;
1465 
1466  /* Programmer's error */
1467  if (acq_res <= 0) {
1468  return ks_error("%s (%s): invalid res (%d)", __FUNCTION__, acq_waves[i].description, acq_res);
1469  }
1470 
1471  if (readwave->fov < 10 || readwave->fov > 600) {
1472  return ks_error("%s (%s): field 'fov' must be in the range 10-600 mm", __FUNCTION__, acq_waves[i].description);
1473  }
1474  if (readwave->res % 2 || readwave->res <= 0 || readwave->res > 2048) {
1475  return ks_error("%s (%s): field 'res' must be even and in the range 2-2048", __FUNCTION__, acq_waves[i].description);
1476  }
1477  if (abs(readwave->nover) > readwave->res / 2) {
1478  return ks_error("%s (%s): field 'nover' may not exceed res/2", __FUNCTION__, acq_waves[i].description);
1479  }
1480 /* if (abs(readwave->nover) % 2) {
1481  return ks_error("%s (%s): nover must be even (%d)", __FUNCTION__, acq_waves[i].description, abs(readwave->nover));
1482  } */
1483  if ( (acq_waves[i].duration / acq_waves[i].res) != GRAD_UPDATE_TIME) {
1484  return ks_error("%s: acq_waves[i] must have dwell = GRAD_UPDATE_TIME", __FUNCTION__);
1485  }
1486  int polarity[numstates];
1487  for (; i < numstates; i++) {
1488  polarity[i] = 1;
1489  if ( acq_waves[i].res != acq_res ) {
1490  return ks_error("%s: acq_waves[%d] does not match res of acq_waves[0] (%d != %d)", __FUNCTION__, i, acq_waves[i].res, acq_waves[0].res);
1491  }
1492  if (acq_waves[i].duration != acq_duration) {
1493  return ks_error("%s: acq_waves[%d] does not match duration of acq_waves[0] (%d != %d)", __FUNCTION__, i, acq_waves[i].duration, acq_waves[0].duration);
1494  }
1495  if (acq_waves[i].waveform[0] < 0 && acq_waves[i].waveform[acq_waves[i].res - 1] < 0) {
1496  /* Assume entire wave is negative. Negate the wave, continue, and re-negate the state later */
1497  polarity[i] = -1;
1498  ks_wave_multiplyval(&acq_waves[i], -1.0);
1499  } else if (acq_waves[i].waveform[acq_waves[i].res - 1] < 0 || acq_waves[i].waveform[0] < 0){
1500  return ks_error("%s: acq_waves[%d] changes polarity", __FUNCTION__, i);
1501  }
1502  if (acq_waves[i].abs_max_amp > acq_max.amp) {
1503  acq_max.amp = acq_waves[i].abs_max_amp;
1504  acq_max.index = i;
1505  }
1506 
1507  const float start_amp = 1.5f * acq_waves[i].waveform[0] - 0.5f * acq_waves[i].waveform[1];
1508  if (acq_start_max.amp < start_amp) {
1509  acq_start_max.amp = start_amp;
1510  acq_start_max.index = i;
1511  }
1512 
1513  const float end_amp = 1.5f * acq_waves[i].waveform[acq_waves[i].res - 1] - 0.5f * acq_waves[i].waveform[acq_waves[i].res - 2];
1514  if (acq_end_max.amp < end_amp) {
1515  acq_end_max.amp = end_amp;
1516  acq_end_max.index = i;
1517  }
1518  }
1519 
1520  /* Set ampmax and slew if no preference */
1521  for (i = 0; i < 2; i++) {
1522  ks_crusher_constraints* constraints = i == 0 ? &pre_crusher_constraints
1523  : &post_crusher_constraints;
1524  constraints->ampmax = constraints->ampmax > 0 ? constraints->ampmax : ks_syslimits_ampmax(loggrd);
1525  constraints->slew = constraints->slew > 0 ? constraints->slew : ks_syslimits_slewrate(loggrd);
1526  }
1527 
1528  max grad_max = acq_max;
1529 
1530  KS_WAVE max_precrusher = KS_INIT_WAVE;
1531  KS_WAVE max_postcrusher = KS_INIT_WAVE;
1532  status = ks_eval_crusher(&max_precrusher, acq_start_max.amp, pre_crusher_constraints);
1533  KS_RAISE(status);
1534  status = ks_eval_crusher(&max_postcrusher, acq_end_max.amp, post_crusher_constraints);
1535  KS_RAISE(status);
1536 
1537  for (i=0; i < numstates; i++) {
1538 
1539  const float start_amp = 1.5f * acq_waves[i].waveform[0] - 0.5f * acq_waves[i].waveform[1];
1540  const float end_amp = 1.5f * acq_waves[i].waveform[acq_waves[i].res - 1] - 0.5f * acq_waves[i].waveform[acq_waves[i].res - 2];
1541 
1542  const float max_crusher_area = fabs(max_precrusher.area) > fabs(max_postcrusher.area) ?
1543  max_precrusher.area : max_postcrusher.area;
1544 
1545  pre_crusher_constraints.area = flag_symmetric_padding ? max_crusher_area : max_precrusher.area;
1546  pre_crusher_constraints.strategy = KS_CRUSHER_STRATEGY_EXACT;
1547  status = ks_eval_crusher(&precrushers[i], start_amp, pre_crusher_constraints);
1548  KS_RAISE(status);
1549 
1550  post_crusher_constraints.area = flag_symmetric_padding ? max_crusher_area : max_postcrusher.area;
1551  post_crusher_constraints.strategy = KS_CRUSHER_STRATEGY_EXACT;
1552  status = ks_eval_crusher(&postcrushers[i], end_amp, post_crusher_constraints);
1553  KS_RAISE(status);
1554 
1555 
1556  if (precrushers[i].abs_max_amp > grad_max.amp) {
1557  grad_max.amp = precrushers[i].abs_max_amp;
1558  grad_max.index = i;
1559  }
1560  if (postcrushers[i].abs_max_amp > grad_max.amp) {
1561  grad_max.amp = precrushers[i].abs_max_amp;
1562  grad_max.index = i;
1563  }
1564 
1565  status = ks_eval_mirrorwave(&postcrushers[i]);
1566  KS_RAISE(status);
1567  max_precrusher_res = IMax(2, max_precrusher_res, precrushers[i].res);
1568  max_postcrusher_res = IMax(2, max_postcrusher_res, postcrushers[i].res);
1569  }
1570 
1571  /* zeropad crushers to make them equally long */
1572  float pad_array[KS_MAXWAVELEN/2] = {0};
1573  int res ;
1574  for (i=0; i < numstates; i++) {
1575  readwave->grad.p_waveformstates[i] = readwave->grad_states[i];
1576  readwave->omega.p_waveformstates[i] = readwave->omega_states[i];
1577  float* dest_grad = readwave->grad.p_waveformstates[i];
1578  float* dest_omega = readwave->omega.p_waveformstates[i];
1579  res = 0;
1580  int res_diff_pre = max_precrusher_res - precrushers[i].res;
1581  int res_diff_post = max_postcrusher_res - postcrushers[i].res;
1582 
1583  status = ks_eval_append_two_waveforms(dest_grad, pad_array, 0, res_diff_pre);
1584  KS_RAISE(status);
1585  res += res_diff_pre;
1586  status = ks_eval_append_two_waveforms(dest_grad, precrushers[i].waveform, res, precrushers[i].res);
1587  KS_RAISE(status);
1588  res += precrushers[i].res;
1589  status = ks_eval_append_two_waveforms(dest_grad, acq_waves[i].waveform, res, acq_res);
1590  KS_RAISE(status);
1591  res += acq_res;
1592 
1593  status = ks_eval_append_two_waveforms(dest_omega, acq_waves[i].waveform, 0, acq_res);
1594  KS_RAISE(status);
1595  ks_waveform_multiplyval(dest_omega, GAM / 10.0, acq_res); /* [Gauss / cm] -> [Hz / mm] */
1596 
1597  status = ks_eval_append_two_waveforms(dest_grad, postcrushers[i].waveform, res, postcrushers[i].res);
1598  KS_RAISE(status);
1599  res += postcrushers[i].res;
1600  status = ks_eval_append_two_waveforms(dest_grad, pad_array, res, res_diff_post);
1601  KS_RAISE(status);
1602  res += res_diff_post;
1603  }
1604 
1605  /* Eval the biggest wave (maxamp) in readwave->grad */
1606  /* copy the waveform */
1607  KS_DESCRIPTION description;
1608  memcpy(readwave->grad.waveform, readwave->grad.p_waveformstates[grad_max.index], sizeof(float) * res);
1609  ks_create_suffixed_description(description, acq_waves[grad_max.index].description, ".grad");
1610 
1611  strcpy(readwave->grad.description, description);
1612  readwave->grad.res = res;
1613  readwave->grad.duration = res * GRAD_UPDATE_TIME;
1614 
1615 
1616  ks_wave_compute_params(&readwave->grad);
1617 
1618 
1619  float sample_area2center;
1620  if (readwave->nover > 0) {
1621  /* nover > 0 results in early echo */
1622  sample_area2center = readwave->nover * ks_calc_fov2gradareapixel(readwave->fov);
1623  } else {
1624  sample_area2center = (readwave->res/2) * ks_calc_fov2gradareapixel(readwave->fov);
1625  }
1626  readwave->acqdelay = max_precrusher_res * GRAD_UPDATE_TIME;
1627  readwave->area2center = precrushers[acq_start_max.index].area + sample_area2center;
1628  readwave->time2center = ks_wave_time2area(&readwave->grad, readwave->area2center);
1629  if (readwave->time2center == KS_NOTSET) {
1630  return ks_error("%s: The read wave can't get to the center!", __FUNCTION__);
1631  }
1632 
1633  ks_create_suffixed_description(description, acq_waves[acq_max.index].description, ".omega");
1634 
1635 
1636  memcpy(readwave->omega.waveform, readwave->omega.p_waveformstates[acq_max.index], sizeof(float) * res);
1637  strcpy(readwave->omega.description, description);
1638  readwave->omega.res = acq_res;
1639  readwave->omega.duration = acq_res * GRAD_UPDATE_TIME;
1640 
1641  ks_wave_compute_params(&readwave->omega);
1642 
1643  readwave->omega.fs_factor = 0.1; /* so 1 bit of iamp = 0.1 mm */
1644 
1645  readwave->acq.duration = acq_duration;
1646  if (readwave->acq.rbw <= 0.0) {
1647  const int sysmintsp = 2;
1648  float dwell = 1.0 / (GAM * acq_waves[acq_max.index].abs_max_amp * 1e-6) * (10.0 / readwave->fov);
1649  int tsp = (int) (dwell);
1650  tsp -= tsp % sysmintsp;
1651  for (; tsp >= sysmintsp; tsp -= sysmintsp) {
1652  if((acq_duration % (2 * tsp)) == 0) {
1653  break; /* duration must be even multiple of tsp */
1654  }
1655  }
1656  if (tsp < sysmintsp) {
1657  return ks_error("%s: readwave amplitude exceeds bw required to sample this fov", __FUNCTION__);
1658  }
1659  readwave->acq.rbw = ks_calc_tsp2bw(tsp);
1660  }
1661  ks_create_suffixed_description(description, acq_waves[0].description, ".readout");
1662  status = ks_eval_read(&readwave->acq, description);
1663  KS_RAISE(status);
1664 
1665  /* Setup the resampler parameters for the read object */
1666  readwave->acq.resampler.ndims = 1;
1667  readwave->acq.resampler.xwave = &readwave->grad;
1668  readwave->acq.resampler.wave_st_idx = readwave->acqdelay / GRAD_UPDATE_TIME;
1669  readwave->acq.resampler.target_res = readwave->nover ? readwave->res/2 + fabs(readwave->nover)
1670  : readwave->res;
1671  readwave->acq.resampler.samples2center = (readwave->time2center - readwave->acqdelay) / readwave->acq.filt.tsp;
1672 
1673  /* Re-negate the negative states */
1674  for (i = 0; i < numstates; i++) {
1675  if (polarity[i] == -1) {
1676  ks_eval_readwave_negatestate(readwave, i);
1677  }
1678  }
1679 
1680  return SUCCESS;
1681 }
float slew
Definition: KSFoundation.h:2372
float fs_factor
Definition: KSFoundation.h:756
float fov
Definition: KSFoundation.h:1610
float ks_calc_tsp2bw(int tsp)
Convert dwell time to receiver bandwidth
Definition: KSFoundation_host.c:6212
void ks_wave_compute_params(KS_WAVE *const wave)
Fills the members of the KS_WAVE sequence object
Definition: KSFoundation_common.c:1123
int wave_st_idx
Definition: KSFoundation.h:779
float ks_calc_fov2gradareapixel(float fov)
Calculates the gradient area needed to move one pixel in k-space
Definition: KSFoundation_common.c:492
KS_WAVEFORM omega_states[KS_READCONTROL_MAXSTATE]
Definition: KSFoundation.h:1619
uint16_t samples2center
Definition: KSFoundation.h:782
#define KS_NOTSET
Definition: KSFoundation.h:115
Definition: KSFoundation.h:2369
Core sequence object making arbitrary waveforms on any board (using float data format)
Definition: KSFoundation.h:743
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:235
#define KS_INIT_WAVE
Definition: KSFoundation.h:295
KS_READ_RESAMPLER resampler
Definition: KSFoundation.h:847
STATUS ks_error(const char *format,...) __attribute__((format(printf
Common error message function for HOST and TGT
int target_res
Definition: KSFoundation.h:781
KS_WAVE omega
Definition: KSFoundation.h:1605
int time2center
Definition: KSFoundation.h:1615
Definition: KSFoundation.h:2363
float rbw
Definition: KSFoundation.h:842
KS_WAVE grad
Definition: KSFoundation.h:1604
float area
Definition: KSFoundation.h:757
int ks_wave_time2area(const KS_WAVE *wave, float area_in)
Calculates the time in [us] that it takes a wave to achieve a certain area
Definition: KSFoundation_common.c:1401
float abs_max_amp
Definition: KSFoundation.h:761
float ampmax
Definition: KSFoundation.h:2371
KS_DESCRIPTION description
Definition: KSFoundation.h:745
LOG_GRAD loggrd
void ks_create_suffixed_description(char *const out, const char *const prefix, const char *suffix,...) __attribute__((format(printf
STATUS ks_eval_append_two_waveforms(KS_WAVEFORM first_waveform, KS_WAVEFORM second_waveform, int res1, int res2)
Definition: KSFoundation_host.c:4473
void ks_eval_readwave_negatestate(KS_READWAVE *readwave, int state)
#### Modify an existing state to its negative (flipped polarity readout)
Definition: KSFoundation_host.c:1686
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:333
ks_enum_crusher_strategy strategy
Definition: KSFoundation.h:2375
int duration
Definition: KSFoundation.h:841
#define KS_RAISE(status)
Definition: KSFoundation.h:190
float ks_syslimits_slewrate(LOG_GRAD loggrd)
Returns the maximum slewrate that can be used on all gradient boards simultaneously
Definition: KSFoundation_common.c:305
STATUS ks_eval_mirrorwave(KS_WAVE *wave)
Flips the contents of the KS_WAVEFORM in a KS_WAVE object
Definition: KSFoundation_host.c:2140
KS_WAVEFORM grad_states[KS_READCONTROL_MAXSTATE]
Definition: KSFoundation.h:1618
void ks_waveform_multiplyval(KS_WAVEFORM waveform, float val, int res)
In-place scalar multiplication of a KS_WAVEFORM
Definition: KSFoundation_common.c:1642
float area
Definition: KSFoundation.h:2370
FILTER_INFO filt
Definition: KSFoundation.h:843
float * p_waveformstates[KS_WAVE_MAXNSTATES]
Definition: KSFoundation.h:749
KS_WAVE * xwave
Definition: KSFoundation.h:775
int acqdelay
Definition: KSFoundation.h:1608
KS_READ acq
Definition: KSFoundation.h:1606
char KS_DESCRIPTION[KS_DESCRIPTION_LENGTH]
Definition: KSFoundation.h:351
int res
Definition: KSFoundation.h:746
int res
Definition: KSFoundation.h:1611
void ks_wave_multiplyval(KS_WAVE *wave, float val)
In-place scalar multiplication of a KS_WAVE
Definition: KSFoundation_common.c:1653
float area2center
Definition: KSFoundation.h:1614
KS_WAVEFORM waveform
Definition: KSFoundation.h:748
int ndims
Definition: KSFoundation.h:778
int duration
Definition: KSFoundation.h:747
#define KS_MAXWAVELEN
Definition: KSFoundation.h:254
STATUS ks_eval_crusher(KS_WAVE *crusher, const float end_amp, const ks_crusher_constraints crusher_constraints)
Sets up a KS_WAVE object that ramps from zero to the specified amplitude and ensures you obtain the m...
Definition: KSFoundation_host.c:964
int nover
Definition: KSFoundation.h:1612

◆ ks_eval_readwave()

STATUS ks_eval_readwave ( KS_READWAVE readwave,
KS_WAVE acq_wave,
float  pre_crusher_area,
float  post_crusher_area,
int  flag_symmetric_padding,
ks_enum_crusher_strategy  pre_strategy,
ks_enum_crusher_strategy  post_strategy 
)

A wrapper of ks_eval_readwave_constrained

Sets up a KS_READWAVE with one state and converts the crusher statergies into constraints

Parameters
[out]readwave- Pointer to an initialised KS_READWAVE object.
[in]acq_wave- Pointer to a KS_WAVE that defines the read gradient shape during the ADC sampling.
[in]pre_crusher_area- desired area before the readout starts
[in]post_crusher_area- desired area after the readout is complete
[in]flag_symmetric_padding- match pre and post areas
[in]pre_strategy- how to optimise the pre crusher waveform shape (fastest, exact area, maximum area)
[in]post_strategy- how to optimise the post crusher waveform shape (fastest, exact area, maximum area)
Return values
STATUSSUCCESS or FAILURE
1725  {
1726 
1727  ks_crusher_constraints pre_crusher_constraints = KS_INIT_CRUSHER_CONSTRAINT;
1728  pre_crusher_constraints.ampmax = ks_syslimits_ampmax(loggrd);
1729  pre_crusher_constraints.slew = ks_syslimits_slewrate(loggrd);
1730  pre_crusher_constraints.min_duration = 0;
1731  pre_crusher_constraints.area = pre_crusher_area;
1732  pre_crusher_constraints.strategy = pre_strategy;
1733  ks_crusher_constraints post_crusher_constraints = pre_crusher_constraints;
1734  post_crusher_constraints.area = post_crusher_area;
1735  post_crusher_constraints.strategy = post_strategy;
1736 
1737  return ks_eval_readwave_constrained(readwave, acq_wave, 1,
1738  flag_symmetric_padding,
1739  pre_crusher_constraints,
1740  post_crusher_constraints);
1741 }
float slew
Definition: KSFoundation.h:2372
int min_duration
Definition: KSFoundation.h:2373
Definition: KSFoundation.h:2369
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:235
#define KS_INIT_CRUSHER_CONSTRAINT
Definition: KSFoundation.h:2378
STATUS ks_eval_readwave_constrained(KS_READWAVE *readwave, KS_WAVE *acq_waves, const int numstates, int flag_symmetric_padding, ks_crusher_constraints pre_crusher_constraints, ks_crusher_constraints post_crusher_constraints)
Sets up an readwave acquisition window with the maximum sampling bandwidth.
Definition: KSFoundation_host.c:1440
float ampmax
Definition: KSFoundation.h:2371
LOG_GRAD loggrd
ks_enum_crusher_strategy strategy
Definition: KSFoundation.h:2375
float ks_syslimits_slewrate(LOG_GRAD loggrd)
Returns the maximum slewrate that can be used on all gradient boards simultaneously
Definition: KSFoundation_common.c:305
float area
Definition: KSFoundation.h:2370

◆ ks_eval_readwave_multistated()

STATUS ks_eval_readwave_multistated ( KS_READWAVE readwave,
KS_WAVE acq_waves,
const int  num_waves,
float  pre_crusher_area,
float  post_crusher_area,
int  flag_symmetric_padding,
ks_enum_crusher_strategy  pre_strategy,
ks_enum_crusher_strategy  post_strategy 
)

A wrapper of ks_eval_readwave_constrained

Converts the crusher statergies into constraints

Parameters
[out]readwave- Pointer to an initialised KS_READWAVE object.
[in]acq_waves- Pointer to a KS_WAVE that defines the read gradient shape during the ADC sampling.
[in]num_waves- Number of waves
[in]pre_crusher_area- desired area before the readout starts
[in]post_crusher_area- desired area after the readout is complete
[in]flag_symmetric_padding- match pre and post areas
[in]pre_strategy- how to optimise the pre crusher waveform shape (fastest, exact area, maximum area)
[in]post_strategy- how to optimise the post crusher waveform shape (fastest, exact area, maximum area)
Return values
STATUSSUCCESS or FAILURE
1704  {
1705  /* TODO: Remove ? */
1706  ks_crusher_constraints pre_crusher_constraints = KS_INIT_CRUSHER_CONSTRAINT;
1707  pre_crusher_constraints.ampmax = ks_syslimits_ampmax(loggrd);
1708  pre_crusher_constraints.slew = ks_syslimits_slewrate(loggrd);
1709  pre_crusher_constraints.min_duration = 0;
1710  pre_crusher_constraints.area = pre_crusher_area;
1711  pre_crusher_constraints.strategy = pre_strategy;
1712  ks_crusher_constraints post_crusher_constraints = pre_crusher_constraints;
1713  post_crusher_constraints.area = post_crusher_area;
1714  post_crusher_constraints.strategy = post_strategy;
1715 
1716  return ks_eval_readwave_constrained(readwave, acq_waves, numstates,
1717  flag_symmetric_padding,
1718  pre_crusher_constraints,
1719  post_crusher_constraints);
1720 }
float slew
Definition: KSFoundation.h:2372
int min_duration
Definition: KSFoundation.h:2373
Definition: KSFoundation.h:2369
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:235
#define KS_INIT_CRUSHER_CONSTRAINT
Definition: KSFoundation.h:2378
STATUS ks_eval_readwave_constrained(KS_READWAVE *readwave, KS_WAVE *acq_waves, const int numstates, int flag_symmetric_padding, ks_crusher_constraints pre_crusher_constraints, ks_crusher_constraints post_crusher_constraints)
Sets up an readwave acquisition window with the maximum sampling bandwidth.
Definition: KSFoundation_host.c:1440
float ampmax
Definition: KSFoundation.h:2371
LOG_GRAD loggrd
ks_enum_crusher_strategy strategy
Definition: KSFoundation.h:2375
float ks_syslimits_slewrate(LOG_GRAD loggrd)
Returns the maximum slewrate that can be used on all gradient boards simultaneously
Definition: KSFoundation_common.c:305
float area
Definition: KSFoundation.h:2370

◆ 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  partial_ky,
int  minkynover 
)
1765  {
1766  int kynover;
1767 
1768  if (partial_ky < 1) {
1769  kynover = yres * (partial_ky - 0.5);
1770  kynover = ((kynover + 1) / 2) * 2; /* round to nearest even number */
1771  if (kynover < minkynover) {
1772  kynover = minkynover; /* protect against too few overscans */
1773  }
1774  kynover = IMin(2, kynover, yres / 2);
1775  } else {
1776  kynover = 0;
1777  }
1778 
1779  return kynover;
1780 
1781 }

◆ 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
1786  {
1787  int i, index, firstline, lastline, halfacslines;
1788  short viewonflag[KS_MAX_PHASEDYN];
1789 
1790  /* reset arrays */
1791  for (i = 0; i < KS_MAX_PHASEDYN; i++) {
1792  phaser->linetoacq[i] = 0;
1793  viewonflag[i] = 0;
1794  }
1795 
1796  if (phaser->nover == 0) {
1797  firstline = 0;
1798  lastline = phaser->res - 1;
1799  } else if (phaser->nover > 0) { /* fractional ky upper half */
1800  firstline = 0;
1801  lastline = phaser->res / 2 + phaser->nover - 1;
1802  } else {
1803  firstline = phaser->res / 2 + phaser->nover; /* NOTE: phase->nover here negative ! */
1804  lastline = phaser->res - 1;
1805  }
1806 
1807 
1808  int acscenter = (phaser->res / 2) + phaser->acsshift;
1809 
1810  /* lower half of k-space: mark the lines to acquire with '1' */
1811  halfacslines = phaser->nacslines / 2;
1812  for (i = acscenter; i <= lastline; i++) {
1813  if (((i + 1) % phaser->R) == 1 || phaser->R == 1) {
1814  viewonflag[i] = 1;
1815  } else if (halfacslines > 0) {
1816  viewonflag[i] = 1;
1817  halfacslines--;
1818  }
1819  }
1820 
1821  /* upper half of k-space: mark the lines to acquire with '1' */
1822  halfacslines = phaser->nacslines - (phaser->nacslines / 2) + halfacslines;
1823  for (i = acscenter - 1; i >= 0; i--) {
1824  if (((i + 1) % phaser->R) == 1 || phaser->R == 1) {
1825  viewonflag[i] = 1;
1826  } else if (halfacslines > 0) {
1827  viewonflag[i] = 1;
1828  halfacslines--;
1829  }
1830  }
1831 
1832  /* sweep kspace linearly and save the line numbers to acquire.
1833  firstline/lastline handles full/partial Fourier */
1834  index = 0;
1835  for (i = firstline; i <= lastline; i++) {
1836  if (viewonflag[i]) {
1837  phaser->linetoacq[index++] = i;
1838  }
1839  }
1840  phaser->numlinestoacq = index;
1841 
1842 }
int R
Definition: KSFoundation.h:1723
int res
Definition: KSFoundation.h:1721
int nacslines
Definition: KSFoundation.h:1724
int numlinestoacq
Definition: KSFoundation.h:1727
int linetoacq[KS_MAX_PHASEDYN]
Definition: KSFoundation.h:1728
int nover
Definition: KSFoundation.h:1722
int acsshift
Definition: KSFoundation.h:1725
#define KS_MAX_PHASEDYN
Definition: KSFoundation.h:263

◆ 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
1847  {
1848 
1849  int kspacelines_noacc;
1850 
1851  if (phaser->nover == 0)
1852  kspacelines_noacc = phaser->res;
1853  else
1854  kspacelines_noacc = phaser->res / 2 + abs(phaser->nover);
1855 
1856  if (kspacelines_noacc < phaser->R) {
1857  /* Number of k-space lines less than R */
1858  return KS_THROW("(%s): R cannot exceed %d", desc, kspacelines_noacc);
1859  } else if (kspacelines_noacc == phaser->R) {
1860  /* Number of k-space lines equal to R. Used for EPI when R is used as shots to produce ETL = 1.
1861  Just make sure that res is even */
1862  return SUCCESS;
1863  }
1864 
1865  if (phaser->nover != 0) {
1866  /* partial Fourier */
1867  const int sign_nover = (phaser->nover >= 0) ? 1 : -1;
1868  phaser->nover = abs(phaser->nover);
1869  int invalidnover = TRUE;
1870 
1871  /* round res and nover to nearest valid values */
1872  if (phaser->R % 2) { /* R is odd */
1873  phaser->res = RUP_FACTOR(phaser->res - 2 * phaser->R, 4 * phaser->R);
1874  phaser->nover = RUP_FACTOR(phaser->nover - phaser->R, 2 * phaser->R);
1875  invalidnover = phaser->nover < (2 * phaser->R) || phaser->nover > (phaser->res / 2);
1876  } else { /* R is even */
1877  phaser->res = RUP_FACTOR(phaser->res - phaser->R, 2 * phaser->R);
1878  phaser->nover = RUP_FACTOR(phaser->nover - phaser->R/2, phaser->R);
1879  invalidnover = phaser->nover < phaser->R || phaser->nover > (phaser->res / 2);
1880  }
1881  phaser->nover *= sign_nover;
1882 
1883  if (invalidnover) {
1884  return KS_THROW("(%s): Valid #overscans not found with res=%d and nover=%d", desc, phaser->res, phaser->nover);
1885  }
1886 
1887  } else {
1888  /* full Fourier */
1889  if (phaser->shotresalign == TRUE) {
1890  int tmp_res = 0;
1891 
1892  tmp_res = RUP_FACTOR( phaser->res - phaser->R/2, phaser->R);
1893 
1894  if (tmp_res % 2 > 0) {
1895  phaser->res = RUP_FACTOR(phaser->res - phaser->R, 2 * phaser->R);
1896  } else {
1897  phaser->res = tmp_res;
1898  }
1899  } else {
1900  phaser->res = RUP_FACTOR(phaser->res, 2);
1901  }
1902 
1903  }
1904 
1905  return SUCCESS;
1906 }
int R
Definition: KSFoundation.h:1723
int res
Definition: KSFoundation.h:1721
int shotresalign
Definition: KSFoundation.h:1729
int nover
Definition: KSFoundation.h:1722
#define KS_THROW(format,...)
Definition: KSFoundation.h:181

◆ 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
1911  {
1912 
1913  if (R <= 1.0) {
1914  phaser->R = 1.0;
1915  phaser->nacslines = 0;
1916  return SUCCESS;
1917  }
1918 
1919  /* Get integer acceleration and store the Rfraction for ARC mode */
1920  double Rfraction = ceil(R) - R;
1921  if (Rfraction > 0.9999) { /* handle roundoff issues for e.g. R=2.0000001 */
1922  Rfraction = 0.0;
1923  }
1924  phaser->R = floor(R + Rfraction + 0.5); /* round-up R to nearest integer */
1925 
1926  if (min_acslines == 0) {
1927  /* ASSET mode */
1928 
1929  /* phaser.nacslines = 0 triggers ASSET scan mode in GEReq_predownload_setrecon_accel() */
1930  phaser->nacslines = 0;
1931 
1932  } else {
1933  /* ARC mode */
1934 
1935  double totlines_spanned = (phaser->nover != 0) ? (phaser->res / 2 + abs(phaser->nover)) : phaser->res;
1936  double acqlines_R = totlines_spanned / phaser->R;
1937  double acqlines_floorR = totlines_spanned / FMax(2, 1.0, floor(R));
1938 
1939  /* adapt #acs lines based on the fraction of the acceleration factor, but never go below min_acslines */
1940  phaser->nacslines = IMax(2, (int) ((acqlines_floorR - acqlines_R) * Rfraction), min_acslines);
1941 
1942  }
1943 
1944  return SUCCESS;
1945 }
int R
Definition: KSFoundation.h:1723
int res
Definition: KSFoundation.h:1721
int nacslines
Definition: KSFoundation.h:1724
int nover
Definition: KSFoundation.h:1722

◆ 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
1953  {
1954 
1955  STATUS status;
1956  float phasepixelarea;
1957 
1958  ks_init_trap(&phaser->grad);
1959 
1960  if (phaser->fov < 0.1 || phaser->fov > 2000.0) {
1961  return ks_error("%s(%s): field 'fov' (%g) must be in the range 0.1-2000 mm", __FUNCTION__, phasername, phaser->fov);
1962  }
1963 
1964  /* adjust res and possibly nover to allow both ARC and ASSET scans with full and partial ky Fourier */
1965  status = ks_eval_phaser_adjustres(phaser, phasername);
1966  KS_RAISE(status);
1967 
1968  phasepixelarea = ks_calc_fov2gradareapixel(phaser->fov); /* [(G/cm)*usec] */
1969 
1970  /* area required to reach the outermost phase encoding step (incl. areaoffset)
1971  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 */
1972  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 */
1973 
1974  /* setup phaser trap */
1975  if (!areSame(phaser->grad.area, 0.0)) {
1976  status = ks_eval_trap_constrained(&phaser->grad, phasername, ampmax, slewrate, minduration);
1977  KS_RAISE(status);
1978  }
1979 
1980  /* setup phaser->linetoacq[] */
1981  ks_eval_phaseviewtable(phaser);
1982 
1983 
1984  return SUCCESS;
1985 
1986 }
int res
Definition: KSFoundation.h:1721
#define areSame(a, b)
Definition: KSFoundation.h:144
KS_TRAP grad
Definition: KSFoundation.h:1719
float ks_calc_fov2gradareapixel(float fov)
Calculates the gradient area needed to move one pixel in k-space
Definition: KSFoundation_common.c:492
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:1847
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:62
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:384
float areaoffset
Definition: KSFoundation.h:1726
float area
Definition: KSFoundation.h:670
#define KS_RAISE(status)
Definition: KSFoundation.h:190
float fov
Definition: KSFoundation.h:1720
void ks_eval_phaseviewtable(KS_PHASER *phaser)
Sets up which lines to acquire for a KS_PHASER object
Definition: KSFoundation_host.c:1786

◆ 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
1993  {
1994 
1996 
1997 }
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:235
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:1952
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:305

◆ 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
2002  {
2003 
2005 
2006 }
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:1952
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:242
float ks_syslimits_slewrate2(LOG_GRAD loggrd)
Returns the maximum slewrate that can be used on two gradient boards simultaneously
Definition: KSFoundation_common.c:320

◆ 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
2011  {
2012 
2014 
2015 }
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:1952
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:335
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:249

◆ 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
2020  {
2021 
2023 
2024 }
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:1952
float ks_syslimits_ampmax1p(LOG_GRAD loggrd)
Returns the maximum gradient amplitude on the physical gradient axes
Definition: KSFoundation_common.c:257
LOG_GRAD loggrd
float ks_syslimits_slewrate1p(LOG_GRAD loggrd)
Returns the maximum gradient amplitude on the physical gradient axes
Definition: KSFoundation_common.c:352

◆ ks_eval_wave()

STATUS ks_eval_wave ( KS_WAVE wave,
const char *const  desc,
const int  res,
const int  duration,
const 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
2029  {
2030  KS_DESCRIPTION tmpdesc;
2031 
2032  strncpy(tmpdesc, desc, KS_DESCRIPTION_LENGTH / 2);
2033  /* don't allow NULL or a description with leading space */
2034  if (desc == NULL || desc[0] == ' ') {
2035  return KS_THROW("wave name (2nd arg) cannot be NULL or leading space");
2036  }
2037 
2038  if (duration < 0 || res < 0) {
2039  return KS_THROW("%s: 'res' and 'duration' must be positive", desc);
2040  }
2041  if (res > KS_MAXWAVELEN) {
2042  return KS_THROW("%s: 'res' cannot exceed %d", wave->description, KS_MAXWAVELEN);
2043  }
2044  if (duration > 0 && (duration % res)) {
2045  return KS_THROW("%s: 'duration' (%d) [us] must be divisible by 'res' (%d)", desc, duration, res);
2046  }
2047  if (duration > 0 && (duration / res) < 2) {
2048  return KS_THROW("%s: 'duration' (%d) [us] must be at least 2x 'res' (%d)", desc, duration, res);
2049  }
2050  if ((waveform == wave->waveform) || (waveform == NULL) ) {
2051  return KS_THROW("%s: cannot eval a wave with its own or NULL waveform", desc);
2052  }
2053 
2054  /* reset wave object */
2055  ks_init_wave(wave);
2056 
2057  /* copy the waveform */
2058  memcpy(wave->waveform, waveform, sizeof(float) * res);
2059 
2060  /* fill in wave object */
2061  strcpy(wave->description, tmpdesc);
2062  wave->res = res;
2063  wave->duration = duration;
2064 
2065  ks_wave_compute_params(wave);
2066 
2067  return SUCCESS;
2068 
2069 }
void ks_wave_compute_params(KS_WAVE *const wave)
Fills the members of the KS_WAVE sequence object
Definition: KSFoundation_common.c:1123
#define KS_DESCRIPTION_LENGTH
Definition: KSFoundation.h:253
KS_DESCRIPTION description
Definition: KSFoundation.h:745
void ks_init_wave(KS_WAVE *wave)
Resets a KS_WAVE sequence object to its default value (KS_INIT_WAVE)
Definition: KSFoundation_host.c:78
char KS_DESCRIPTION[KS_DESCRIPTION_LENGTH]
Definition: KSFoundation.h:351
#define KS_THROW(format,...)
Definition: KSFoundation.h:181
int res
Definition: KSFoundation.h:746
KS_WAVEFORM waveform
Definition: KSFoundation.h:748
int duration
Definition: KSFoundation.h:747
#define KS_MAXWAVELEN
Definition: KSFoundation.h:254

◆ 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) See also ks_eval_stretch_rf().

Parameters
[in,out]waveKS_WAVE object, whose KS_WAVEFORM to be mirrored
Return values
STATUSSUCCESS or FAILURE
2140  {
2141  KS_WAVEFORM tmpwave;
2142  int i;
2143 
2144  memcpy(tmpwave, wave->waveform, sizeof(float) * wave->res);
2145 
2146  for (i = 0; i < wave->res; i++) {
2147  wave->waveform[i] = tmpwave[wave->res-1 - i];
2148  }
2149  return SUCCESS;
2150 }
float KS_WAVEFORM[KS_MAXWAVELEN]
Definition: KSFoundation.h:352
int res
Definition: KSFoundation.h:746
KS_WAVEFORM waveform
Definition: KSFoundation.h:748

◆ ks_eval_mirrorwaveform()

STATUS ks_eval_mirrorwaveform ( KS_WAVEFORM  waveform,
int  res 
)

Mirror in the res (time) domain a KS_WAVEFORM object

Parameters
[in]waveformpointer to an array of floats
[in]resthe number of points in the wave shape you want to mirrow
Return values
STATUSSUCCESS or FAILURE
2155  {
2156  KS_WAVEFORM tmpwave;
2157  int i;
2158 
2159  memcpy(tmpwave, waveform, sizeof(float) * res);
2160 
2161  for (i = 0; i < res; i++) {
2162  waveform[i] = tmpwave[res - 1 - i];
2163  }
2164  return SUCCESS;
2165 }
float KS_WAVEFORM[KS_MAXWAVELEN]
Definition: KSFoundation.h:352

◆ 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
2074  {
2075  KS_DESCRIPTION tmpdesc;
2076  int i;
2077 
2078  strncpy(tmpdesc, desc, KS_DESCRIPTION_LENGTH / 2);
2079  /* don't allow empty description or a description with leading space */
2080  if (desc == NULL || desc[0] == ' ') {
2081  return ks_error("ks_eval_wave_file: wave name (2nd arg) cannot be NULL or leading space");
2082  }
2083  if (res % 2) {
2084  return ks_error("ks_eval_wave_file(%s): 'res' must be even", wave->description);
2085  }
2086  if (duration > 0 && (duration % res)) {
2087  return ks_error("ks_eval_wave_file(%s): 'duration' (%d) [us] must be divisible by 'res' (%d)", desc, duration, res);
2088  }
2089  if (duration > 0 && (duration / res) < 2) {
2090  return ks_error("ks_eval_wave_file(%s): 'duration' (%d) [us] must be at least 2x 'res' (%d)", desc, duration, res);
2091  }
2092  if (duration < 0 || duration > 4 * KS_MAXWAVELEN) {
2093  return ks_error("ks_eval_wave_file(%s): 'duration' [us] must be in the range [0, %d]", desc, 4 * KS_MAXWAVELEN);
2094  }
2095 
2096  /* reset wave object */
2097  ks_init_wave(wave);
2098 
2099  /* fill in wave object */
2100  strcpy(wave->description, tmpdesc);
2101  wave->res = res;
2102  wave->duration = duration;
2103 
2104  /* read from file */
2105  if (!strncasecmp(format, "ge", 2)) { /* short format with 32-byte header and big-endian short int */
2106  KS_IWAVE iwave;
2107  uextwave(iwave, res, (char *) filename);
2108  for (i = 0; i < res; i++)
2109  wave->waveform[i] = (float) iwave[i] / (float) MAX_PG_WAMP;
2110  } else if (!strncasecmp(format, "short", 5)) {
2111  KS_IWAVE iwave;
2112  FILE *fp;
2113  int n;
2114  if ((fp = fopen(filename, "r")) == NULL)
2115  return ks_error("ks_eval_wave_file (%s): Error opening file '%s'", wave->description, filename);
2116  if ((n = fread(iwave, sizeof(short), res, fp)) < res)
2117  return ks_error("ks_eval_wave_file (%s): Only %d out of %d elements read from file '%s'", wave->description, n, res, filename);
2118  fclose(fp);
2119  for (i = 0; i < res; i++)
2120  wave->waveform[i] = (float) iwave[i] / (float) MAX_PG_WAMP;
2121  } else if (!strncasecmp(format, "float", 5)) {
2122  FILE *fp;
2123  int n;
2124  if ((fp = fopen(filename, "r")) == NULL)
2125  return ks_error("ks_eval_wave_file (%s): Error opening file '%s'", wave->description, filename);
2126  if ((n = fread(wave->waveform, sizeof(float), res, fp)) < res)
2127  return ks_error("ks_eval_wave_file (%s): Only %d out of %d elements read from file '%s'", wave->description, n, res, filename);
2128  fclose(fp);
2129  } else {
2130  return ks_error("ks_eval_wave_file (%s): 'format' (5th arg) must be 'ge','short' or 'float'", wave->description);
2131  }
2132 
2133 
2134  return SUCCESS;
2135 
2136 } /* ks_eval_wave_file */
short KS_IWAVE[KS_MAXWAVELEN]
Definition: KSFoundation.h:353
STATUS ks_error(const char *format,...) __attribute__((format(printf
Common error message function for HOST and TGT
#define KS_DESCRIPTION_LENGTH
Definition: KSFoundation.h:253
KS_DESCRIPTION description
Definition: KSFoundation.h:745
void ks_init_wave(KS_WAVE *wave)
Resets a KS_WAVE sequence object to its default value (KS_INIT_WAVE)
Definition: KSFoundation_host.c:78
char KS_DESCRIPTION[KS_DESCRIPTION_LENGTH]
Definition: KSFoundation.h:351
int res
Definition: KSFoundation.h:746
KS_WAVEFORM waveform
Definition: KSFoundation.h:748
int duration
Definition: KSFoundation.h:747
#define KS_MAXWAVELEN
Definition: KSFoundation.h:254

◆ 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
2176  {
2177  KS_DESCRIPTION tmpdesc;
2178  int i;
2179  double x, window, rfenvelope;
2180  int duration = RUP_GRD(tbw / bw * 1e6); /* [us]. We round up to nearest multiple of 4 (GRAD_UPDATE_TIME) to better align to gradients */
2181  int res = duration / RF_UPDATE_TIME; /* # sample points. RF_UPDATE_TIME = 2 => res always even */
2182 
2183  /* don't allow empty description or a description with leading space */
2184  if (desc == NULL || desc[0] == ' ') {
2185  return KS_THROW("RF name (2nd arg) cannot be NULL or leading space");
2186  }
2187  strncpy(tmpdesc, desc, KS_DESCRIPTION_LENGTH/2);
2188  tmpdesc[KS_DESCRIPTION_LENGTH/2] = 0;
2189 
2190  /* reset RF object */
2191  ks_init_rf(rf);
2192 
2193  /* fill in wave object */
2194  ks_create_suffixed_description(rf->rfwave.description, tmpdesc, ".wave");
2195 
2196  if (bw < 2 || bw > 100000) {
2197  return KS_THROW("%s: 'bw' (%g, 3rd arg) must be in range [2,100000] Hz", rf->rfwave.description, bw);
2198  }
2199  if (tbw < 2 || tbw > 100) {
2200  return KS_THROW("%s: 'tbw' (%g, 4th arg) must be in range [2,100]", rf->rfwave.description, tbw);
2201  }
2202  if (res > KS_MAXWAVELEN || res < 4) {
2203  return KS_THROW("%s: combination of 'bw' and 'tbw' resulted in a resolution outside the valid range [4, %d]", rf->rfwave.description, KS_MAXWAVELEN);
2204  }
2205  if (flip <= 0 || flip > 10000.0) {
2206  return KS_THROW("%s: 'flip' must be in range (0, 10000]", rf->rfwave.description);
2207  }
2208 
2209  rf->rfwave.res = res;
2210  rf->rfwave.duration = duration;
2211  rf->iso2end = duration / 2;
2212  rf->start2iso = duration / 2;
2213  rf->bw = bw;
2214  rf->flip = flip;
2215 
2216  /* generate waveform with max 1.0 */
2217  for (i = 0 ; i < (res - 1) ; i++) {
2218  x = 2.0 * i / (res - 1) - 1.0;
2219 
2220  if (fabs(2.0 * PI * (tbw / 4.0)*x) > 1e-6) {
2221  rfenvelope = sin(2.0 * PI * (tbw / 4.0) * x) / (2.0 * PI * (tbw / 4.0) * x);
2222  } else {
2223  rfenvelope = 1.0;
2224  }
2225 
2226  window = 1.0; /* init to no filter */
2227 
2228  if (wintype == KS_RF_SINCWIN_HAMMING) {
2229  window = 0.54 + 0.46 * cos(PI * x);
2230  } else if (wintype == KS_RF_SINCWIN_HANNING) {
2231  window = (1.0 + cos(PI * x)) * 0.5;
2232  } else if (wintype == KS_RF_SINCWIN_BLACKMAN) {
2233  window = 0.42 + 0.5 * cos(PI * x) + 0.08 * cos(2 * PI * x);
2234  } else if (wintype == KS_RF_SINCWIN_BARTLETT) {
2235  if (x < 0.0)
2236  window = x + 1.0;
2237  else
2238  window = 1.0 - x;
2239  }
2240 
2241  rf->rfwave.waveform[i] = rfenvelope * window;
2242 
2243  } /* for */
2244 
2245  /* initialize amplitude to 1 */
2246  rf->amp = 1;
2247 
2248  /* set the usage of the RF PULSE to off until .base.ninst > 0 (see ks_pg_rf() on HOST) */
2249  rf->rfpulse.activity = PSD_PULSE_OFF;
2250 
2251  /* initialize number of occurrences of RF pulse to zero */
2252  rf->rfpulse.num = 0;
2253 
2254  /* setup rfpulse structure */
2255  return ks_eval_rfstat(rf);
2256 }
int start2iso
Definition: KSFoundation.h:1032
Definition: KSFoundation.h:2341
RF_PULSE rfpulse
Definition: KSFoundation.h:1036
float flip
Definition: KSFoundation.h:1028
#define KS_DESCRIPTION_LENGTH
Definition: KSFoundation.h:253
void ks_init_rf(KS_RF *rf)
Resets a KS_RF sequence object to its default value (KS_INIT_RF)
Definition: KSFoundation_host.c:86
KS_DESCRIPTION description
Definition: KSFoundation.h:745
STATUS ks_eval_rfstat(KS_RF *rf)
Sets up the RF_PULSE structure of a KS_RF object
Definition: KSFoundation_host.c:2712
void ks_create_suffixed_description(char *const out, const char *const prefix, const char *suffix,...) __attribute__((format(printf
KS_WAVE rfwave
Definition: KSFoundation.h:1037
Definition: KSFoundation.h:2341
float amp
Definition: KSFoundation.h:1031
Definition: KSFoundation.h:2341
int iso2end
Definition: KSFoundation.h:1033
char KS_DESCRIPTION[KS_DESCRIPTION_LENGTH]
Definition: KSFoundation.h:351
#define KS_THROW(format,...)
Definition: KSFoundation.h:181
int res
Definition: KSFoundation.h:746
Definition: KSFoundation.h:2341
KS_WAVEFORM waveform
Definition: KSFoundation.h:748
int duration
Definition: KSFoundation.h:747
#define KS_MAXWAVELEN
Definition: KSFoundation.h:254
float bw
Definition: KSFoundation.h:1029

◆ 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

2261  {
2262  STATUS status;
2263  int debug = 1;
2264 
2265  /* reset RF object */
2266  ks_init_rf(rf);
2267 
2268  KS_DESCRIPTION tmpdesc;
2269  int i;
2270 
2271  /* don't allow empty description or a description with leading space */
2272  if (desc == NULL || desc[0] == ' ') {
2273  return ks_error("ks_eval_rf_secant: 'RF name' (2nd arg) cannot be NULL or leading space");
2274  }
2275  strncpy(tmpdesc, desc, KS_DESCRIPTION_LENGTH/2);
2276  tmpdesc[KS_DESCRIPTION_LENGTH/2] = 0;
2277 
2278  /* fill in wave object */
2279  ks_create_suffixed_description(rf->rfwave.description, tmpdesc, ".wave");
2280 
2281  if (A0 < 0 || 0.25 < A0) {
2282  return KS_THROW("%s: 'A0' (%g, 3rd arg) must be in range [0,0.25] Gauss", rf->rfwave.description, A0);
2283  }
2284  if (tbw < 2 || 20 < tbw) {
2285  return KS_THROW("%s: 'tbw' (%g, 4th arg) must be in range [2,20]", rf->rfwave.description, tbw);
2286  }
2287  if (bw < 100 || 10000 < bw) {
2288  return KS_THROW("%s: 'bw' (%g, 5th arg) must be in range [0,10000] Hz", rf->rfwave.description, bw);
2289  }
2290 
2291  int duration = RUP_GRD((int)(1000000.0 * tbw / bw));
2292  int res = duration / GRAD_UPDATE_TIME; /* # sample points. RF_UPDATE_TIME = 2 => res always even */
2293 
2294  if (res < 4 || KS_MAXWAVELEN < res) {
2295  return KS_THROW("%s: tbw / bw resulted in a resolution(%d)/duration(%d) outside the valid range [4, %d]", rf->rfwave.description, res, duration, KS_MAXWAVELEN);
2296  }
2297 
2298  rf->rfwave.res = res;
2299  rf->rfwave.duration = duration;
2300  rf->iso2end = duration / 2;
2301  rf->start2iso = duration / 2;
2302 
2303  /* calculate pulse properties */
2304  float y = 5; /* 5 is a standard value from literature */
2305  float B = (bw * PI) / y;
2306  float Acheck = (PI * bw) / (2.0 * PI * 4258 * sqrt(y)); /* Gauss */
2307 
2308  if (Acheck > A0 * 0.5) {
2309  return KS_THROW("%s: The adiabatic condition might not be met since %g !>> %g (needs to be at least half)", rf->rfwave.description, A0, Acheck);
2310  }
2311 
2312  rf->bw = bw;
2313  rf->flip = 180.0;
2314 
2315  double rfenvelope, phaseenvelope, tau, sech;
2316  for (i = 0 ; i < res; i++) {
2317 
2318  /* range from -2pi to 2pi */
2319  tau = ((i / (((float)res - 1.0) * 0.5)) - 1.0) * 2.0 * PI;
2320 
2321  sech = 1.0 / cosh(tau * B * 0.001);
2322  rfenvelope = A0 * sech;
2323  phaseenvelope = y * log(sech) + y * log(A0);
2324 
2325  rf->rfwave.waveform[i] = rfenvelope;
2326  rf->thetawave.waveform[i] = phaseenvelope * (180.0/PI);
2327 
2328  }
2329 
2330  if (debug == 1) {
2331  ks_print_waveform(rf->rfwave.waveform, "secant_a.txt", rf->rfwave.res);
2332  ks_print_waveform(rf->thetawave.waveform, "secant_p.txt", rf->rfwave.res);
2333  ks_error("HS: A0=%f >> Ac=%f, bw=%f, tbw=%f", A0, Acheck, bw, tbw);
2334  ks_error("HS: y=%f, B=%f", y, B);
2335  }
2336 
2337  /* Normalize waveform to one & initialize amplitude to 1 */
2338  ks_wave_multiplyval(&rf->rfwave, 1.0 / ks_wave_absmax(&rf->rfwave));
2339  rf->amp = 1;
2340 
2341  /* set the usage of the RF PULSE to off until .base.ninst > 0 (see ks_pg_rf() on HOST) */
2342  rf->rfpulse.activity = PSD_PULSE_OFF;
2343 
2344  /* initialize number of occurrences of RF pulse to zero */
2345  rf->rfpulse.num = 0;
2346 
2347  /* setup rfpulse structure */
2348  status = ks_eval_rfstat(rf);
2349  KS_RAISE(status);
2350 
2351  rf->rfpulse.max_b1 = A0;
2352  rf->thetawave.res = res;
2353  rf->thetawave.duration = duration;
2354  rf->role = KS_RF_ROLE_INV;
2355  return status;
2356 }
int start2iso
Definition: KSFoundation.h:1032
int role
Definition: KSFoundation.h:1027
int debug
Definition: GERequired.e:3728
RF_PULSE rfpulse
Definition: KSFoundation.h:1036
STATUS ks_error(const char *format,...) __attribute__((format(printf
Common error message function for HOST and TGT
Definition: KSFoundation.h:2340
float flip
Definition: KSFoundation.h:1028
float ks_wave_absmax(const KS_WAVE *wave)
Returns the maximum absolute value in a KS_WAVE sequence object
Definition: KSFoundation_common.c:1359
#define KS_DESCRIPTION_LENGTH
Definition: KSFoundation.h:253
KS_WAVE thetawave
Definition: KSFoundation.h:1039
void ks_init_rf(KS_RF *rf)
Resets a KS_RF sequence object to its default value (KS_INIT_RF)
Definition: KSFoundation_host.c:86
KS_DESCRIPTION description
Definition: KSFoundation.h:745
STATUS ks_eval_rfstat(KS_RF *rf)
Sets up the RF_PULSE structure of a KS_RF object
Definition: KSFoundation_host.c:2712
void ks_create_suffixed_description(char *const out, const char *const prefix, const char *suffix,...) __attribute__((format(printf
KS_WAVE rfwave
Definition: KSFoundation.h:1037
float amp
Definition: KSFoundation.h:1031
#define KS_RAISE(status)
Definition: KSFoundation.h:190
int iso2end
Definition: KSFoundation.h:1033
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:6680
char KS_DESCRIPTION[KS_DESCRIPTION_LENGTH]
Definition: KSFoundation.h:351
#define KS_THROW(format,...)
Definition: KSFoundation.h:181
int res
Definition: KSFoundation.h:746
void ks_wave_multiplyval(KS_WAVE *wave, float val)
In-place scalar multiplication of a KS_WAVE
Definition: KSFoundation_common.c:1653
KS_WAVEFORM waveform
Definition: KSFoundation.h:748
int duration
Definition: KSFoundation.h:747
#define KS_MAXWAVELEN
Definition: KSFoundation.h:254
float bw
Definition: KSFoundation.h:1029

◆ 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
2361  {
2362  KS_DESCRIPTION tmpdesc;
2363  int i;
2364 
2365  /* don't allow empty description or a description with leading space */
2366  if (desc == NULL || desc[0] == ' ') {
2367  return KS_THROW("RF name (2nd arg) cannot be NULL or leading space");
2368  }
2369  strncpy(tmpdesc, desc, KS_DESCRIPTION_LENGTH/2);
2370  tmpdesc[KS_DESCRIPTION_LENGTH/2] = 0;
2371 
2372  /* reset RF object */
2373  ks_init_rf(rf);
2374 
2375  /* fill in wave object */
2376  ks_create_suffixed_description(rf->rfwave.description, tmpdesc, ".wave");
2377 
2378  if (duration > KS_MAXWAVELEN * RF_UPDATE_TIME || duration < 4) {
2379  return KS_THROW("%s: 'duration' (3rd arg) must be in range [4, %d]", rf->rfwave.description, KS_MAXWAVELEN * RF_UPDATE_TIME);
2380  }
2381  if (duration % 2) {
2382  return KS_THROW("%s: 'duration' (3rd arg) must be divisible by 2", rf->rfwave.description);
2383  }
2384 
2385  rf->rfwave.res = duration / RF_UPDATE_TIME;
2386  rf->rfwave.duration = duration;
2387  rf->iso2end = duration / 2;
2388  rf->start2iso = duration / 2;
2389  rf->bw = 0;
2390  rf->flip = flip;
2391 
2392  /* generate waveform with max 1.0 */
2393  for (i = 0 ; i < rf->rfwave.res ; i++) {
2394  rf->rfwave.waveform[i] = 1.0;
2395  }
2396 
2397  /* initialize amplitude to 1 */
2398  rf->amp = 1;
2399 
2400  /* set the usage of the RF PULSE to off until .base.ninst > 0 (see ks_pg_rf() on HOST) */
2401  rf->rfpulse.activity = PSD_PULSE_OFF;
2402 
2403  /* initialize number of occurrences of RF pulse to zero */
2404  rf->rfpulse.num = 0;
2405 
2406  /* setup rfpulse structure */
2407  STATUS status = ks_eval_rfstat(rf);
2408  KS_RAISE(status);
2409 
2410  return SUCCESS;
2411 }
int start2iso
Definition: KSFoundation.h:1032
RF_PULSE rfpulse
Definition: KSFoundation.h:1036
float flip
Definition: KSFoundation.h:1028
#define KS_DESCRIPTION_LENGTH
Definition: KSFoundation.h:253
void ks_init_rf(KS_RF *rf)
Resets a KS_RF sequence object to its default value (KS_INIT_RF)
Definition: KSFoundation_host.c:86
KS_DESCRIPTION description
Definition: KSFoundation.h:745
STATUS ks_eval_rfstat(KS_RF *rf)
Sets up the RF_PULSE structure of a KS_RF object
Definition: KSFoundation_host.c:2712
void ks_create_suffixed_description(char *const out, const char *const prefix, const char *suffix,...) __attribute__((format(printf
KS_WAVE rfwave
Definition: KSFoundation.h:1037
float amp
Definition: KSFoundation.h:1031
#define KS_RAISE(status)
Definition: KSFoundation.h:190
int iso2end
Definition: KSFoundation.h:1033
char KS_DESCRIPTION[KS_DESCRIPTION_LENGTH]
Definition: KSFoundation.h:351
#define KS_THROW(format,...)
Definition: KSFoundation.h:181
int res
Definition: KSFoundation.h:746
KS_WAVEFORM waveform
Definition: KSFoundation.h:748
int duration
Definition: KSFoundation.h:747
#define KS_MAXWAVELEN
Definition: KSFoundation.h:254
float bw
Definition: KSFoundation.h:1029

◆ 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
2416  {
2417 
2418  if (order < 1) {
2419  return KS_THROW("%s: 'order' (%d, 3rd arg) must be positive", desc, order);
2420  }
2421 
2422  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);
2423 
2424  return ks_eval_rf_hard(rf, desc, duration, flip);
2425 
2426 }
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:2361
#define KS_THROW(format,...)
Definition: KSFoundation.h:181

◆ ks_eval_rf_binomial()

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

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
[in]MTreduction[0=OFF,1=ON] reduces offresonace excitation and therefore MT effects
Return values
STATUSSUCCESS or FAILURE
2431  {
2432  KS_DESCRIPTION tmpdesc;
2433  int i, j;
2434 
2435  /* Don't allow empty description or a description with leading space */
2436  if (desc == NULL || desc[0] == ' ') {
2437  return KS_THROW("RF name (2nd arg) cannot be NULL or leading space");
2438  }
2439  strncpy(tmpdesc, desc, KS_DESCRIPTION_LENGTH/2);
2440  tmpdesc[KS_DESCRIPTION_LENGTH/2] = 0;
2441 
2442  /* Reset RF object */
2443  ks_init_rf(rf);
2444 
2445  /* Fill in wave object */
2446  ks_create_suffixed_description(rf->rfwave.description, tmpdesc, ".wave");
2447  sprintf(rf->designinfo, "%s", tmpdesc);
2448 
2449  if (offResExc < 0 || offResExc > 1) {
2450  return KS_THROW("%s: 'offResExc' (3rd arg) must be either 0 or 1", rf->rfwave.description);
2451  }
2452  if (nPulses < 2 || nPulses > 10) {
2453  return KS_THROW("%s: 'nPulses' (4th arg) must be in range [2, 10]", rf->rfwave.description);
2454  }
2455 
2456  int pascalstriangle[9][10] = { {1,1},
2457  {1,2,1},
2458  {1,3,3,1},
2459  {1,4,6,4,1},
2460  {1,5,10,10,5,1},
2461  {1,6,15,20,15,6,1},
2462  {1,7,21,35,35,21,7,1},
2463  {1,8,28,56,70,56,28,8,1},
2464  {1,9,36,84,126,126,84,36,9,1} };
2465 
2466  int P[10];
2467  int pulseSum = 0;
2468  for (i = 0; i < nPulses; i++) {
2469  P[i] = pascalstriangle[nPulses - 2][i];
2470  pulseSum += P[i];
2471  }
2472 
2473  /* Calculate subpulse separation */
2474  float tau = fabs(1.0 / (2.0 * offsetFreq)); /* sec */
2475 
2476  /* Calculate number of samples so that each subpulse is as close as possible to maxB1, without exceeding it */
2477  float maxB1 = 0.20; /* Gauss */
2478  float pulseArea = (float) flip * (PI / 180.0); /* radians or Hz (depending on how you look at it) */
2479  float dt = RF_UPDATE_TIME / 1000000.0; /* sec */
2480  float subArea[10];
2481  int ntps[10];
2482  for (i = 0; i < nPulses; i++) {
2483  subArea[i] = (P[i] * pulseArea) / (pulseSum * 2.0 * PI * GAM); /* Gauss * sec */
2484  if (MTreduction) {
2485  ntps[i] = ceil(tau / dt);
2486  } else {
2487  ntps[i] = ceil(subArea[i] / (maxB1 * dt)); /* number of time points in sub pulse */
2488  if (ntps[i] < 32 ) {
2489  ntps[i] = 32;
2490  }
2491  }
2492  }
2493 
2494  int ntp = 0;
2495  for (i = 0; i < nPulses; i++) {
2496  if (ntps[i]>ntp) {
2497  ntp = ntps[i];
2498  }
2499  }
2500 
2501  int idx = 0;
2502  for (i = 0; i < nPulses; i++) {
2503 
2504  /* Calculate how much we have to attenuate this pulse
2505  to account for the fact that with this dwell time
2506  we couldn't hit the target flip exactly */
2507  float currentSubArea = ntp * dt * maxB1; /* Gauss * sec */
2508  float amp = maxB1 * (subArea[i] / currentSubArea); /* Gauss */
2509 
2510  /* Flip every other sub pulse for off resonace excitaion */
2511  if (offResExc) {
2512  /* make sure the second center sub-pulse flips in the right direction */
2513  amp *= pow(-1.0, (float)(i + nPulses/2 % 2));
2514  }
2515 
2516  /* Stitch on the sub pulse */
2517  for (j = 0; j < ntp; j++) {
2518  rf->rfwave.waveform[idx] = amp;
2519  idx++;
2520  }
2521 
2522  /* Stitch on the gap, skip for last sub pulse */
2523  if (i < nPulses - 1) {
2524  int ntp_gap = ceil((tau/dt) - (float)ntp);
2525  if (MTreduction) {
2526  ntp_gap = 0;
2527  }
2528  for (j = 0; j < ntp_gap; j++) {
2529  rf->rfwave.waveform[idx] = 0.0;
2530  idx++;
2531  }
2532  }
2533  }
2534 
2535  /* Make sure the resolution is divisible by 2 and the duration divisible by 4 */
2536  rf->rfwave.res = RUP_FACTOR(idx, 4);
2537  rf->rfwave.duration = rf->rfwave.res * RF_UPDATE_TIME;
2538  rf->iso2end = rf->rfwave.duration / 2;
2539  rf->start2iso = rf->rfwave.duration / 2;
2540  rf->bw = 0; /* ? */
2541  rf->flip = flip;
2542 
2543  /* If the resolution has been rounded up, place zeros at the end */
2544  for (j = idx; j < rf->rfwave.res; j++) {
2545  rf->rfwave.waveform[j] = 0.0;
2546  }
2547 
2548  /* Normalize waveform to one */
2549  ks_wave_multiplyval(&rf->rfwave, 1.0 / ks_wave_absmax(&rf->rfwave));
2550 
2551  /* Initialize amplitude to one */
2552  rf->amp = 1;
2553 
2554  /* Set the usage of the RF PULSE to off until .base.ninst > 0 (see ks_pg_rf() on HOST) */
2555  rf->rfpulse.activity = PSD_PULSE_OFF;
2556 
2557  /* Never allow binomial RF pulses to be stretched. We can avoid this by always setting extgradfile = 1 (even if this is not the case) */
2558  rf->rfpulse.extgradfile = 1;
2559 
2560  /* Initialize number of occurrences of RF pulse to zero */
2561  rf->rfpulse.num = 0;
2562 
2563  /* Save original waveform to fool rfstat, with abs(waveform) */
2564  float *saveWave = (float*)alloca(rf->rfwave.res * sizeof(float));
2565  if (offResExc) {
2566  memcpy(saveWave, rf->rfwave.waveform, rf->rfwave.res * sizeof(float));
2567  for (j = 0; j < rf->rfwave.res; j++) {
2568  rf->rfwave.waveform[j] = fabs(rf->rfwave.waveform[j]);
2569  }
2570  }
2571 
2572  /* Setup rfpulse structure */
2573  STATUS status = ks_eval_rfstat(rf);
2574  KS_RAISE(status);
2575 
2576  /* Put back the correct waveform */
2577  if (offResExc) {
2578  memcpy(rf->rfwave.waveform, saveWave, rf->rfwave.res * sizeof(float));
2579  }
2580 
2581  return SUCCESS;
2582 
2583 }
int start2iso
Definition: KSFoundation.h:1032
KS_DESCRIPTION designinfo
Definition: KSFoundation.h:1035
RF_PULSE rfpulse
Definition: KSFoundation.h:1036
float flip
Definition: KSFoundation.h:1028
float ks_wave_absmax(const KS_WAVE *wave)
Returns the maximum absolute value in a KS_WAVE sequence object
Definition: KSFoundation_common.c:1359
#define KS_DESCRIPTION_LENGTH
Definition: KSFoundation.h:253
void ks_init_rf(KS_RF *rf)
Resets a KS_RF sequence object to its default value (KS_INIT_RF)
Definition: KSFoundation_host.c:86
KS_DESCRIPTION description
Definition: KSFoundation.h:745
STATUS ks_eval_rfstat(KS_RF *rf)
Sets up the RF_PULSE structure of a KS_RF object
Definition: KSFoundation_host.c:2712
void ks_create_suffixed_description(char *const out, const char *const prefix, const char *suffix,...) __attribute__((format(printf
KS_WAVE rfwave
Definition: KSFoundation.h:1037
float amp
Definition: KSFoundation.h:1031
#define KS_RAISE(status)
Definition: KSFoundation.h:190
int iso2end
Definition: KSFoundation.h:1033
float maxB1[MAX_ENTRY_POINTS]
Definition: GERequired.e:303
char KS_DESCRIPTION[KS_DESCRIPTION_LENGTH]
Definition: KSFoundation.h:351
#define KS_THROW(format,...)
Definition: KSFoundation.h:181
int res
Definition: KSFoundation.h:746
void ks_wave_multiplyval(KS_WAVE *wave, float val)
In-place scalar multiplication of a KS_WAVE
Definition: KSFoundation_common.c:1653
KS_WAVEFORM waveform
Definition: KSFoundation.h:748
int duration
Definition: KSFoundation.h:747
float bw
Definition: KSFoundation.h:1029

◆ ks_eval_mt_rf_binomial()

STATUS ks_eval_mt_rf_binomial ( KS_RF rf,
const char *const  desc,
int  sub_pulse_dur,
int  nPulses,
int  prp 
)

Sets up a KS_RF object as a Binomial MT RF pulse

The major features of binomial-pulse-train frequency spectra are as follows: a series of sinc functions at frequency intervals of 1/PRP, whose width is inversely related to the overall pulse train duration (nPulses-1)*PRP, modulated by the single-binomial-pulse spectrum. Hence, the width of the sinc functions, or “spikes” in the frequency spectrum becomes narrower as the number of pulses is increased, and they become closer together as the pulse repetition period is increased.

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]sub_pulse_dur
[in]nPulses
[in]prp
Return values
STATUSSUCCESS or FAILURE
2588  {
2589  KS_DESCRIPTION tmpdesc;
2590  int i, j, k;
2591 
2592  if (prp < sub_pulse_dur) {
2593  return ks_error("ks_eval_mt_rf_binomial: PRP(=%d) must be bigger than sub_pulse_dur(=%d)", prp, sub_pulse_dur);
2594  }
2595 
2596  double offsetFreq = 421.5420;
2597  if (cffield != 30000) {
2598  offsetFreq *= 0.5;
2599  }
2600 
2601  /* constraining the pulse repetition period to be greater or less than 1/(2 x chemical shift). */
2602  double nogo = 1.0/(2.0*offsetFreq)*1000000.0;
2603  if (areSameRelative(prp, nogo, 0.01)) {
2604  return ks_error("ks_eval_mt_rf_binomial: PRP(=%d) is too similar to %f (would cause artifacts)", prp, nogo);
2605  }
2606 
2607  /* Don't allow empty description or a description with leading space */
2608  if (desc == NULL || desc[0] == ' ') {
2609  return ks_error("ks_eval_mt_rf_binomial: RF name (2nd arg) cannot be NULL or leading space");
2610  }
2611  strncpy(tmpdesc, desc, KS_DESCRIPTION_LENGTH/2);
2612  tmpdesc[KS_DESCRIPTION_LENGTH/2] = 0;
2613 
2614  /* Reset RF object */
2615  ks_init_rf(rf);
2616 
2617  /* Fill in wave object */
2618  sprintf(rf->rfwave.description, "%s.wave", tmpdesc);
2619  sprintf(rf->designinfo, "%s", tmpdesc);
2620 
2621  double dt = 2e-6; /* sec */
2622  float pulseArea = (float) 90.0 * 4.0 * (PI / 180.0); /* radians or Hz (depending on how you look at it) */
2623  double subArea = (pulseArea) / (4.0 * 2.0 * PI * GAM); /* Gauss * sec */
2624  int ntps = ceil(sub_pulse_dur/4/RF_UPDATE_TIME); /* number of time points in sub pulse */
2625  float maxB1 = subArea/(ntps * dt); /* Gauss */
2626 
2627  /* Calculate how much we have to attenuate this pulse
2628  to account for the fact that with this dwell time
2629  we couldn't hit the target flip exactly */
2630  double currentSubArea = ntps * dt * maxB1; /* Gauss * sec */
2631  float amp = maxB1 * (subArea / currentSubArea); /* Gauss */
2632 
2633  int ntp_gap = ceil((prp/RF_UPDATE_TIME) - (float)ntps);
2634 
2635  int idx = 0, sub_dir = 1, dir = 1;
2636  for (i = 0; i < nPulses; i++) {
2637 
2638  dir = pow(-1.0, (float)(i + nPulses/2 % 2));
2639 
2640  for (k = 0; k < 4; k++) {
2641 
2642  if (k == 1 || k == 2) {
2643  sub_dir = -1;
2644  } else {
2645  sub_dir = 1;
2646  }
2647 
2648  /* Stitch on the sub pulse */
2649  for (j = 0; j < ntps; j++) {
2650  rf->rfwave.waveform[idx] = amp * sub_dir * dir;
2651  idx++;
2652  }
2653 
2654  }
2655  if ((nPulses > 1) && (i != nPulses-1)) {
2656  /* Stitch on the pause */
2657  for (j = 0; j < ntp_gap; j++) {
2658  rf->rfwave.waveform[idx] = 0.0;
2659  idx++;
2660  }
2661  }
2662  }
2663 
2664  /* Make sure the resolution is divisible by 2 and the duration divisible by 4 */
2665  rf->rfwave.res = RUP_FACTOR(idx, 4);
2666  rf->rfwave.duration = rf->rfwave.res * RF_UPDATE_TIME;
2667  rf->iso2end = rf->rfwave.duration / 2;
2668  rf->start2iso = rf->rfwave.duration / 2;
2669  rf->bw = 0; /* ? */
2670  rf->flip = 90 + 180 + 90;
2671 
2672  /* If the resolution has been rounded up, place zeros at the end */
2673  for (j = idx; j < rf->rfwave.res; j++) {
2674  rf->rfwave.waveform[j] = 0.0;
2675  }
2676 
2677  /* Normalize waveform to one */
2678  ks_wave_multiplyval(&rf->rfwave, 1.0 / ks_wave_absmax(&rf->rfwave));
2679 
2680  /* Initialize amplitude to one */
2681  rf->amp = 1;
2682 
2683  /* Set the usage of the RF PULSE to off until .base.ninst > 0 (see ks_pg_rf() on HOST) */
2684  rf->rfpulse.activity = PSD_PULSE_OFF;
2685 
2686  /* Never allow binomial RF pulses to be stretched. We can avoid this by always setting extgradfile = 1 (even if this is not the case) */
2687  rf->rfpulse.extgradfile = 1;
2688 
2689  /* Initialize number of occurrences of RF pulse to zero */
2690  rf->rfpulse.num = 0;
2691 
2692  /* Save original waveform to fool rfstat, with abs(waveform) */
2693  float *saveWave = (float*)alloca(rf->rfwave.res * sizeof(float));
2694  memcpy(saveWave, rf->rfwave.waveform, rf->rfwave.res * sizeof(float));
2695  for (j = 0; j < rf->rfwave.res; j++) {
2696  rf->rfwave.waveform[j] = fabs(rf->rfwave.waveform[j]);
2697  }
2698 
2699  /* Setup rfpulse structure */
2700  STATUS status = ks_eval_rfstat(rf);
2701  if (status != SUCCESS) return status;
2702 
2703  /* Put back the correct waveform */
2704  memcpy(rf->rfwave.waveform, saveWave, rf->rfwave.res * sizeof(float));
2705 
2706  return SUCCESS;
2707 }
int start2iso
Definition: KSFoundation.h:1032
KS_DESCRIPTION designinfo
Definition: KSFoundation.h:1035
RF_PULSE rfpulse
Definition: KSFoundation.h:1036
STATUS ks_error(const char *format,...) __attribute__((format(printf
Common error message function for HOST and TGT
float flip
Definition: KSFoundation.h:1028
float ks_wave_absmax(const KS_WAVE *wave)
Returns the maximum absolute value in a KS_WAVE sequence object
Definition: KSFoundation_common.c:1359
#define KS_DESCRIPTION_LENGTH
Definition: KSFoundation.h:253
void ks_init_rf(KS_RF *rf)
Resets a KS_RF sequence object to its default value (KS_INIT_RF)
Definition: KSFoundation_host.c:86
#define areSameRelative(a, b, r)
Definition: KSFoundation.h:159
KS_DESCRIPTION description
Definition: KSFoundation.h:745
STATUS ks_eval_rfstat(KS_RF *rf)
Sets up the RF_PULSE structure of a KS_RF object
Definition: KSFoundation_host.c:2712
KS_WAVE rfwave
Definition: KSFoundation.h:1037
float amp
Definition: KSFoundation.h:1031
int iso2end
Definition: KSFoundation.h:1033
float maxB1[MAX_ENTRY_POINTS]
Definition: GERequired.e:303
char KS_DESCRIPTION[KS_DESCRIPTION_LENGTH]
Definition: KSFoundation.h:351
int res
Definition: KSFoundation.h:746
void ks_wave_multiplyval(KS_WAVE *wave, float val)
In-place scalar multiplication of a KS_WAVE
Definition: KSFoundation_common.c:1653
KS_WAVEFORM waveform
Definition: KSFoundation.h:748
int duration
Definition: KSFoundation.h:747
float bw
Definition: KSFoundation.h:1029

◆ 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
2712  {
2713  double standard_pw = 1e-3;
2714 
2715  double nRFenvelope;
2716  int indx;
2717 
2718  double area = 0.0;
2719  double abswidth = 0.0;
2720  double effwidth = 0.0;
2721  double dtycyc = 0.0;
2722  double max_pw = 0.0;
2723  double max_b1 = 0.0;
2724  double max_int_b1_sq = 0.0;
2725  double max_rms_b1 = 0.0;
2726 
2727 
2728  if (rf->thetawave.res > 0 || rf->omegawave.res > 0) {
2729  return ks_error("ks_eval_rfstat(%s): function is not validated for phase/frequency modulated RF-pulses", rf->rfwave.description);
2730  }
2731  /* require fields 'duration', 'isodelay' and 'bw' to be set to realistic values */
2732  if (rf->rfwave.duration < 4 || rf->rfwave.duration > KS_MAXWAVELEN * RF_UPDATE_TIME || rf->rfwave.duration % 2) {
2733  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);
2734  }
2735  if (rf->bw < 0 || rf->bw > 100000) {
2736  return ks_error("ks_eval_rfstat: 'rf.bw' (%f) must be in the range [0,100000] Hz", rf->bw);
2737  }
2738  if (rf->iso2end < 0 || rf->iso2end > KS_MAXWAVELEN * RF_UPDATE_TIME) {
2739  return ks_error("ks_eval_rfstat: 'rf.iso2end' (%d) must be in the range [0,%d] us", rf->iso2end, KS_MAXWAVELEN * RF_UPDATE_TIME);
2740  }
2741  if (rf->flip < 0.0 || rf->flip > 10000.0) {
2742  return ks_error("ks_eval_rfstat: 'rf.flip' (%.2f) must be in the range [0,3000] deg", rf->flip);
2743  }
2744  /* for reference, see: /ESE_xxxxx/psd/include/private/sar_pm.h */
2745 
2746  /* normalize RF just in case */
2747  ks_wave_multiplyval(&rf->rfwave, 1.0 / ks_wave_absmax(&rf->rfwave));
2748 
2749 
2750  for (indx = 0; indx < rf->rfwave.res; indx++) {
2751  nRFenvelope = rf->rfwave.waveform[indx];
2752  area += nRFenvelope;
2753  abswidth += fabs(nRFenvelope);
2754  effwidth += (nRFenvelope * nRFenvelope);
2755 
2756  /* dtycyc: % of Pulse Widths above 5% power */
2757  if (fabs(nRFenvelope) > 0.05)
2758  dtycyc++;
2759  }
2760 
2761  area /= rf->rfwave.res;
2762  abswidth /= rf->rfwave.res;
2763  effwidth /= rf->rfwave.res;
2764  dtycyc /= rf->rfwave.res;
2765 
2766  /* max_pw: % of the pulse width whose widest lobe is above 5% power
2767  GE uses max_pw = dtycyc for most pulses (sar_pm.h)
2768  Neither max_pw nor dtycyc seem to match well with the values used in GEs product sequences unlike
2769  the other fields, which seems to agree within a few percent */
2770  max_pw = dtycyc;
2771 
2772  max_b1 = rf->flip / 360.0 / (area * (rf->rfwave.duration) * 1e-6) / GAM;
2773  max_int_b1_sq = max_b1 * max_b1 * effwidth * (rf->rfwave.duration) * 1e-6 / standard_pw;
2774  max_rms_b1 = sqrt(max_int_b1_sq / ((rf->rfwave.duration) * 1e-6) * standard_pw);
2775 
2776 
2777  ks_eval_rf_relink(rf);
2778 
2779  rf->rfpulse.abswidth = abswidth;
2780  rf->rfpulse.effwidth = effwidth;
2781  rf->rfpulse.area = area;
2782  rf->rfpulse.dtycyc = dtycyc;
2783  rf->rfpulse.maxpw = max_pw;
2784  rf->rfpulse.num = 0;
2785  rf->rfpulse.max_b1 = max_b1;
2786  rf->rfpulse.max_int_b1_sq = max_int_b1_sq;
2787  rf->rfpulse.max_rms_b1 = max_rms_b1;
2788  rf->rfpulse.nom_fa = rf->flip; /* nom_fa and act_fa are equal */
2789  rf->rfpulse.nom_pw = rf->rfwave.duration;
2790  rf->rfpulse.nom_bw = rf->bw;
2791  rf->rfpulse.activity = PSD_PULSE_OFF; /* is set to PSD_SCAN_ON by ks_pg_rf() */
2792  rf->rfpulse.reference = 0;
2793  rf->rfpulse.isodelay = rf->iso2end;
2794  rf->rfpulse.scale = 1.0;
2795  if (rf->rfpulse.extgradfile != 1) {
2796  rf->rfpulse.extgradfile = 0; /* Only allow 0 or 1. Disallows RF pulse stretching if 1 */
2797  }
2798 
2799  return SUCCESS;
2800 }
RF_PULSE rfpulse
Definition: KSFoundation.h:1036
STATUS ks_error(const char *format,...) __attribute__((format(printf
Common error message function for HOST and TGT
float flip
Definition: KSFoundation.h:1028
float ks_wave_absmax(const KS_WAVE *wave)
Returns the maximum absolute value in a KS_WAVE sequence object
Definition: KSFoundation_common.c:1359
KS_WAVE thetawave
Definition: KSFoundation.h:1039
KS_DESCRIPTION description
Definition: KSFoundation.h:745
KS_WAVE rfwave
Definition: KSFoundation.h:1037
int iso2end
Definition: KSFoundation.h:1033
void ks_eval_rf_relink(KS_RF *rf)
(Internal use) Relinks pointers between fields in a KS_RF object
Definition: KSFoundation_host.c:2869
KS_WAVE omegawave
Definition: KSFoundation.h:1038
int res
Definition: KSFoundation.h:746
void ks_wave_multiplyval(KS_WAVE *wave, float val)
In-place scalar multiplication of a KS_WAVE
Definition: KSFoundation_common.c:1653
KS_WAVEFORM waveform
Definition: KSFoundation.h:748
int duration
Definition: KSFoundation.h:747
#define KS_MAXWAVELEN
Definition: KSFoundation.h:254
float bw
Definition: KSFoundation.h:1029

◆ 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
2805  {
2806 
2807  if (desc == NULL || desc[0] == ' ') {
2808  return ks_error("ks_eval_rf: description (2nd arg) cannot be NULL or begin with a space");
2809  } else {
2810  strncpy(rf->rfwave.description, desc, KS_DESCRIPTION_LENGTH / 2); /* truncate after half max # chars to allow for suffixes */
2811  }
2812 
2813  ks_eval_rf_relink(rf);
2814 
2815  if (rf->role <= KS_RF_ROLE_NOTSET || rf->role > KS_RF_ROLE_INV) {
2816  return ks_error("ks_eval_rf(%s): 'rf.role' is not set or is invalid", desc);
2817  }
2818  if (rf->flip <= 0.0 || rf->flip > 10000.0) {
2819  return ks_error("ks_eval_rf(%s): 'rf.flip' (%.2f) must be in the range (0,3000] deg", desc, rf->flip);
2820  }
2821  if (rf->bw < 0 || rf->bw > 100000) {
2822  return ks_error("ks_eval_rf(%s): 'rf.bw' (%.2f) must be in the range [0,100000]", desc, rf->bw);
2823  }
2824  /* check that RF duration is even */
2825  if (rf->rfwave.duration % 2)
2826  return ks_error("ks_eval_rf (%s): RF duration (%d) [us] must be even", rf->rfwave.description, rf->rfwave.duration);
2827 
2828  /* check that RF duration and optional THETA / OMEGA waveforms have same duration */
2829  if (rf->omegawave.res) {
2830  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 */
2831  if (rf->rfwave.duration != rf->omegawave.duration)
2832  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);
2833  }
2834  if (rf->thetawave.res) {
2835  sprintf(rf->thetawave.description, "%s", rf->rfwave.description);
2836  if (rf->rfwave.duration != rf->thetawave.duration)
2837  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);
2838  }
2839 
2840  if (rf->iso2end < 0 || rf->iso2end > rf->rfwave.duration) {
2841  return ks_error("ks_eval_rf (%s): 'rf.iso2end (%d) must in range [0,%d] [us]", rf->rfwave.description, rf->iso2end, rf->rfwave.duration);
2842  }
2843 
2844  /* check that .iso2end is equal to .rfpulse.isodelay */
2845  if (rf->rfpulse.isodelay != rf->iso2end) {
2846  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);
2847  }
2848 
2849  rf->rfpulse.isodelay = RUP_GRD(rf->rfpulse.isodelay); /* Round up using RUP_GRD() to fall on gradient raster (GRAD_UPDATE_TIME = 4us) */
2850  rf->iso2end = rf->rfpulse.isodelay;
2851  rf->start2iso = rf->rfwave.duration - rf->iso2end; /* time from start of RF pulse to its magnetic center */
2852 
2853  /* initialize amplitude to 1 */
2854  rf->amp = 1;
2855 
2856  /* set the usage of the RF PULSE to off until .base.ninst > 0 (see ks_pg_rf() on HOST) */
2857  rf->rfpulse.activity = PSD_PULSE_OFF;
2858 
2859  /* initialize number of occurrences of RF pulse to zero */
2860  rf->rfpulse.num = 0;
2861 
2862 
2863  return SUCCESS;
2864 
2865 } /* ks_eval_rf */
int start2iso
Definition: KSFoundation.h:1032
int role
Definition: KSFoundation.h:1027
RF_PULSE rfpulse
Definition: KSFoundation.h:1036
STATUS ks_error(const char *format,...) __attribute__((format(printf
Common error message function for HOST and TGT
Definition: KSFoundation.h:2340
float flip
Definition: KSFoundation.h:1028
#define KS_DESCRIPTION_LENGTH
Definition: KSFoundation.h:253
KS_WAVE thetawave
Definition: KSFoundation.h:1039
KS_DESCRIPTION description
Definition: KSFoundation.h:745
KS_WAVE rfwave
Definition: KSFoundation.h:1037
Definition: KSFoundation.h:2340
float amp
Definition: KSFoundation.h:1031
int iso2end
Definition: KSFoundation.h:1033
void ks_eval_rf_relink(KS_RF *rf)
(Internal use) Relinks pointers between fields in a KS_RF object
Definition: KSFoundation_host.c:2869
KS_WAVE omegawave
Definition: KSFoundation.h:1038
int res
Definition: KSFoundation.h:746
int duration
Definition: KSFoundation.h:747
float bw
Definition: KSFoundation.h:1029

◆ 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
2869  {
2870 
2871  /* link as required by RF_PULSE */
2872  rf->rfpulse.pw = &(rf->rfwave.duration);
2873  rf->rfpulse.amp = &(rf->amp);
2874  rf->rfpulse.act_fa = &(rf->flip);
2875  rf->rfpulse.res = &(rf->rfwave.res);
2876  rf->rfpulse.exciter = &ks_rhoboard;
2877 }
int ks_rhoboard
Definition: KSFoundation_common.c:42
RF_PULSE rfpulse
Definition: KSFoundation.h:1036
float flip
Definition: KSFoundation.h:1028
KS_WAVE rfwave
Definition: KSFoundation.h:1037
float amp
Definition: KSFoundation.h:1031
int res
Definition: KSFoundation.h:746
int duration
Definition: KSFoundation.h:747

◆ 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
3928  {
3929 
3930  STATUS status;
3931  int idx;
3932  int debug = 0;
3933  int mirror_rfpulse = 0;
3934 
3935  if (areSame(stretch_factor, 1.0)) {
3936  return SUCCESS;
3937  }
3938 
3939  mirror_rfpulse = (stretch_factor < 0); /* we are going to mirror the RF pulse after stretching */
3940  stretch_factor = fabsf(stretch_factor);
3941 
3942  if (stretch_factor < FLT_EPSILON) {
3943  return ks_error("%s(%s): can not stretch by %f", __FUNCTION__, rf->rfwave.description, stretch_factor);
3944  }
3945 
3946  /* get original duration/resolution and calculate new duration/resolution */
3947  int orgDur = rf->rfwave.duration;
3948  int newDur = RUP_GRD((int)(orgDur * stretch_factor));
3949  int orgRes = rf->rfwave.res;
3950  int newRes = newDur/RF_UPDATE_TIME;
3951 
3952  if (newRes > KS_MAXWAVELEN) {
3953  return ks_error("%s(%s): stretch => too high res (%d)", __FUNCTION__, rf->rfwave.description, newRes);
3954  }
3955 
3956  /* create index for interpolation */
3957  float *orgGrid = (float*)alloca(orgRes * sizeof(float));
3958  float *newGrid = (float*)alloca(newRes * sizeof(float));
3959  for (idx = 0; idx < orgRes; idx++) {
3960  orgGrid[idx] = (float) idx / (orgRes-1);
3961  }
3962  for (idx = 0; idx < newRes; idx++) {
3963  newGrid[idx] = (float) idx / (newRes-1);
3964  }
3965 
3966  if (debug == 1) {ks_print_waveform(rf->rfwave.waveform, "stretch_orgrf.txt", orgRes); }
3967 
3968  /* perform linear interpolation */
3969  ks_eval_linear_interp1(orgGrid, orgRes, rf->rfwave.waveform, newGrid, newRes, rf->rfwave.waveform);
3970 
3971  if (debug == 1) {ks_print_waveform(rf->rfwave.waveform, "stretch_newrf.txt", newRes); }
3972 
3973  /* set new duration, resolution and bandwidth */
3974  rf->rfwave.duration = newDur;
3975  rf->rfwave.res = newRes;
3976  rf->bw *= ((float)orgDur/newDur);
3977  rf->rfpulse.isodelay = RUP_FACTOR((int)(rf->rfpulse.isodelay * stretch_factor),RF_UPDATE_TIME);
3978  rf->iso2end = rf->rfpulse.isodelay;
3979  rf->start2iso = rf->rfwave.duration - rf->iso2end;
3980 
3981  if (mirror_rfpulse) {
3982  rf->iso2end = rf->start2iso; /* N.B.: start2iso will be updated again in ks_eval_rf() */
3983  rf->rfpulse.isodelay = rf->start2iso;
3984  ks_eval_mirrorwave(&rf->rfwave);
3987  }
3988 
3989  char description[KS_DESCRIPTION_LENGTH];
3990  ks_create_suffixed_description(description, rf->rfwave.description, "_stretched");
3991  status = ks_eval_rf(rf, description);
3992  KS_RAISE(status);
3993 
3994  status = ks_eval_rfstat(rf);
3995  KS_RAISE(status);
3996 
3997  return SUCCESS;
3998 }
int start2iso
Definition: KSFoundation.h:1032
#define areSame(a, b)
Definition: KSFoundation.h:144
int debug
Definition: GERequired.e:3728
RF_PULSE rfpulse
Definition: KSFoundation.h:1036
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:4278
#define KS_DESCRIPTION_LENGTH
Definition: KSFoundation.h:253
KS_WAVE thetawave
Definition: KSFoundation.h:1039
KS_DESCRIPTION description
Definition: KSFoundation.h:745
STATUS ks_eval_rfstat(KS_RF *rf)
Sets up the RF_PULSE structure of a KS_RF object
Definition: KSFoundation_host.c:2712
void ks_create_suffixed_description(char *const out, const char *const prefix, const char *suffix,...) __attribute__((format(printf
KS_WAVE rfwave
Definition: KSFoundation.h:1037
#define KS_RAISE(status)
Definition: KSFoundation.h:190
STATUS ks_eval_mirrorwave(KS_WAVE *wave)
Flips the contents of the KS_WAVEFORM in a KS_WAVE object
Definition: KSFoundation_host.c:2140
int iso2end
Definition: KSFoundation.h:1033
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:6680
STATUS ks_eval_rf(KS_RF *rf, const char *const desc)
Sets up a KS_RF object
Definition: KSFoundation_host.c:2805
KS_WAVE omegawave
Definition: KSFoundation.h:1038
int res
Definition: KSFoundation.h:746
KS_WAVEFORM waveform
Definition: KSFoundation.h:748
int duration
Definition: KSFoundation.h:747
#define KS_MAXWAVELEN
Definition: KSFoundation.h:254
float bw
Definition: KSFoundation.h:1029

◆ 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
2888  {
2889 
2890  /* Reset all fields and waveforms */
2891  ks_init_trap(trap);
2892 
2893  if ((rfduration < GRAD_UPDATE_TIME) || (rfduration % GRAD_UPDATE_TIME)) {
2894  return ks_error("%s: RF duration must be positive and divisible by 4", __FUNCTION__);
2895  }
2896 
2897  if (slthick < 0 || bw < 0) {
2898  /* N.B.: slthick may be exactly 0, in which case a zero amp gradient is created */
2899  return ks_error("%s: slice thickness & bandwidth cannot be negative", __FUNCTION__);
2900  }
2901 
2902  if (desc == NULL || desc[0] == ' ') {
2903  return ks_error("ks_eval_seltrap: trap name (2nd arg) cannot be NULL or begin with a space");
2904  }
2905 
2906  strncpy(trap->description, desc, KS_DESCRIPTION_LENGTH - 1);
2907 
2908  trap->amp = ks_calc_selgradamp(bw, slthick); /* amp [G/cm] */
2909 
2910  trap->ramptime = RUP_GRD((int) (trap->amp / slewrate)); /* ramp time [us] */
2911 
2912  if (trap->ramptime <= 0) {
2913  trap->ramptime = 4;
2914  }
2915 
2916  trap->plateautime = rfduration; /* plateau time [us] */
2917  trap->duration = trap->plateautime + 2 * trap->ramptime; /* total duration [us] */
2918  trap->area = (trap->plateautime + trap->ramptime) * trap->amp;
2919 
2920 
2921  return SUCCESS;
2922 
2923 }
int plateautime
Definition: KSFoundation.h:672
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:62
#define KS_DESCRIPTION_LENGTH
Definition: KSFoundation.h:253
float area
Definition: KSFoundation.h:670
float amp
Definition: KSFoundation.h:669
KS_DESCRIPTION description
Definition: KSFoundation.h:668
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:448
int duration
Definition: KSFoundation.h:673
int ramptime
Definition: KSFoundation.h:671

◆ 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
2967  {
2968  char selname[KS_DESCRIPTION_LENGTH], preselname[KS_DESCRIPTION_LENGTH], postselname[KS_DESCRIPTION_LENGTH];
2969  float minthick;
2970  STATUS status;
2971 
2972  if (desc == NULL || desc[0] == ' ') {
2973  return ks_error("%s: description (2nd arg) cannot be NULL or begin with a space", __FUNCTION__);
2974  }
2975 
2976  /* setup the RF pulse */
2977  status = ks_eval_rf(&selrf->rf, desc);
2978  KS_RAISE(status);
2979 
2980  /* with slice selection (trap or gradwave), the RF duration must be divisible by GRAD_UPDATE_TIME (4) [us] */
2981  if ((selrf->rf.rfwave.duration < 4) || (selrf->rf.rfwave.duration % 4)) {
2982  return ks_error("%s (%s): RF duration (%d) must be divisible by GRAD_UPDATE_TIME (4)", __FUNCTION__, desc, selrf->rf.rfwave.duration);
2983  }
2984 
2985  /* init members of selrf to their default state */
2986  ks_init_trap(&selrf->pregrad);
2987  ks_init_trap(&selrf->grad);
2988  ks_init_trap(&selrf->postgrad);
2989  ks_init_sms_info(&selrf->sms_info);
2990 
2991  /* Slice thickness */
2992  /* protect against e.g. +/-Inf thicknesses. Note that we do *NOT* complain about slthick = 0. This is OK and means zero amp dummy gradient */
2993  if (selrf->slthick < 0 || selrf->slthick > 600) {
2994  return ks_error("%s (%s): Thickness [%f] not in range [0,600] mm", __FUNCTION__, desc, selrf->slthick);
2995  }
2996 
2997  minthick = selrf->rf.bw / (ampmax * (0.1) * GAM);
2998  if (selrf->slthick > 0 && selrf->slthick < minthick) {
2999  /* N.B.: checking for slthick > 0 above, allows slthick = 0 without leaading to this error (meaning no slice sel amp) */
3000  return ks_error("%s (%s): Minimum thickness violation (desired - %.2f [mm] min - %.2f [mm])", __FUNCTION__, desc, selrf->slthick, minthick);
3001  }
3002 
3003  /* If bridged crushers are wanted, add a constant gradwave as slice slelection */
3004  if (selrf->rf.role == KS_RF_ROLE_REF && selrf->bridge_crushers && selrf->gradwave.res == 0) {
3005  ks_init_wave(&selrf->gradwave);
3006  ks_eval_selwave(&selrf->gradwave, selname, selrf->slthick, selrf->rf.bw, selrf->rf.rfwave.duration);
3007  KS_RAISE(status);
3008  }
3009 
3010  /* if we have a gradwave generated, check that it has the same duration as the rfpulse */
3011  if (selrf->gradwave.res) {
3012  if (selrf->gradwave.duration != selrf->rf.rfwave.duration) {
3013  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);
3014  }
3015  if (selrf->gradwave.duration % selrf->gradwave.res) {
3016  return ks_error("%s (%s): Custom gradient wave duration (%d) must be divisible by res (%d)", __FUNCTION__, desc, selrf->gradwave.duration, selrf->gradwave.res);
3017  }
3019  }
3020 
3021  /* Setup the gradients (trapezoid case) */
3022  ks_create_suffixed_description(selname, selrf->rf.rfwave.description, "_trap"); /* suffix '.x' is added automatically in ks_pg_wave(), where 'x' is the 1st letter of the board , ".trap"*/
3023 
3024 
3025  /* slice select */
3026  if (selrf->gradwave.res == 0) {
3027  /* 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
3028  This is facilitated by ks_eval_seltrap()->ks_calc_selgradamp() */
3029  status = ks_eval_seltrap(&selrf->grad, selname, slewrate, selrf->slthick, selrf->rf.bw, selrf->rf.rfwave.duration);
3030  KS_RAISE(status);
3031  } else {
3033  /* slice select using existing custom waveform (.gradwave) - scaling gradwave amplitude correctly regardless of current amplitude
3034  assumes that the maximum absolute value in gradwave.waveform corresponds to the gradient amplitude intended for the current rf BW and slthick */
3035  float maxval = ks_wave_max(&selrf->gradwave); /* not absmax, but max, to allow for larger negative lobes for e.g. flyback SPSP */
3036  if (maxval <= 0) {
3037  /* a gradwave that is strictly below zero, normalize instead by absmax */
3038  maxval = fabs(ks_wave_min(&selrf->gradwave));
3039  }
3040  ks_wave_multiplyval(&selrf->gradwave, ks_calc_selgradamp(selrf->rf.bw, selrf->slthick) / maxval);
3041 
3042  } else if (selrf->gradwave.gradwave_units == KS_NOTSET) {
3043  return ks_error("%s (%s): Custom gradient wave scaling policy not set", __FUNCTION__, desc);
3044  }
3045 
3047  }
3048 
3049 
3050  switch (selrf->rf.role) {
3051 
3052  /********************************************************************************/
3053  /*********************** RF SEL EXCITATION **************************************/
3054  /********************************************************************************/
3055  case KS_RF_ROLE_EXC: {
3056 
3057  int gradwave_ramp_duration = 0;
3058 
3059  ks_create_suffixed_description(postselname, selrf->rf.rfwave.description, ".reph");
3060  if (selrf->gradwave.res == 0) { /* Slice selection using .grad (KS_TRAP) */
3061 
3062  /* area needed for rephaser */
3063  selrf->postgrad.area = -(selrf->rf.rfpulse.isodelay + selrf->grad.ramptime / 2) * selrf->grad.amp;
3064 
3065  } else { /* Slice selection using .gradwave (KS_WAVE) */
3066 
3067  int i, isostart;
3068  int grad_dwell = selrf->gradwave.duration / selrf->gradwave.res;
3069 
3070  /* if gradwave starts at non-zeoro, add ramps */
3071  if (selrf->gradwave.waveform[0] > 0) {
3072 
3073  if (areSame(selrf->gradwave.waveform[selrf->gradwave.res - 1], 0)) {
3074  return ks_error("%s: exc-gradwave starts at non-zero and ends at zero",__FUNCTION__);
3075  }
3076 
3077  KS_WAVE ramp;
3078  status = ks_eval_ramp(&ramp, slewrate*0.99, selrf->gradwave.waveform[0], grad_dwell);
3079  KS_RAISE(status);
3080 
3081  /* Append ramp to slice selective gradwave (including some mirror gymnastics) */
3082  status = ks_eval_mirrorwave(&ramp);
3083  KS_RAISE(status);
3084 
3085  status = ks_eval_mirrorwave(&selrf->gradwave);
3086  KS_RAISE(status);
3087 
3088  status = ks_eval_append_two_waves(&selrf->gradwave, &ramp);
3089  KS_RAISE(status);
3090 
3091  status = ks_eval_mirrorwave(&selrf->gradwave);
3092  KS_RAISE(status);
3093 
3094  status = ks_eval_append_two_waves(&selrf->gradwave, &ramp);
3095  KS_RAISE(status);
3096 
3097  /* calculate usefull params */
3099  gradwave_ramp_duration = ramp.duration;
3100  }
3101 
3102  /* rephaser area needed for gradient wave */
3103  selrf->postgrad.area = 0;
3104  if (selrf->rf.iso2end_subpulse != KS_NOTSET) {
3105  /* SPSP's subpulses may be linear, min or max phase pulses, not neccesarily
3106  centered on the plateau of each lobe. For these pulses, the iso2end_subpulse field
3107  is not KS_NOTSET (-1). One can also have self-rephased gradwaves, not needing
3108  a .postgrad. In this case rf.iso2end_subpulse = 0 would disable this, while keeping
3109  rf.iso2end for TE calculations */
3110  isostart = (selrf->gradwave.duration - selrf->rf.iso2end_subpulse) / grad_dwell;
3111  } else {
3112  isostart = (selrf->gradwave.duration - selrf->rf.iso2end) / grad_dwell;
3113  }
3114  isostart -= gradwave_ramp_duration / grad_dwell;
3115  for (i = isostart; i < selrf->gradwave.res; i++) {
3116  selrf->postgrad.area -= selrf->gradwave.waveform[i] * grad_dwell;
3117  }
3118  }
3119 
3120  /* add off-set area */
3121  selrf->postgrad.area += selrf->postgrad_area_offset;
3122 
3123  /* setup rephaser trapezoid */
3124  status = ks_eval_trap_constrained(&selrf->postgrad, postselname, ampmax, slewrate, 0);
3125  KS_RAISE(status);
3126 
3127  selrf->grad2rf_start = selrf->grad.ramptime + gradwave_ramp_duration;
3128  selrf->rf2grad_end = selrf->postgrad.duration + selrf->grad.ramptime + gradwave_ramp_duration;
3129 
3130  break;
3131  }
3132  /********************************************************************************/
3133  /*********************** RF SEL REFOCUSING **************************************/
3134  /********************************************************************************/
3135  case KS_RF_ROLE_REF: {
3136  float grad_area = (selrf->gradwave.res==0) ? selrf->grad.area : selrf->gradwave.area;
3137 
3138  /* area [(G/cm)*us], GAM [Hz/G], slthick [mm] */
3139  const float slthick = selrf->slthick > 0 ? selrf->slthick : 10; /* dafault to 10mm if not selective */
3140  float crusher_area;
3141  if (areSame(selrf->crusher_dephasing, -1.0)) { /* flowcomp z negative crushers using slicesel area */
3142  crusher_area = -grad_area; /* negative flowcomp z crushers */
3143  } else {
3144  crusher_area = 1e7 * selrf->crusher_dephasing / (GAM * slthick);
3145  crusher_area -= grad_area / 2.0;
3146  if (crusher_area < 0.0) {crusher_area = 0.0;}
3147  }
3148 
3149  float pregrad_area = crusher_area + selrf->pregrad_area_offset;
3150  float postgrad_area = crusher_area + selrf->postgrad_area_offset;
3151 
3152 
3153  if (selrf->gradwave.res==0) {
3154  /* KS_TRAP crushers (non-bridged) */
3155  selrf->pregrad.area = pregrad_area;
3156  selrf->postgrad.area = postgrad_area;
3157 
3158  ks_create_suffixed_description(preselname, selrf->rf.rfwave.description, ".LC");
3159  ks_create_suffixed_description(postselname, selrf->rf.rfwave.description, ".RC");
3160 
3161  status = ks_eval_trap_constrained(&selrf->pregrad, preselname, ampmax, slewrate, 0);
3162  KS_RAISE(status);
3163  status = ks_eval_trap_constrained(&selrf->postgrad, postselname, ampmax, slewrate, 0);
3164  KS_RAISE(status);
3165 
3166  selrf->grad2rf_start = selrf->pregrad.duration + selrf->grad.ramptime;
3167  selrf->rf2grad_end = selrf->postgrad.duration + selrf->grad.ramptime;
3168 
3169  } else {
3170  /* KS_WAVE crushers (bridged) */
3171  ks_crusher_constraints crusher_constraints = KS_INIT_CRUSHER_CONSTRAINT;
3172  crusher_constraints.ampmax = ampmax;
3173  crusher_constraints.slew = slewrate;
3174  crusher_constraints.min_duration = 0;
3175  crusher_constraints.strategy = KS_CRUSHER_STRATEGY_EXACT;
3176  KS_WAVE precrusher;
3177  KS_WAVE postcrusher;
3178 
3179  const float start_amp = 1.5f * selrf->gradwave.waveform[0] - 0.5f * selrf->gradwave.waveform[1];
3180  crusher_constraints.area = pregrad_area;
3181  status = ks_eval_crusher(&precrusher,start_amp, crusher_constraints);
3182  KS_RAISE(status);
3183 
3184  const float end_amp = 1.5f * selrf->gradwave.waveform[selrf->gradwave.res - 1] - 0.5f * selrf->gradwave.waveform[selrf->gradwave.res - 2];
3185  crusher_constraints.area = postgrad_area;
3186  status = ks_eval_crusher(&postcrusher, end_amp, crusher_constraints);
3187  KS_RAISE(status);
3188 
3189  /* Append crushers to slice selective gradwave (including some mirror gymnastics) */
3190  status = ks_eval_mirrorwave(&selrf->gradwave);
3191  KS_RAISE(status);
3192 
3193  status = ks_eval_mirrorwave(&precrusher);
3194  KS_RAISE(status);
3195 
3196  status = ks_eval_append_two_waves(&selrf->gradwave, &precrusher);
3197  KS_RAISE(status);
3198 
3199  status = ks_eval_mirrorwave(&selrf->gradwave);
3200  KS_RAISE(status);
3201 
3202  status = ks_eval_mirrorwave(&postcrusher);
3203  KS_RAISE(status);
3204 
3205  status = ks_eval_append_two_waves(&selrf->gradwave, &postcrusher);
3206  KS_RAISE(status);
3207 
3208  /* calculate usefull params */
3210  selrf->grad2rf_start = precrusher.duration;
3211  selrf->rf2grad_end = postcrusher.duration;
3212 
3213  }
3214 
3215  break;
3216  }
3217  /********************************************************************************/
3218  /*********************** RF SEL INVERSION ***************************************/
3219  /********************************************************************************/
3220  case KS_RF_ROLE_INV:
3221 
3222  if (selrf->gradwave.res!=0) {
3223  return ks_error("%s(%s): KS_RF_ROLE_INV does not support gradwaves yet.",__FUNCTION__,desc);
3224  }
3225 
3226  selrf->grad2rf_start = selrf->grad.ramptime;
3227  selrf->rf2grad_end = selrf->grad.ramptime;
3228 
3229  break;
3230  /********************************************************************************/
3231  /*********************** RF SPATIAL SATURATION **********************************/
3232  /********************************************************************************/
3233  case KS_RF_ROLE_SPSAT:
3234 
3235  if (selrf->gradwave.res!=0) {
3236  return ks_error("%s(%s): KS_RF_ROLE_SPSAT does not support gradwaves yet.",__FUNCTION__,desc);
3237  }
3238 
3239  selrf->grad2rf_start = selrf->grad.ramptime;
3240  selrf->rf2grad_end = selrf->grad.ramptime;
3241 
3242  break;
3243  /********************************************************************************/
3244  /********************************************************************************/
3245  /********************************************************************************/
3246  default:
3247  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);
3248  break;
3249 
3250  } /* role */
3251 
3252 
3253 
3254  /********************************************************************************/
3255  /*********************** Validation *********************************************/
3256  /********************************************************************************/
3257 
3258  if (selrf->gradwave.res) { /* custom gradient wave */
3259  float mostfavorable_gradmax = FMax(3, loggrd.tx, loggrd.ty, loggrd.tz);
3260 
3261  if (ks_wave_absmax(&selrf->gradwave) > mostfavorable_gradmax) {
3262  return ks_error("ks_eval_selrf(%s): too large gradient amplitude for 'gradwave'", desc);
3263  }
3264 
3265  /* we explicitly check for the hardware limits since loggrd is typically lower because obloptimze_epi
3266  (which allows access to the full slewrate limits) was not called for the rf pulse */
3267 
3268  /* we use config variables because loggrd or physgrd may be derated for silent scanning purposes selrf waves are timing sensitive */
3269  extern float cfxfs, cfyfs, cfzfs;
3270  extern int cfrmp2xfs, cfrmp2yfs, cfrmp2zfs;
3271  float max_sys_slewrate = FMin(3, cfxfs, cfyfs, cfzfs)/IMax(3,cfrmp2xfs,cfrmp2yfs,cfrmp2zfs);
3272  float max_gradwave_slewrate = ks_wave_maxslew(&selrf->gradwave);
3273  if (max_gradwave_slewrate > 1.00001 * max_sys_slewrate) {
3274  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);
3275  }
3276 
3277  } else { /* standard trapezoid */
3278 
3279  /* timing check: grad vs. RF */
3280  if (selrf->grad.duration && selrf->grad.plateautime != selrf->rf.rfwave.duration) {
3281  return ks_error("ks_eval_selrf(%s): grad.plateautime (%d) != rf.rfwave.duration (%d)", desc, selrf->grad.plateautime, selrf->rf.rfwave.duration);
3282  }
3283 
3284  /* gradient duration divisibility check */
3285  if (selrf->grad.duration % GRAD_UPDATE_TIME) {
3286  return ks_error("ks_eval_selrf(%s): grad.duration (%d) must be divisible by 4 [us]", desc, selrf->grad.duration);
3287  }
3288 
3289  }
3290 
3291  if (selrf->rf.omegawave.res) {
3293  float grad_absmax;
3294  if (selrf->gradwave.res) {
3296  grad_absmax = selrf->gradwave.abs_max_amp;
3297  } else if (selrf->grad.duration) {
3298  grad_absmax = fabs(selrf->grad.amp);
3299  } else {
3300  return ks_error("ks_eval_selrf(%s): no grads for the omega, this shouldn't be possible", desc);
3301  }
3302  ks_wave_multiplyval(&selrf->rf.omegawave, grad_absmax / selrf->rf.omegawave.abs_max_amp * GAM / 10.0);
3303  selrf->rf.omegawave.fs_factor = 0.1; /* so 1 bit of iamp = 0.1 mm */
3304  }
3305 
3306  return SUCCESS;
3307 
3308 } /* ks_eval_selrf_constrained */
float slew
Definition: KSFoundation.h:2372
STATUS ks_eval_append_two_waves(KS_WAVE *first_wave, KS_WAVE *second_wave)
Definition: KSFoundation_host.c:4438
int plateautime
Definition: KSFoundation.h:672
KS_TRAP grad
Definition: KSFoundation.h:1463
float fs_factor
Definition: KSFoundation.h:756
void ks_wave_compute_params(KS_WAVE *const wave)
Fills the members of the KS_WAVE sequence object
Definition: KSFoundation_common.c:1123
int iso2end_subpulse
Definition: KSFoundation.h:1034
#define areSame(a, b)
Definition: KSFoundation.h:144
int min_duration
Definition: KSFoundation.h:2373
float pregrad_area_offset
Definition: KSFoundation.h:1459
#define KS_NOTSET
Definition: KSFoundation.h:115
int role
Definition: KSFoundation.h:1027
Definition: KSFoundation.h:2369
Core sequence object making arbitrary waveforms on any board (using float data format)
Definition: KSFoundation.h:743
KS_SMS_INFO sms_info
Definition: KSFoundation.h:1468
float ks_wave_maxslew(const KS_WAVE *wave)
Returns the maximum absolute value in a KS_WAVE
Definition: KSFoundation_common.c:1394
KS_WAVE gradwave
Definition: KSFoundation.h:1465
float crusher_dephasing
Definition: KSFoundation.h:1457
RF_PULSE rfpulse
Definition: KSFoundation.h:1036
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:62
Definition: KSFoundation.h:2340
Definition: KSFoundation.h:2340
int gradwave_units
Definition: KSFoundation.h:751
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:384
Definition: KSFoundation.h:2359
Definition: KSFoundation.h:2363
int bridge_crushers
Definition: KSFoundation.h:1458
Definition: KSFoundation.h:2340
float area
Definition: KSFoundation.h:757
#define KS_INIT_CRUSHER_CONSTRAINT
Definition: KSFoundation.h:2378
float ks_wave_absmax(const KS_WAVE *wave)
Returns the maximum absolute value in a KS_WAVE sequence object
Definition: KSFoundation_common.c:1359
#define KS_DESCRIPTION_LENGTH
Definition: KSFoundation.h:253
KS_TRAP pregrad
Definition: KSFoundation.h:1462
float postgrad_area_offset
Definition: KSFoundation.h:1460
int grad2rf_start
Definition: KSFoundation.h:1466
float abs_max_amp
Definition: KSFoundation.h:761
STATUS ks_eval_ramp(KS_WAVE *ramp, float slew, float end_amp, int dwell)
Definition: KSFoundation_host.c:633
KS_RF rf
Definition: KSFoundation.h:1454
float ampmax
Definition: KSFoundation.h:2371
KS_DESCRIPTION description
Definition: KSFoundation.h:745
LOG_GRAD loggrd
void ks_create_suffixed_description(char *const out, const char *const prefix, const char *suffix,...) __attribute__((format(printf
KS_WAVE rfwave
Definition: KSFoundation.h:1037
ks_enum_crusher_strategy strategy
Definition: KSFoundation.h:2375
float area
Definition: KSFoundation.h:670
#define KS_RAISE(status)
Definition: KSFoundation.h:190
float amp
Definition: KSFoundation.h:669
STATUS ks_eval_selwave(KS_WAVE *gradwave, const char *const desc, float slthick, float bw, int rfduration)
Definition: KSFoundation_host.c:2928
void ks_init_wave(KS_WAVE *wave)
Resets a KS_WAVE sequence object to its default value (KS_INIT_WAVE)
Definition: KSFoundation_host.c:78
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:94
KS_TRAP postgrad
Definition: KSFoundation.h:1464
STATUS ks_eval_mirrorwave(KS_WAVE *wave)
Flips the contents of the KS_WAVEFORM in a KS_WAVE object
Definition: KSFoundation_host.c:2140
int iso2end
Definition: KSFoundation.h:1033
STATUS ks_eval_rf(KS_RF *rf, const char *const desc)
Sets up a KS_RF object
Definition: KSFoundation_host.c:2805
float area
Definition: KSFoundation.h:2370
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:448
float ks_wave_max(const KS_WAVE *wave)
Returns the maximum value in a KS_WAVE sequence object
Definition: KSFoundation_common.c:1276
KS_WAVE omegawave
Definition: KSFoundation.h:1038
int rf2grad_end
Definition: KSFoundation.h:1467
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:2888
int duration
Definition: KSFoundation.h:673
float ks_wave_min(const KS_WAVE *wave)
Returns the minimum value in a KS_WAVE sequence object
Definition: KSFoundation_common.c:1306
int res
Definition: KSFoundation.h:746
void ks_wave_multiplyval(KS_WAVE *wave, float val)
In-place scalar multiplication of a KS_WAVE
Definition: KSFoundation_common.c:1653
Definition: KSFoundation.h:2340
KS_WAVEFORM waveform
Definition: KSFoundation.h:748
int ramptime
Definition: KSFoundation.h:671
int duration
Definition: KSFoundation.h:747
STATUS ks_eval_crusher(KS_WAVE *crusher, const float end_amp, const ks_crusher_constraints crusher_constraints)
Sets up a KS_WAVE object that ramps from zero to the specified amplitude and ensures you obtain the m...
Definition: KSFoundation_host.c:964
float slthick
Definition: KSFoundation.h:1456
float bw
Definition: KSFoundation.h:1029

◆ 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
3312  {
3313 
3315 
3316 }
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:235
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:2967
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:305

◆ 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
3321  {
3322 
3324 
3325 }
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:2967
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:242
float ks_syslimits_slewrate2(LOG_GRAD loggrd)
Returns the maximum slewrate that can be used on two gradient boards simultaneously
Definition: KSFoundation_common.c:320

◆ 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
3330  {
3331 
3333 
3334 }
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:2967
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:335
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:249

◆ 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
3339  {
3340 
3342 
3343 }
float ks_syslimits_ampmax1p(LOG_GRAD loggrd)
Returns the maximum gradient amplitude on the physical gradient axes
Definition: KSFoundation_common.c:257
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:2967
LOG_GRAD loggrd
float ks_syslimits_slewrate1p(LOG_GRAD loggrd)
Returns the maximum gradient amplitude on the physical gradient axes
Definition: KSFoundation_common.c:352

◆ 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
3753  {
3754  /*
3755  % Return optimal phases for phase-modulated or amplitude-modulated low-peak power multiband pulses.
3756  %
3757  % sms_phase_modulation: Optimal phases
3758  % sms_multiband_factor: Number of bands
3759  % sms_phase_modulation_mode: phasmod, amplmod, or quadmod
3760  %
3761  % Adopted for EPIC from Will Grissom's (Vanderbilt University, 2015) MATLAB code by Ola Norbeck (Karolinska Unviversity Hospital, 2015).
3762  */
3763 
3764  int jdx;
3765 
3766  if (sms_phase_modulation_mode == KS_SELRF_SMS_PHAS_MOD_PHAS) {
3767 
3768  /* Wong's sms_phase_modulation: From E C Wong, ISMRM 2012, p. 2209 */
3769 
3770  if ((sms_multiband_factor < 2) || (sms_multiband_factor > 16)) {
3771  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);
3772  }
3773 
3774  float P[15][16] = {
3775  /* mbf = 2 */ {0.0, 0.0}, /* alt.{0.0, 3.142} */
3776  /* mbf = 3 */ {0.0, 0.730, 4.602},
3777  /* mbf = 4 */ {0.0, 3.875, 5.940, 6.197},
3778  /* mbf = 5 */ {0.0, 3.778, 5.335, 0.872, 0.471},
3779  /* mbf = 6 */ {0.0, 2.005, 1.674, 5.012, 5.736, 4.123},
3780  /* mbf = 7 */ {0.0, 3.002, 5.998, 5.909, 2.624, 2.528, 2.440},
3781  /* mbf = 8 */ {0.0, 1.036, 3.414, 3.778, 3.215, 1.756, 4.555, 2.467},
3782  /* mbf = 9 */ {0.0, 1.250, 1.783, 3.558, 0.739, 3.319, 1.296, 0.521, 5.332},
3783  /* mbf = 10 */ {0.0, 4.418, 2.360, 0.677, 2.253, 3.472, 3.040, 3.974, 1.192, 2.510},
3784  /* mbf = 11 */ {0.0, 5.041, 4.285, 3.001, 5.765, 4.295, 0.056, 4.213, 6.040, 1.078, 2.759},
3785  /* 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},
3786  /* 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},
3787  /* 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},
3788  /* 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},
3789  /* 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}
3790  };
3791 
3792  for (jdx = 0; jdx < sms_multiband_factor; jdx++ ) {
3793  sms_phase_modulation[jdx] = P[sms_multiband_factor - 2][jdx];
3794  }
3795 
3796  } else if (sms_phase_modulation_mode == KS_SELRF_SMS_PHAS_MOD_AMPL) {
3797 
3798  /* Seada's Hermitian sms_phase_modulation: From Seada et. al. Optimized Amplitude Modulated Multiband RF Pulse Design. MRM 2017 */
3799 
3800  if ((sms_multiband_factor < 3) || (sms_multiband_factor > 12)) {
3801  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);
3802  }
3803 
3804  float P[10][12] = {
3805  /* mbf = 3 */ {1.2846, 0.0000, -1.2846},
3806  /* mbf = 4 */ {0.9739, 1.3718, -1.3718, -0.9739},
3807  /* mbf = 5 */ {1.1572, -0.9931, 0.0000, 0.9931, -1.1572},
3808  /* mbf = 6 */ {1.6912, 2.8117, 1.1572, -1.1572, -2.8117, -1.6912},
3809  /* mbf = 7 */ {2.5813, -0.5620, 0.1030, 0.0000, -0.1030, 0.5620, -2.5813},
3810  /* mbf = 8 */ {2.1118, 0.2199, 1.4643, 1.9914, -1.9914, -1.4643, -0.2199, -2.1118},
3811  /* mbf = 9 */ {0.4800, -2.6669, -0.6458, -0.4189, 0.0000, 0.4189, 0.6458, 2.6669, -0.4800},
3812  /* mbf = 10 */ {1.6825, -2.3946, 2.9130, 0.3037, 0.7365, -0.7365, -0.3037, -2.9130, 2.3946, -1.6825},
3813  /* mbf = 11 */ {1.4050, 0.8866, -1.8535, 0.0698, -1.4940, 0.0000, 1.4940, -0.0698, 1.8535, -0.8866, -1.4050},
3814  /* 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}
3815  };
3816 
3817  for (jdx = 0; jdx < sms_multiband_factor; jdx++ ) {
3818  sms_phase_modulation[jdx] = P[sms_multiband_factor - 3][jdx];
3819  }
3820 
3821 
3822  } else if (sms_phase_modulation_mode == KS_SELRF_SMS_PHAS_MOD_QUAD) {
3823 
3824  /* Grissom's quadratic sms_phase_modulation (unpublished) */
3825 
3826  for (jdx = 0; jdx < sms_multiband_factor; jdx++) {
3827  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 */
3828  }
3829 
3830  } else {
3831 
3832  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);
3833 
3834  }; /* end of if */
3835 
3836  return SUCCESS;
3837 
3838 } /* EOF */
STATUS ks_error(const char *format,...) __attribute__((format(printf
Common error message function for HOST and TGT
Definition: KSFoundation.h:2355
Definition: KSFoundation.h:2354
Definition: KSFoundation.h:2353

◆ 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
3348  {
3349 
3350  STATUS status;
3351  float curr_b1 = 0.0;
3352  int debug = 0;
3353  double stepDown = 1.01;
3354  double stepUp = 1.05;
3355 
3356  /* Init tmp rf object */
3357  KS_SELRF selrftmp;
3358  ks_init_selrf(&selrftmp);
3359 
3360  /* Make the RF-pulse longer until its B1 peak is under its target */
3361  do {
3362  selrftmp = *selrf;
3363  ks_eval_stretch_rf(&selrftmp.rf, scaleFactor);
3364  status = ks_eval_selrf(&selrftmp, "tmp+");
3365  if (status == FAILURE) {continue;}
3366  if (sms_multiband_factor > 1) {
3367  status = ks_eval_sms_make_multiband(&selrftmp, &selrftmp, sms_multiband_factor, sms_phase_modulation_mode, sms_slice_gap, 0);
3368  } else {
3369  status = ks_eval_rfstat(&selrftmp.rf);
3370  }
3371  if (status == FAILURE) {continue;}
3372  curr_b1 = selrftmp.rf.rfpulse.max_b1;
3373  scaleFactor *= stepUp;
3374  if (debug) {ks_dbg("+ scaleFactor=%f, currB1=%f, maxB1=%f", scaleFactor, curr_b1, max_b1);}
3375  }
3376  while (fabs(curr_b1) > max_b1);
3377  scaleFactor /= stepUp;
3378 
3379  /* Make the RF-pulse shorter until its B1 peak hits its target */
3380  while ((fabs(curr_b1) < max_b1) && (scaleFactor > 0)) {
3381  selrftmp = *selrf;
3382  ks_eval_stretch_rf(&selrftmp.rf, scaleFactor);
3383  status = ks_eval_selrf(&selrftmp, "tmp-");
3384  if (status == FAILURE) {break;}
3385  if (sms_multiband_factor > 1) {
3386  status = ks_eval_sms_make_multiband(&selrftmp, &selrftmp, sms_multiband_factor, sms_phase_modulation_mode, sms_slice_gap, 0);
3387  } else {
3388  status = ks_eval_rfstat(&selrftmp.rf);
3389  }
3390  if (status == FAILURE) {break;}
3391  curr_b1 = selrftmp.rf.rfpulse.max_b1;
3392  scaleFactor /= stepDown;
3393  if (debug) {ks_dbg("- scaleFactor=%f, currB1=%f, maxB1=%f", scaleFactor, curr_b1, max_b1);}
3394  }
3395  scaleFactor *= stepDown;
3396 
3397  return scaleFactor;
3398 }
void ks_init_selrf(KS_SELRF *selrf)
Resets a KS_SELRF sequence object to its default value (KS_INIT_SELRF)
Definition: KSFoundation_host.c:102
Composite sequence object for slice-selective RF
Definition: KSFoundation.h:1453
int debug
Definition: GERequired.e:3728
RF_PULSE rfpulse
Definition: KSFoundation.h:1036
KS_RF rf
Definition: KSFoundation.h:1454
STATUS ks_eval_rfstat(KS_RF *rf)
Sets up the RF_PULSE structure of a KS_RF object
Definition: KSFoundation_host.c:2712
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:3470
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:3312
STATUS ks_eval_stretch_rf(KS_RF *rf, float stretch_factor)
In-place stretching of a KS_RF object
Definition: KSFoundation_host.c:3928

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

ADDTITLEHERE

ADDDESCHERE

Parameters
FA_trainADDTEXTHERE
MZ_trainADDTEXTHERE
[in]NADDTEXTHERE
[in]E1ADDTEXTHERE
[in]target_MTADDTEXTHERE
[in]nADDTEXTHERE
Returns
void
3403  {
3404  float MT;
3405  n = (n < 0) ? (N-1) : n;
3406  if (n == 0) {
3407  MZ_train[n] = 1.0;
3408  FA_train[n] = asin(target_MT);
3409  } else {
3410  ks_eval_transient_SPGR_FA_train_recursive(FA_train, MZ_train, N, E1, target_MT, n-1);
3411  MT = MZ_train[n-1] * sin(FA_train[n-1]);
3412  MZ_train[n] = MZ_train[n-1] * cos(FA_train[n-1]) * E1 + 1.0 - E1;
3413  FA_train[n] = asin( MT / MZ_train[n] );
3414  }
3415 }
void ks_eval_transient_SPGR_FA_train_recursive(float *FA_train, float *MZ_train, int N, float E1, float target_MT, int n)
ADDTITLEHERE
Definition: KSFoundation_host.c:3403

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

ADDTITLEHERE

ADDDESCHERE

Parameters
FA_trainADDTEXTHERE
MZ_trainADDTEXTHERE
[in]NADDTEXTHERE
[in]E1ADDTEXTHERE
[in]MTloADDTEXTHERE
[in]MThiADDTEXTHERE
[in]total_FAADDTEXTHERE
Returns
void
3420  {
3421  float thresh = 1e-7;
3422  if ((MThi-MTlo) < thresh) {
3423  return ks_eval_transient_SPGR_FA_train_recursive(FA_train, MZ_train, N, E1, MTlo, -1);
3424  }
3425  float MT = (MTlo + MThi)/2.0;
3426  ks_eval_transient_SPGR_FA_train_recursive(FA_train, MZ_train, N, E1, MT, -1);
3427  if (ks_eval_check_FA_train(N, FA_train, MZ_train, total_FA) == FAILURE) {
3428  return ks_eval_transient_SPGR_FA_train_binary_search(FA_train, MZ_train, N, E1, MTlo, MT, total_FA); /* Less greed */
3429  } else {
3430  return ks_eval_transient_SPGR_FA_train_binary_search(FA_train, MZ_train, N, E1, MT, MThi, total_FA); /* More greed */
3431  }
3432 }
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)
ADDTITLEHERE
Definition: KSFoundation_host.c:3420
STATUS ks_eval_check_FA_train(int N, float *FA_train, float *MZ_train, float total_FA)
ADDTITLEHERE
Definition: KSFoundation_host.c:3437
void ks_eval_transient_SPGR_FA_train_recursive(float *FA_train, float *MZ_train, int N, float E1, float target_MT, int n)
ADDTITLEHERE
Definition: KSFoundation_host.c:3403

◆ ks_eval_check_FA_train()

STATUS ks_eval_check_FA_train ( int  N,
float *  FA_train,
float *  MZ_train,
float  total_FA 
)

ADDTITLEHERE

ADDDESCHERE

Parameters
[in]NADDTEXTHERE
FA_trainADDTEXTHERE
MZ_trainADDTEXTHERE
[in]total_FAADDTEXTHERE
Return values
STATUSADDTEXTHERE
3437  {
3438  if (isnan(FA_train[N-1])) {return FAILURE;} /* infeasible solution */
3439  if (acos(MZ_train[N-1] * cos(FA_train[N-1])) > total_FA * PI/180.0) {return FAILURE;} /* total FA exceeded */
3440  int n;
3441  for (n=0; n<N; n++) {
3442  if ((n > 0) && (FA_train[n] < FA_train[n-1])) {return FAILURE;} /* decreasing FA */
3443  if ((n < N-1) && (FA_train[n] > 89.0 * PI/180.0)) {return FAILURE;} /* only last FA may be 90 degrees */
3444  }
3445  return SUCCESS;
3446 }

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

ADDTITLEHERE

ADDDESCHERE

Parameters
FA_trainADDTEXTHERE
[in]NADDTEXTHERE
[in]TRADDTEXTHERE
[in]T1ADDTEXTHERE
[in]total_FAADDTEXTHERE
Return values
STATUSADDTEXTHERE
3451  {
3452  if (N <= 0) {return ks_error("%s: N must be >0, but is %d", __FUNCTION__, N);}
3453  float E1 = exp(-TR/T1);
3454  float MTlo = 0.0;
3455  float MThi = 1.0;
3456  float MZ_train[N];
3457  ks_eval_transient_SPGR_FA_train_binary_search(FA_train, MZ_train, N, E1, MTlo, MThi, total_FA);
3458  if (ks_eval_check_FA_train(N, FA_train, MZ_train, total_FA) == FAILURE) {
3459  return ks_error("Ops");
3460  } else {
3461  int n;
3462  for (n=0; n<N; n++) {FA_train[n]*=180.0/PI;} /* radians->degrees */
3463  return SUCCESS;
3464  }
3465 }
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)
ADDTITLEHERE
Definition: KSFoundation_host.c:3420
STATUS ks_eval_check_FA_train(int N, float *FA_train, float *MZ_train, float total_FA)
ADDTITLEHERE
Definition: KSFoundation_host.c:3437

◆ 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
3471  {
3472 
3473  int jdx, idx;
3474  float sms_phase_modulation[16] = KS_INITZEROS(16);
3475  STATUS status;
3476 
3477  /* Debug stuff */
3478  int single_band_mode = -1; /* -1 = off */
3479  char fname[KS_DESCRIPTION_LENGTH + 64];
3480  char outputdir_uid[250];
3481  FILE *asciiWaveReal;
3482  FILE *asciiWaveImag;
3483  FILE *asciiWaveRealMB;
3484  FILE *asciiWaveImagMB;
3485  FILE *asciiWaveRealMod;
3486  FILE *asciiWaveImagMod;
3487  FILE *asciiWaveParams;
3488 
3489  if (debug) {
3490 
3491 #ifdef SIM
3492  sprintf(outputdir_uid, "./");
3493 #else
3494  char cmd[250];
3495  snprintf(outputdir_uid, 250, "/usr/g/mrraw/kstmp/%010d/", rhkacq_uid);
3496  snprintf(cmd, 250, "mkdir -p %s > /dev/null", outputdir_uid);
3497  system(cmd);
3498 #endif
3499 
3500  snprintf(fname, KS_DESCRIPTION_LENGTH + 64, "%s%s_mb_rf_Real.txt", outputdir_uid, selrfMB->rf.rfwave.description);
3501  asciiWaveReal = fopen(fname,"w");
3502  snprintf(fname, KS_DESCRIPTION_LENGTH + 64, "%s%s_mb_rf_Imag.txt", outputdir_uid, selrfMB->rf.rfwave.description);
3503  asciiWaveImag = fopen(fname,"w");
3504  snprintf(fname, KS_DESCRIPTION_LENGTH + 64, "%s%s_mb_rfMB_Real.txt", outputdir_uid, selrfMB->rf.rfwave.description);
3505  asciiWaveRealMB = fopen(fname,"w");
3506  snprintf(fname, KS_DESCRIPTION_LENGTH + 64, "%s%s_mb_rfMB_Imag.txt", outputdir_uid, selrfMB->rf.rfwave.description);
3507  asciiWaveImagMB = fopen(fname,"w");
3508  snprintf(fname, KS_DESCRIPTION_LENGTH + 64, "%s%s_mod_rfMB_Real.txt", outputdir_uid, selrfMB->rf.rfwave.description);
3509  asciiWaveRealMod = fopen(fname,"w");
3510  snprintf(fname, KS_DESCRIPTION_LENGTH + 64, "%s%s_mod_rfMB_Imag.txt", outputdir_uid, selrfMB->rf.rfwave.description);
3511  asciiWaveImagMod = fopen(fname,"w");
3512  }
3513 
3514  /* Check if gradient is present */
3515  if (areSame(selrf->grad.amp, 0) && (selrf->gradwave.res == 0)) {
3516  return ks_error("ks_eval_sms_make_multiband: Need a pre calculated gradient");
3517  }
3518 
3519  /* Duplicate the selRF structure */
3520  if (selrfMB != selrf ) {
3521  *selrfMB = *selrf;
3522  }
3523 
3524  /* Set info struct */
3525  selrfMB->sms_info.mb_factor = sms_multiband_factor;
3526  selrfMB->sms_info.slice_gap = sms_slice_gap;
3527  selrfMB->sms_info.pulse_type = KS_SELRF_SMS_MB;
3528 
3529  /* Save max b1 to make sure it does not get overwritten */
3530  double base_rf_max_amp = ks_waveform_absmax(selrf->rf.rfwave.waveform, ks_wave_res(&selrf->rf.rfwave));
3531 
3532  /* Get phase modulation to lower peak B1 */
3533  if (sms_phase_modulation_mode != KS_SELRF_SMS_PHAS_MOD_OFF) {
3534  status = ks_eval_sms_get_phase_modulation(sms_phase_modulation, sms_multiband_factor, sms_phase_modulation_mode);
3535  KS_RAISE(status);
3536  }
3537 
3538  /* initialize thetawave */
3539  if (selrfMB->rf.thetawave.res == 0) {
3540  ks_init_wave(&selrfMB->rf.thetawave);
3541  strncpy(selrfMB->rf.thetawave.description, selrfMB->rf.rfwave.description, KS_DESCRIPTION_LENGTH);
3542  selrfMB->rf.thetawave.res = selrfMB->rf.rfwave.res;
3543  selrfMB->rf.thetawave.duration = selrfMB->rf.rfwave.duration;
3544  }
3545 
3546  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); }
3547 
3548  /* Figure out a new resolution and interpolate, since MB pulses require high res */
3549  int newRes = selrf->rf.rfwave.duration/RF_UPDATE_TIME;
3550  float *newGrid = (float*)alloca(newRes * sizeof(float));
3551  for (idx = 0; idx < newRes; idx++) {
3552  newGrid[idx] = (float) idx / (newRes-1);
3553  }
3554  KS_WAVEFORM sb_rf = KS_INIT_WAVEFORM;
3555  KS_WAVEFORM sb_th = KS_INIT_WAVEFORM;
3556  if (newRes/selrf->rf.rfwave.res > 1) {
3557  /* create index for interpolation */
3558  float *orgGrid = (float*)alloca(selrf->rf.rfwave.res * sizeof(float));
3559  for (idx = 0; idx < selrf->rf.rfwave.res; idx++) {
3560  orgGrid[idx] = (float) idx / (selrf->rf.rfwave.res-1);
3561  }
3562  /* perform linear interpolation */
3563  ks_eval_linear_interp1(orgGrid, selrf->rf.rfwave.res, selrf->rf.rfwave.waveform, newGrid, newRes, sb_rf);
3564  ks_eval_linear_interp1(orgGrid, selrf->rf.thetawave.res, selrf->rf.thetawave.waveform, newGrid, newRes, sb_th);
3565 
3566  } else {
3567  for (idx=0; idx < newRes; idx++) {
3568  sb_rf[idx] = selrf->rf.rfwave.waveform[idx];
3569  sb_th[idx] = selrf->rf.thetawave.waveform[idx];
3570  }
3571  }
3572 
3573  /* If gradwave is used calc modulation and offset to account for ramps and crushers */
3575  if (selrf->gradwave.res != 0) {
3576 
3577  /* Find slice selective grad in gradwave */
3578  int gradWaveOffset_start = 0, gradWaveOffset_end = 0;
3579  int grad_update_time = selrf->gradwave.duration/selrf->gradwave.res;
3580  int gradWaveRes = selrf->gradwave.res;
3581  if (selrf->bridge_crushers) {
3582  gradWaveOffset_start = selrf->grad2rf_start/grad_update_time;
3583  gradWaveOffset_end = selrf->gradwave.res - selrf->rf2grad_end/grad_update_time;
3584  } else {
3585  int ramp_dur = (selrf->gradwave.duration - selrf->rf.rfwave.duration)/2;
3586  gradWaveOffset_start = ramp_dur/grad_update_time;
3587  gradWaveOffset_end = selrf->gradwave.res - gradWaveOffset_start;
3588  }
3589  gradWaveRes = gradWaveOffset_end-gradWaveOffset_start;
3591  for (idx=0; idx < gradWaveRes; idx++) {
3592  grad[idx] = selrf->gradwave.waveform[gradWaveOffset_start + idx];
3593  }
3594 
3595  /* interpolate if needed */
3596  if (grad_update_time/RF_UPDATE_TIME > 1) {
3597  /* create index for interpolation */
3598  float *orgGrid = (float*)alloca(gradWaveRes * sizeof(float));
3599  for (idx = 0; idx < gradWaveRes; idx++) {
3600  orgGrid[idx] = (float) idx / (gradWaveRes-1);
3601  }
3602  /* perform linear interpolation */
3603  ks_eval_linear_interp1(orgGrid, gradWaveRes, grad, newGrid, newRes, grad);
3604  }
3605  if (debug) {sprintf(fname, "%s%s_mb_gradwave.txt", outputdir_uid, selrfMB->rf.rfwave.description); ks_print_waveform(grad, fname, newRes); }
3606 
3607  /* calc kt modulation */
3608  ks_eval_mirrorwaveform(grad, newRes);
3609  ks_waveform_cumsum(kt, grad, newRes);
3610 
3611  /* center the modualtion */
3612  float kt_max = ks_waveform_max(kt, newRes);
3613  for (idx=0; idx < newRes; idx++) {
3614  kt[idx] = kt[idx] - kt_max/2.0;
3615  }
3616  } else if (debug) {
3618  for (idx=0; idx < newRes; idx++) {
3619  grad[idx] = selrf->grad.amp;
3620  }
3621  sprintf(fname, "%s%s_mb_gradwave.txt", outputdir_uid, selrfMB->rf.rfwave.description); ks_print_waveform(grad, fname, newRes);
3622  }
3623 
3624  /* Calc and apply sms modulation to base pulse */
3625  float *sms_wave_real = (float*)alloca(newRes*sizeof(float));
3626  float *sms_wave_imag = (float*)alloca(newRes*sizeof(float));
3627  for (idx=0; idx < newRes; idx++) {
3628 
3629  double wave_real, wave_imag, sms_modulation_real = 0, sms_modulation_imag = 0, p, slice_offset, wave_grad = 0;
3630 
3631  /* Convert base pulse from polar to cartesian */
3632  wave_real = sb_rf[idx] * cos(sb_th[idx]*(PI/180.0));
3633  wave_imag = sb_rf[idx] * sin(sb_th[idx]*(PI/180.0));
3634  if (debug) {
3635  fprintf(asciiWaveReal, "%d %f\n", idx, wave_real); fflush(asciiWaveReal);
3636  fprintf(asciiWaveImag, "%d %f\n", idx, wave_imag); fflush(asciiWaveImag);
3637  }
3638 
3639  /* Grad */
3640  if (selrf->gradwave.res == 0) {
3641  double centeredIndex = idx - (newRes-1) * 0.5;
3642  wave_grad = selrf->grad.amp * centeredIndex;
3643  } else {
3644  wave_grad = kt[idx];
3645  }
3646 
3647  /* Calc sms modulation */
3648  for (jdx = 0; jdx < sms_multiband_factor; jdx++) {
3649 
3650  slice_offset = (sms_slice_gap / 10.0) * (1.0 + jdx - (sms_multiband_factor / 2.0) - 0.5);
3651  p = 2.0 * PI * GAM * slice_offset * wave_grad * RF_UPDATE_TIME * 1e-6 + sms_phase_modulation[jdx];
3652 
3653  if (single_band_mode >= 0) {
3654  if (jdx == single_band_mode) {
3655  sms_modulation_real += cos(p);
3656  sms_modulation_imag += sin(p);
3657  }
3658  } else {
3659  sms_modulation_real += cos(p);
3660  sms_modulation_imag += sin(p);
3661  }
3662  }
3663 
3664  /* Apply sms modulation via complex multiplication */
3665  sms_wave_real[idx] = wave_real * sms_modulation_real - wave_imag * sms_modulation_imag;
3666  sms_wave_imag[idx] = wave_real * sms_modulation_imag + wave_imag * sms_modulation_real;
3667 
3668  if (debug) {
3669  fprintf(asciiWaveRealMB, "%d %f\n", idx, sms_wave_real[idx]); fflush(asciiWaveRealMB);
3670  fprintf(asciiWaveImagMB, "%d %f\n", idx, sms_wave_imag[idx]); fflush(asciiWaveImagMB);
3671  fprintf(asciiWaveRealMod, "%d %f\n", idx, sms_modulation_real); fflush(asciiWaveRealMod);
3672  fprintf(asciiWaveImagMod, "%d %f\n", idx, sms_modulation_imag); fflush(asciiWaveImagMod);
3673  }
3674 
3675  }
3676 
3677  if (debug) {
3678  fclose(asciiWaveReal);
3679  fclose(asciiWaveImag);
3680  fclose(asciiWaveRealMB);
3681  fclose(asciiWaveImagMB);
3682  fclose(asciiWaveRealMod);
3683  fclose(asciiWaveImagMod);
3684  /*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);*/
3685  }
3686 
3687  for (idx=0; idx < newRes; idx++) {
3688 
3689  if (sms_phase_modulation_mode == KS_SELRF_SMS_PHAS_MOD_OFF || sms_phase_modulation_mode == KS_SELRF_SMS_PHAS_MOD_AMPL) {
3690 
3691  /* Use real part and place waves in selrfMB */
3692  selrfMB->rf.rfwave.waveform[idx] = sms_wave_real[idx];
3693 
3694  } else {
3695 
3696  /* Convert from polar to cartesian and place waves in selrfMB */
3697  selrfMB->rf.rfwave.waveform[idx] = sqrt(pow(sms_wave_real[idx], 2.0) + pow(sms_wave_imag[idx], 2.0));
3698  selrfMB->rf.thetawave.waveform[idx] = atan2(sms_wave_imag[idx], sms_wave_real[idx]) * (180.0/PI);
3699 
3700  }
3701  }
3702 
3703  /* Update resolution fields */
3704  selrfMB->rf.rfwave.res = newRes;
3705  if (sms_phase_modulation_mode == KS_SELRF_SMS_PHAS_MOD_OFF || sms_phase_modulation_mode == KS_SELRF_SMS_PHAS_MOD_AMPL) {
3706  selrfMB->rf.thetawave.res = 0;
3707  selrfMB->rf.thetawave.duration = 0;
3708  } else {
3709  selrfMB->rf.thetawave.res = newRes;
3710  }
3711 
3712  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); }
3713  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); }
3714  if (debug && selrf->gradwave.res) {sprintf(fname, "%s%s_mb_grad.txt", outputdir_uid, selrfMB->rf.rfwave.description); ks_print_waveform(selrfMB->gradwave.waveform, fname, selrfMB->gradwave.res); }
3715 
3716  /* Modify rfpulse structure */
3717  double standard_pw = 1e-3;
3718  double b1_scale_factor = ks_waveform_absmax(selrfMB->rf.rfwave.waveform, newRes) / base_rf_max_amp;
3719  selrfMB->rf.rfpulse.max_b1 *= b1_scale_factor;
3720  selrfMB->rf.rfpulse.area /= b1_scale_factor;
3721  selrfMB->rf.rfpulse.effwidth /= b1_scale_factor;
3722  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;
3723  selrfMB->rf.rfpulse.max_rms_b1 = sqrt(selrfMB->rf.rfpulse.max_int_b1_sq / ((selrfMB->rf.rfwave.duration) * 1e-6) * standard_pw);
3724 
3725 
3726  /* Normalize to 1 */
3727  ks_wave_multiplyval(&selrfMB->rf.rfwave, 1.0 / ks_wave_absmax(&selrfMB->rf.rfwave));
3728 
3729  /* register the RF for later heat calcs and RF scaling */
3730  char description[KS_DESCRIPTION_LENGTH + 4];
3731  ks_create_suffixed_description(description, selrfMB->rf.rfwave.description, "_sms");
3732  status = ks_eval_rf(&selrfMB->rf, description);
3733  KS_RAISE(status);
3734 
3735  if (debug) {
3736  sprintf(fname, "%s%s_mb_params.txt", outputdir_uid, selrfMB->rf.rfwave.description);
3737  asciiWaveParams = fopen(fname,"w");
3738  fprintf(asciiWaveParams, "%f\n", (float)selrfMB->rf.flip);
3739  fprintf(asciiWaveParams, "%f\n", (float)selrfMB->grad.amp);
3740  fprintf(asciiWaveParams, "%f\n", (float)selrfMB->rf.rfpulse.max_b1);
3741  fprintf(asciiWaveParams, "%f\n", (float)selrfMB->slthick);
3742  fprintf(asciiWaveParams, "%f\n", (float)sms_slice_gap);
3743  fprintf(asciiWaveParams, "%f\n", (float)sms_multiband_factor);
3744  fprintf(asciiWaveParams, "%f\n", (float)b1_scale_factor);
3745  fflush(asciiWaveParams); fclose(asciiWaveParams);
3746  }
3747 
3748  return SUCCESS;
3749 
3750 } /* EOF */
float ks_waveform_max(const KS_WAVEFORM waveform, int res)
Returns the maximum value in a KS_WAVEFORM
Definition: KSFoundation_common.c:1253
KS_TRAP grad
Definition: KSFoundation.h:1463
#define areSame(a, b)
Definition: KSFoundation.h:144
int debug
Definition: GERequired.e:3728
KS_SMS_INFO sms_info
Definition: KSFoundation.h:1468
KS_WAVE gradwave
Definition: KSFoundation.h:1465
RF_PULSE rfpulse
Definition: KSFoundation.h:1036
STATUS ks_error(const char *format,...) __attribute__((format(printf
Common error message function for HOST and TGT
#define KS_INITZEROS(n)
Definition: KSFoundation.h:276
float slice_gap
Definition: KSFoundation.h:1337
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:3753
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:4278
int bridge_crushers
Definition: KSFoundation.h:1458
float flip
Definition: KSFoundation.h:1028
float ks_wave_absmax(const KS_WAVE *wave)
Returns the maximum absolute value in a KS_WAVE sequence object
Definition: KSFoundation_common.c:1359
#define KS_INIT_WAVEFORM
Definition: KSFoundation.h:294
#define KS_DESCRIPTION_LENGTH
Definition: KSFoundation.h:253
int grad2rf_start
Definition: KSFoundation.h:1466
KS_WAVE thetawave
Definition: KSFoundation.h:1039
int rhkacq_uid
KS_RF rf
Definition: KSFoundation.h:1454
KS_DESCRIPTION description
Definition: KSFoundation.h:745
void ks_create_suffixed_description(char *const out, const char *const prefix, const char *suffix,...) __attribute__((format(printf
KS_WAVE rfwave
Definition: KSFoundation.h:1037
float KS_WAVEFORM[KS_MAXWAVELEN]
Definition: KSFoundation.h:352
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:1571
#define KS_RAISE(status)
Definition: KSFoundation.h:190
float amp
Definition: KSFoundation.h:669
int mb_factor
Definition: KSFoundation.h:1336
void ks_init_wave(KS_WAVE *wave)
Resets a KS_WAVE sequence object to its default value (KS_INIT_WAVE)
Definition: KSFoundation_host.c:78
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:6680
STATUS ks_eval_rf(KS_RF *rf, const char *const desc)
Sets up a KS_RF object
Definition: KSFoundation_host.c:2805
int ks_wave_res(const KS_WAVE *wave)
Returns the number of samples in a KS_WAVE sequence object
Definition: KSFoundation_common.c:1156
int pulse_type
Definition: KSFoundation.h:1338
int rf2grad_end
Definition: KSFoundation.h:1467
float ks_waveform_absmax(const KS_WAVEFORM waveform, int res)
Returns the maximum absolute value in a KS_WAVEFORM
Definition: KSFoundation_common.c:1336
STATUS ks_eval_mirrorwaveform(KS_WAVEFORM waveform, int res)
Mirror in the res (time) domain a KS_WAVEFORM object
Definition: KSFoundation_host.c:2155
Definition: KSFoundation.h:2354
Definition: KSFoundation.h:2345
Definition: KSFoundation.h:2352
int res
Definition: KSFoundation.h:746
void ks_wave_multiplyval(KS_WAVE *wave, float val)
In-place scalar multiplication of a KS_WAVE
Definition: KSFoundation_common.c:1653
KS_WAVEFORM waveform
Definition: KSFoundation.h:748
int duration
Definition: KSFoundation.h:747
float slthick
Definition: KSFoundation.h:1456

◆ ks_eval_sms_calc_slice_gap()

float ks_eval_sms_calc_slice_gap ( int  sms_multiband_factor,
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]nslicesPrescribed number of slices
[in]slthickPrescribed slice thickness
[in]slspacePrescribed slice spacing
Return values
slicegapSMS slice gap in [mm]
3840  {
3841 
3842  float gap;
3843 
3844  if (nslices % sms_multiband_factor) {
3845  ks_error("ks_eval_sms_calc_slice_gap: Number of slices (%d, 3rd arg) must be divisible with the SMS factor (%d, 1st arg)", nslices, sms_multiband_factor);
3846  }
3847 
3848  /* gap between MB slices mm */
3849  gap = (float)((nslices + sms_multiband_factor - 1) / sms_multiband_factor) * (float)(slthick + slspace);
3850 
3851  return gap;
3852 
3853 } /* EOF */
STATUS ks_error(const char *format,...) __attribute__((format(printf
Common error message function for HOST and TGT

◆ ks_eval_sms_calc_slice_gap_from_scan_info()

float ks_eval_sms_calc_slice_gap_from_scan_info ( int  sms_multiband_factor,
int  nslices,
const SCAN_INFO *  org_slice_positions 
)

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

Parameters
[in]sms_multiband_factorThe multi-band acceleration factor
[in]nslicesPrescribed number of slices
[in]org_slice_positionsPrescribed slice positions
Return values
slicegapSMS slice gap in [mm]
3855  {
3856 
3857  float gap;
3858 
3859  if (nslices % sms_multiband_factor) {
3860  ks_error("ks_eval_sms_calc_slice_gap: Number of slices (%d, 3rd arg) must be divisible with the SMS factor (%d, 1st arg)", nslices, sms_multiband_factor);
3861  }
3862 
3863  gap = fabs(org_slice_positions[nslices/sms_multiband_factor].optloc - org_slice_positions[0].optloc);
3864 
3865  return gap;
3866 }
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]
3871  {
3872 
3873  if ((sms_slice_gap <= 0.0) || (caipi_fov_shift <= 1)) {
3874  return 0.0;
3875  }
3876 
3877  /* Calculate CAIPI-blip area in us*G/cm */
3878  return ((caipi_fov_shift-1)*(float)PI/(float)caipi_fov_shift) / (2.0*PI*GAM*1e-6 * (sms_slice_gap/10.0));
3879 
3880 } /* EOF */

◆ 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
4005  {
4006 
4007  STATUS status;
4008  int jdx, idx, kdx = 0, gdx = 0, debug = 0;
4009  int orgDur = selrfPINS->rf.rfwave.duration;
4010  KS_TRAP gzblip = KS_INIT_TRAP;
4011 
4012  /* Duplicate the selRF structure */
4013  if (selrfPINS != selrf ) {
4014  *selrfPINS = *selrf;
4015  }
4016 
4017  /* Set info struct */
4018  selrfPINS->sms_info.slice_gap = sms_slice_gap;
4019  selrfPINS->sms_info.pulse_type = KS_SELRF_SMS_PINS;
4020 
4021  ks_init_trap(&selrfPINS->grad);
4022 
4023  if (debug == 1) {ks_print_waveform(selrf->rf.rfwave.waveform, "pins_rfMB_base.txt", selrf->rf.rfwave.res); }
4024 
4025  double kzw = (selrf->rf.bw * selrf->rf.rfwave.duration * 1e-6) / (selrf->slthick / 10); /* 1/cm, width in kz-space we must go */
4026  int NPulses = (floor(ceil(kzw / (1 / (sms_slice_gap / 10))) / 2) * 2) + 1; /* number of subpulses (odd) */
4027 
4028  /* Init waves */
4029  KS_WAVEFORM rfLowRes = KS_INIT_WAVEFORM;
4030  KS_WAVE gzBlipWave = KS_INIT_WAVE;
4031  float *lowResGrid = (float*)alloca(NPulses * sizeof(float));
4032  float *baseGrid = (float*)alloca(selrf->rf.rfwave.res * sizeof(float));
4033 
4034  /* create index for interpolation */
4035  for (idx = 0; idx < selrf->rf.rfwave.res; idx++) {
4036  baseGrid[idx] = (float) idx / (selrf->rf.rfwave.res - 1);
4037  }
4038  for (idx = 0; idx < NPulses; idx++) {
4039  lowResGrid[idx] = (float) idx / (NPulses - 1);
4040  }
4041 
4042  /* Interpolate the base rf to get PINS sub pulse amplitudes and high res for MB pulse */
4043  ks_eval_linear_interp1(baseGrid, selrf->rf.rfwave.res, selrf->rf.rfwave.waveform, lowResGrid, NPulses, rfLowRes);
4044 
4045  if (debug == 1) {ks_print_waveform(rfLowRes, "pins_rfLowRes.txt", NPulses); }
4046 
4047  /* Create blip */
4048  double gArea = 1 / (sms_slice_gap / 10) / (GAM); /* (g sec)/cm, k-area of each blip */
4049  gzblip.area = gArea * 1e6; /* (G/cm)*us */
4050  status = ks_eval_trap1(&gzblip, "PINSblip"); KS_RAISE(status);
4051  status = ks_eval_trap2wave(&gzBlipWave, &gzblip); KS_RAISE(status);
4052  ks_init_trap(&gzblip);
4053 
4054  /* Matched-duration RF subpulses */
4055  float maxB1 = 0.24; /* temp. */
4056  float dt = GRAD_UPDATE_TIME / 1000000.0; /* sec */
4057  float pulseArea = (float) selrf->rf.flip * (PI / 180.0); /* radians or Hz (depending on how you look at it) */
4058  float subArea = (ks_waveform_absmax(rfLowRes, NPulses) * pulseArea) / (ks_waveform_sum(rfLowRes, NPulses) * 2.0 * PI * GAM); /* Gauss * sec */
4059  int hpw = ceil(subArea / (maxB1 * dt)); /* number of time points in sub pulse */
4060  float scaleFactor = 1.0;
4061 
4062  /* kludge :( */
4063  for (idx = 0; idx < NPulses; idx++) {
4064  if (areSame(rfLowRes[idx], 0)) {
4065  rfLowRes[idx] = rfLowRes[idx] + 0.01;
4066  }
4067  }
4068 
4069  /* Loop to concatenate the sub pulses and gradient blips */
4070  for (idx = 0; idx < NPulses; idx++) {
4071  for (jdx = 0; jdx < hpw; jdx++) {
4072  selrfPINS->rf.rfwave.waveform[kdx] = rfLowRes[idx] / scaleFactor;
4073  selrfPINS->gradwave.waveform[kdx] = 0.0;
4074  kdx++;
4075  }
4076  if (idx != NPulses - 1) {
4077  for (jdx = 0; jdx < gzBlipWave.res; jdx++) {
4078  selrfPINS->rf.rfwave.waveform[kdx] = 0.0;
4079  selrfPINS->gradwave.waveform[kdx] = gzBlipWave.waveform[jdx];
4080  kdx++; gdx++;
4081  }
4082  }
4083  }
4084 
4085  /* Set duration and resolution */
4087  selrfPINS->gradwave.res = RUP_FACTOR(kdx, GRAD_UPDATE_TIME);
4088  selrfPINS->gradwave.duration = selrfPINS->gradwave.res * GRAD_UPDATE_TIME;
4089  selrfPINS->rf.rfwave.res = selrfPINS->gradwave.res;
4090  selrfPINS->rf.rfwave.duration = selrfPINS->gradwave.duration;
4091  selrfPINS->rf.rfpulse.isodelay = RUP_FACTOR(ceil((float)selrfPINS->rf.rfpulse.isodelay * ((float)selrfPINS->rf.rfwave.duration/(float)orgDur)), GRAD_UPDATE_TIME);
4092  selrfPINS->rf.iso2end = selrfPINS->rf.rfpulse.isodelay;
4093  selrfPINS->rf.bw = 1e3 * (NPulses+1) * selrf->slthick / sms_slice_gap;
4094 
4095  /* If the resolution has been rounded up, place zeros at the end */
4096  for (idx = kdx; idx < selrfPINS->rf.rfwave.res; idx++) {
4097  selrfPINS->rf.rfwave.waveform[idx] = 0.0;
4098  selrfPINS->gradwave.waveform[idx] = 0.0;
4099  }
4100 
4101  if (debug == 1) {ks_print_waveform(selrfPINS->rf.rfwave.waveform, "pins_RF.txt", selrfPINS->rf.rfwave.res); }
4102  if (debug == 1) {ks_print_waveform(selrfPINS->gradwave.waveform, "pins_GRAD.txt", selrfPINS->gradwave.res); }
4103 
4104  /* Normalize to 1 */
4105  ks_waveform_multiplyval(selrfPINS->rf.rfwave.waveform, 1.0 / ks_waveform_absmax(selrfPINS->rf.rfwave.waveform, selrfPINS->rf.rfwave.res), selrfPINS->rf.rfwave.res);
4106 
4107  /* rfStat */
4108  status = ks_eval_rfstat(&selrfPINS->rf); KS_RAISE(status);
4109 
4110  /* init theta wave */
4111  ks_init_wave(&selrfPINS->rf.thetawave);
4112  selrfPINS->rf.thetawave.duration = selrfPINS->gradwave.duration;
4113  selrfPINS->rf.thetawave.res = selrfPINS->gradwave.res;
4114  if (debug == 1) {ks_print_waveform(selrfPINS->rf.thetawave.waveform, "pins_TH.txt", selrfPINS->rf.thetawave.res); }
4115 
4116  /* Register the RF */
4117  KS_DESCRIPTION description;
4118  ks_create_suffixed_description(description, selrfPINS->rf.rfwave.description, "_PINS");
4119  ks_create_suffixed_description(selrfPINS->gradwave.description, selrfPINS->rf.rfwave.description, "_PINS");
4120  if (debug == 1) {fprintf(stderr, "---------- PINS %s rfstat ---------\n", selrfPINS->rf.rfwave.description);}
4121  if (debug == 1) {ks_print_rfpulse(selrfPINS->rf.rfpulse, stderr);}
4122  return ks_eval_rf(&selrfPINS->rf, description);
4123 
4124 }
Definition: KSFoundation.h:2359
KS_TRAP grad
Definition: KSFoundation.h:1463
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:515
Core sequence object for making trapezoids on X,Y,Z, and OMEGA boards
Definition: KSFoundation.h:666
#define areSame(a, b)
Definition: KSFoundation.h:144
Core sequence object making arbitrary waveforms on any board (using float data format)
Definition: KSFoundation.h:743
float ks_waveform_sum(const KS_WAVEFORM waveform, int res)
Returns the sum of a KS_WAVEFORM
Definition: KSFoundation_common.c:1515
int debug
Definition: GERequired.e:3728
KS_SMS_INFO sms_info
Definition: KSFoundation.h:1468
#define KS_INIT_WAVE
Definition: KSFoundation.h:295
KS_WAVE gradwave
Definition: KSFoundation.h:1465
RF_PULSE rfpulse
Definition: KSFoundation.h:1036
void ks_init_trap(KS_TRAP *trap)
Resets a KS_TRAP sequence object to its default value (KS_INIT_TRAP)
Definition: KSFoundation_host.c:62
int gradwave_units
Definition: KSFoundation.h:751
float slice_gap
Definition: KSFoundation.h:1337
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:4278
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:4334
float flip
Definition: KSFoundation.h:1028
#define KS_INIT_WAVEFORM
Definition: KSFoundation.h:294
#define KS_INIT_TRAP
Definition: KSFoundation.h:298
KS_WAVE thetawave
Definition: KSFoundation.h:1039
KS_RF rf
Definition: KSFoundation.h:1454
KS_DESCRIPTION description
Definition: KSFoundation.h:745
STATUS ks_eval_rfstat(KS_RF *rf)
Sets up the RF_PULSE structure of a KS_RF object
Definition: KSFoundation_host.c:2712
void ks_create_suffixed_description(char *const out, const char *const prefix, const char *suffix,...) __attribute__((format(printf
KS_WAVE rfwave
Definition: KSFoundation.h:1037
float KS_WAVEFORM[KS_MAXWAVELEN]
Definition: KSFoundation.h:352
float area
Definition: KSFoundation.h:670
#define KS_RAISE(status)
Definition: KSFoundation.h:190
void ks_init_wave(KS_WAVE *wave)
Resets a KS_WAVE sequence object to its default value (KS_INIT_WAVE)
Definition: KSFoundation_host.c:78
int iso2end
Definition: KSFoundation.h:1033
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:6680
STATUS ks_eval_rf(KS_RF *rf, const char *const desc)
Sets up a KS_RF object
Definition: KSFoundation_host.c:2805
void ks_waveform_multiplyval(KS_WAVEFORM waveform, float val, int res)
In-place scalar multiplication of a KS_WAVEFORM
Definition: KSFoundation_common.c:1642
float maxB1[MAX_ENTRY_POINTS]
Definition: GERequired.e:303
int pulse_type
Definition: KSFoundation.h:1338
void ks_print_rfpulse(RF_PULSE rfpulse, FILE *fp)
Writes out the contents of an RF_PULSE struct for debugging
Definition: KSFoundation_host.c:6875
float ks_waveform_absmax(const KS_WAVEFORM waveform, int res)
Returns the maximum absolute value in a KS_WAVEFORM
Definition: KSFoundation_common.c:1336
char KS_DESCRIPTION[KS_DESCRIPTION_LENGTH]
Definition: KSFoundation.h:351
int res
Definition: KSFoundation.h:746
KS_WAVEFORM waveform
Definition: KSFoundation.h:748
int duration
Definition: KSFoundation.h:747
float slthick
Definition: KSFoundation.h:1456
float bw
Definition: KSFoundation.h:1029
Definition: KSFoundation.h:2346

◆ ks_eval_sms_make_pins_dante()

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

ADDTITLEHERE

ADDDESCHERE

Parameters
selrfPINSADDTEXTHERE
selrfADDTEXTHERE
[in]sms_slice_gapADDTEXTHERE
Return values
STATUSADDTEXTHERE
4129  {
4130  STATUS status;
4131  int jdx, idx, kdx = 0, gdx = 0, debug = 1;
4132  int orgDur = selrfPINS->rf.rfwave.duration;
4133  KS_TRAP gzblip = KS_INIT_TRAP;
4134 
4135  /* Duplicate the selRF structure */
4136  if (selrfPINS != selrf ) {
4137  *selrfPINS = *selrf;
4138  }
4139 
4140  /* Set info struct */
4141  selrfPINS->sms_info.slice_gap = sms_slice_gap;
4143 
4144  if (debug == 1) {ks_print_waveform(selrf->rf.rfwave.waveform, "dpins_rfMB_base.txt", selrf->rf.rfwave.res); }
4145  if (debug == 1) {ks_print_waveform(selrf->rf.thetawave.waveform, "dpins_phMB_base.txt", selrf->rf.thetawave.res); }
4146 
4147  /* Create blip */
4148  KS_WAVE gzBlipWave = KS_INIT_WAVE;
4149  double gArea = 1 / (sms_slice_gap / 10) / (GAM); /* (g sec)/cm, k-area of each blip */
4150  gzblip.area = gArea * 1e6; /* (G/cm)*us */
4151  status = ks_eval_trap1(&gzblip, "PINSblip"); KS_RAISE(status);
4152  status = ks_eval_trap2wave(&gzBlipWave, &gzblip); KS_RAISE(status);
4153  ks_init_trap(&gzblip);
4154 
4155  /* Calcualte PINS parameters */
4156  double kzw = (selrf->rf.bw * selrf->rf.rfwave.duration * 1e-6) / (selrf->slthick / 10); /* 1/cm, width in kz-space we must go */
4157  int NPulses = (floor(ceil(kzw / (1 / (sms_slice_gap / 10))) / 2) * 2) + 1; /* number of subpulses (odd) */
4158  int hpw = ceil((float)selrf->rf.rfwave.res / (float)NPulses); /* number of time points in sub pulse */
4159  int interpolRes = hpw * NPulses;
4160 
4161  /* Create index for interpolation */
4162  float *newGrid = (float*)alloca(interpolRes * sizeof(float));
4163  float *baseGrid = (float*)alloca(selrf->rf.rfwave.res * sizeof(float));
4164  for (idx = 0; idx < selrf->rf.rfwave.res; idx++) {
4165  baseGrid[idx] = (float) idx / (selrf->rf.rfwave.res - 1);
4166  }
4167  for (idx = 0; idx < interpolRes; idx++) {
4168  newGrid[idx] = (float) idx / (interpolRes - 1);
4169  }
4170 
4171  /* Interpolate the base rf & phase to make their resolution divideble by NPulses */
4172  KS_WAVEFORM rfNewRes = KS_INIT_WAVEFORM;
4173  KS_WAVEFORM phNewRes = KS_INIT_WAVEFORM;
4174  ks_eval_linear_interp1(baseGrid, selrf->rf.rfwave.res, selrf->rf.rfwave.waveform, newGrid, interpolRes, rfNewRes);
4175  ks_eval_linear_interp1(baseGrid, selrf->rf.thetawave.res, selrf->rf.thetawave.waveform, newGrid, interpolRes, phNewRes);
4176 
4177  if (debug == 1) {ks_print_waveform(rfNewRes, "rfNewRes.txt", interpolRes); }
4178  if (debug == 1) {ks_print_waveform(phNewRes, "phNewRes.txt", interpolRes); }
4179 
4180  /* Loop to concatenate the sub pulses and gradient blips */
4181  for (idx = 0; idx < NPulses; idx++) {
4182  for (jdx = 0; jdx < hpw; jdx++) {
4183  selrfPINS->rf.rfwave.waveform[kdx] = rfNewRes[(idx * hpw) + jdx];
4184  selrfPINS->rf.thetawave.waveform[kdx] = phNewRes[(idx * hpw) + jdx];
4185  selrfPINS->gradwave.waveform[kdx] = 0.0;
4186  kdx++;
4187  }
4188  if (idx != NPulses - 1) {
4189  for (jdx = 0; jdx < gzBlipWave.res; jdx++) {
4190  selrfPINS->rf.rfwave.waveform[kdx] = 0.0;
4191  selrfPINS->rf.thetawave.waveform[kdx] = 0.0;
4192  selrfPINS->gradwave.waveform[kdx] = gzBlipWave.waveform[jdx];
4193  kdx++; gdx++;
4194  }
4195  }
4196  }
4197 
4198  /* Set duration and resolution */
4199  selrfPINS->grad.duration = 0;
4201  selrfPINS->gradwave.res = RUP_FACTOR(kdx, GRAD_UPDATE_TIME);
4202  selrfPINS->gradwave.duration = selrfPINS->gradwave.res * GRAD_UPDATE_TIME;
4203  selrfPINS->rf.rfwave.res = selrfPINS->gradwave.res;
4204  selrfPINS->rf.rfwave.duration = selrfPINS->gradwave.duration;
4205  selrfPINS->rf.rfpulse.isodelay = RUP_FACTOR((int)(selrfPINS->rf.rfpulse.isodelay * ((float)selrfPINS->rf.rfwave.duration/(float)orgDur)),RF_UPDATE_TIME);
4206  selrfPINS->rf.iso2end = selrfPINS->rf.rfpulse.isodelay;
4207  selrfPINS->rf.bw = 1e3 * (NPulses+1) * selrf->slthick / sms_slice_gap;
4208 
4209  /* If the resolution has been rounded up, place zeros at the end */
4210  for (idx = kdx; idx < selrfPINS->rf.rfwave.res; idx++) {
4211  selrfPINS->rf.rfwave.waveform[idx] = 0.0;
4212  selrfPINS->rf.thetawave.waveform[idx] = selrfPINS->rf.thetawave.waveform[kdx];
4213  selrfPINS->gradwave.waveform[idx] = 0.0;
4214  }
4215 
4216  if (debug == 1) {ks_print_waveform(selrfPINS->rf.rfwave.waveform, "dpins_RF.txt", selrfPINS->rf.rfwave.res); }
4217  if (debug == 1) {ks_print_waveform(selrfPINS->gradwave.waveform, "dpins_GRAD.txt", selrfPINS->gradwave.res); }
4218 
4219  /* Normalize to 1 */
4220  ks_waveform_multiplyval(selrfPINS->rf.rfwave.waveform, 1.0 / ks_waveform_absmax(selrfPINS->rf.rfwave.waveform, selrfPINS->rf.rfwave.res), selrfPINS->rf.rfwave.res);
4221 
4222  /* rfStat */
4223  float orgMaxB1 = selrfPINS->rf.rfpulse.max_b1;
4224  selrfPINS->rf.thetawave.res = 0;
4225  status = ks_eval_rfstat(&selrfPINS->rf); KS_RAISE(status);
4226  selrfPINS->rf.rfpulse.max_b1 = orgMaxB1;
4227 
4228  /* Theta wave */
4229  selrfPINS->rf.thetawave.duration = selrfPINS->gradwave.duration;
4230  selrfPINS->rf.thetawave.res = selrfPINS->gradwave.res;
4231 
4232  for (idx = 0; idx < selrfPINS->rf.thetawave.res; idx++) {
4233  if (fabs(selrfPINS->rf.thetawave.waveform[idx]) > 180.0) {
4234  double angle = selrfPINS->rf.thetawave.waveform[idx];
4235  double revolutions = floor((angle + 180.0) / 360.0);
4236  selrfPINS->rf.thetawave.waveform[idx] = (float) (angle - revolutions * 360.0);
4237  }
4238  }
4239 
4240  if (debug == 1) {ks_print_waveform(selrfPINS->rf.thetawave.waveform, "dpins_TH.txt", selrfPINS->rf.thetawave.res); }
4241 
4242  /* Register the RF */
4243  KS_DESCRIPTION description;
4244  ks_create_suffixed_description(description, selrfPINS->rf.rfwave.description, "_PINSDANTE");
4245  ks_create_suffixed_description(selrfPINS->gradwave.description, selrfPINS->rf.rfwave.description, "_PINSDANTE");
4246  if (debug == 1) {fprintf(stderr, "---------- PINS DANTE %s rfstat ---------\n", selrfPINS->rf.rfwave.description);}
4247  if (debug == 1) {ks_print_rfpulse(selrfPINS->rf.rfpulse, stderr);}
4248  return ks_eval_rf(&selrfPINS->rf, description);
4249 
4250 }
Definition: KSFoundation.h:2359
KS_TRAP grad
Definition: KSFoundation.h:1463
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:515
Core sequence object for making trapezoids on X,Y,Z, and OMEGA boards
Definition: KSFoundation.h:666
Core sequence object making arbitrary waveforms on any board (using float data format)
Definition: KSFoundation.h:743
int debug
Definition: GERequired.e:3728
KS_SMS_INFO sms_info
Definition: KSFoundation.h:1468
#define KS_INIT_WAVE
Definition: KSFoundation.h:295
KS_WAVE gradwave
Definition: KSFoundation.h:1465
RF_PULSE rfpulse
Definition: KSFoundation.h:1036
void ks_init_trap(KS_TRAP *trap)
Resets a KS_TRAP sequence object to its default value (KS_INIT_TRAP)
Definition: KSFoundation_host.c:62
int gradwave_units
Definition: KSFoundation.h:751
float slice_gap
Definition: KSFoundation.h:1337
Definition: KSFoundation.h:2347
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:4278
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:4334
#define KS_INIT_WAVEFORM
Definition: KSFoundation.h:294
#define KS_INIT_TRAP
Definition: KSFoundation.h:298
KS_WAVE thetawave
Definition: KSFoundation.h:1039
KS_RF rf
Definition: KSFoundation.h:1454
KS_DESCRIPTION description
Definition: KSFoundation.h:745
STATUS ks_eval_rfstat(KS_RF *rf)
Sets up the RF_PULSE structure of a KS_RF object
Definition: KSFoundation_host.c:2712
void ks_create_suffixed_description(char *const out, const char *const prefix, const char *suffix,...) __attribute__((format(printf
KS_WAVE rfwave
Definition: KSFoundation.h:1037
float KS_WAVEFORM[KS_MAXWAVELEN]
Definition: KSFoundation.h:352
float area
Definition: KSFoundation.h:670
#define KS_RAISE(status)
Definition: KSFoundation.h:190
int iso2end
Definition: KSFoundation.h:1033
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:6680
STATUS ks_eval_rf(KS_RF *rf, const char *const desc)
Sets up a KS_RF object
Definition: KSFoundation_host.c:2805
void ks_waveform_multiplyval(KS_WAVEFORM waveform, float val, int res)
In-place scalar multiplication of a KS_WAVEFORM
Definition: KSFoundation_common.c:1642
int pulse_type
Definition: KSFoundation.h:1338
void ks_print_rfpulse(RF_PULSE rfpulse, FILE *fp)
Writes out the contents of an RF_PULSE struct for debugging
Definition: KSFoundation_host.c:6875
float ks_waveform_absmax(const KS_WAVEFORM waveform, int res)
Returns the maximum absolute value in a KS_WAVEFORM
Definition: KSFoundation_common.c:1336
int duration
Definition: KSFoundation.h:673
char KS_DESCRIPTION[KS_DESCRIPTION_LENGTH]
Definition: KSFoundation.h:351
int res
Definition: KSFoundation.h:746
KS_WAVEFORM waveform
Definition: KSFoundation.h:748
int duration
Definition: KSFoundation.h:747
float slthick
Definition: KSFoundation.h:1456
float bw
Definition: KSFoundation.h:1029

◆ 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
4255  {
4256 
4257  float dist = fabs(value - x[0]);
4258  float newDist;
4259  int idx = 0, i;
4260 
4261  for (i = 1; i < length; i++) {
4262 
4263  newDist = value - x[i];
4264 
4265  if (0 < newDist && newDist < dist) {
4266 
4267  dist = newDist;
4268  idx = i;
4269  }
4270  }
4271 
4272  return idx;
4273 }

◆ 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
4278  {
4279 
4280  int i, index;
4281  float dx, dy;
4282  float *slope = (float*)alloca(x_length * sizeof(float));
4283  float *intercept = (float*)alloca(x_length * sizeof(float));
4284  float *Y = (float*)alloca(x_length * sizeof(float));
4285  memcpy(Y, y, x_length * sizeof(float));
4286 
4287  for (i = 0; i < x_length; i++) {
4288 
4289  if (i < x_length - 1) {
4290 
4291  dx = x[i + 1] - x[i];
4292  dy = Y[i + 1] - Y[i];
4293  slope[i] = dy / dx;
4294  intercept[i] = Y[i] - x[i] * slope[i];
4295 
4296  } else {
4297 
4298  slope[i] = slope[i - 1];
4299  intercept[i] = intercept[i - 1];
4300 
4301  }
4302  }
4303 
4304  for (i = 0; i < xx_length; i++) {
4305 
4306  index = ks_eval_findNearestNeighbourIndex(xx[i], x, x_length);
4307  yy[i] = slope[index] * xx[i] + intercept[index];
4308  }
4309 } /* EOF */
int ks_eval_findNearestNeighbourIndex(float value, const float *x, int length)
Find nearest neighbor index (NEEDS BETTER DOCUMENTATION)
Definition: KSFoundation_host.c:4255

◆ 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
4334  {
4335 
4336  int i;
4337  int idx = 0;
4338 
4339  ks_init_wave(wave);
4340 
4341  int pointsInRamp = (trap->ramptime / GRAD_UPDATE_TIME) + 1;
4342  int pointsInPlateau = (trap->plateautime / GRAD_UPDATE_TIME) - 2;
4343  int pointsInWave = 2 * pointsInRamp + pointsInPlateau;
4344  double slope = trap->amp / pointsInRamp;
4345 
4346  if (trap->duration != (pointsInWave * GRAD_UPDATE_TIME)) {
4347  return ks_error("ks_eval_trap2wave(%s): field .ramptime or .plateautime not divisible by GRAD_UPDATE_TIME (4)", trap->description);
4348  }
4349 
4350  /* ramp up */
4351  for (i = 1; i <= pointsInRamp; i++) {
4352  wave->waveform[idx++] = (float)(i * slope);
4353  }
4354 
4355  /* plateau */
4356  for (i = 0; i < pointsInPlateau; i++) {
4357  wave->waveform[idx++] = trap->amp;
4358  }
4359 
4360  /* ramp down */
4361  for (i = pointsInRamp; i > 0; i--) {
4362  wave->waveform[idx++] = (float)(i * slope);
4363  }
4364 
4365  /* set fields */
4366  wave->res = idx;
4367  if (pointsInWave != wave->res) {
4368  return ks_error("ks_eval_trap2wave(%s): Implementation error - The resulting KS_WAVE has wrong res [%d!=%d]", trap->description, pointsInWave, wave->res);
4369  }
4370 
4371  wave->duration = idx * GRAD_UPDATE_TIME;
4372  if (trap->duration != wave->duration) {
4373  return ks_error("ks_eval_trap2wave(%s): Implementation error - The resulting KS_WAVE has wrong duration [%d!=%d]", trap->description,trap->duration,wave->duration);
4374  }
4375 
4377 
4378  return SUCCESS;
4379 
4380 } /* EOF */
int plateautime
Definition: KSFoundation.h:672
STATUS ks_error(const char *format,...) __attribute__((format(printf
Common error message function for HOST and TGT
KS_DESCRIPTION description
Definition: KSFoundation.h:745
void ks_create_suffixed_description(char *const out, const char *const prefix, const char *suffix,...) __attribute__((format(printf
float amp
Definition: KSFoundation.h:669
void ks_init_wave(KS_WAVE *wave)
Resets a KS_WAVE sequence object to its default value (KS_INIT_WAVE)
Definition: KSFoundation_host.c:78
KS_DESCRIPTION description
Definition: KSFoundation.h:668
int duration
Definition: KSFoundation.h:673
int res
Definition: KSFoundation.h:746
KS_WAVEFORM waveform
Definition: KSFoundation.h:748
int ramptime
Definition: KSFoundation.h:671
int duration
Definition: KSFoundation.h:747

◆ ks_eval_coords2wave()

STATUS ks_eval_coords2wave ( KS_WAVE wave,
float *  t,
float *  G,
int  num_coords,
int  dwell,
const char *  desc 
)
4383  {
4384  int i, bin;
4385 
4386  if (num_coords < 2) {
4387  return KS_THROW("%s: At least two coords must be provided, not %d", desc, num_coords);
4388  }
4389  if (fabs(t[0]) > 0.0f) {
4390  return KS_THROW("%s: First coord must be t=0, not %f", desc, t[0]);
4391  }
4392 
4393  for (i = 1; i < num_coords; i++) {
4394  if (t[i] < t[i-1]) {
4395  return KS_THROW("%s: Coords must be in time order (%f < %f)", desc, t[i], t[i-1]);
4396  }
4397  }
4398 
4399  int duration = (int)(t[num_coords-1]);
4400  if ((duration % (dwell)) || (fabs(t[num_coords-1] - (float)(duration)) > 0)) {
4401  return KS_THROW("%s: Last coord must be a multiple of %d, not %f", desc, dwell, t[num_coords-1]);
4402  }
4403  int res = duration / dwell;
4404 
4405  KS_WAVEFORM waveform = KS_INIT_WAVEFORM;
4406 
4407  /* loop over line segments */
4408  for (i = 1; i < num_coords; i++) {
4409  if ((t[i] - t[i-1]) <= 0) {
4410  continue; /* empty segment */
4411  }
4412  float slope = (G[i] - G[i-1]) / (t[i] - t[i-1]);
4413  int startbin = (int) floor(t[i-1] / dwell);
4414  int endbin = (int) floor(t[i] / dwell);
4415 
4416  /* startbin */
4417  float dur = FMin(2, t[i], (float)((startbin+1) * dwell)) - t[i-1];
4418  waveform[startbin] += dur * (G[i-1] + dur * slope / 2.0) / dwell;
4419 
4420  /* middle bins */
4421  for (bin = startbin + 1; bin < endbin; bin++) {
4422  waveform[bin] = G[i-1] + (dwell * ((float)(bin) + 0.5) - t[i-1]) * slope;
4423  }
4424 
4425  /* endbin */
4426  if ((startbin != endbin) && (endbin < res)) {
4427  dur = t[i] - dwell * (float)(endbin);
4428  waveform[endbin] += dur * (G[i] - dur * slope / 2.0) / dwell;
4429  }
4430  }
4431 
4432  return ks_eval_wave(wave, desc, res, duration, waveform);
4433 }
#define KS_INIT_WAVEFORM
Definition: KSFoundation.h:294
float KS_WAVEFORM[KS_MAXWAVELEN]
Definition: KSFoundation.h:352
STATUS ks_eval_wave(KS_WAVE *wave, const char *const desc, const int res, const int duration, const KS_WAVEFORM waveform)
Sets up a KS_WAVE object based on a waveform (KS_WAVEFORM) in memory
Definition: KSFoundation_host.c:2029
#define KS_THROW(format,...)
Definition: KSFoundation.h:181

◆ ks_eval_append_two_waves()

STATUS ks_eval_append_two_waves ( KS_WAVE first_wave,
KS_WAVE second_wave 
)
4438  {
4439  STATUS status;
4440  /* check inputs */
4441  if (first_wave->res > 0 && second_wave->res > 0 &&
4442  (first_wave->duration / first_wave->res) != (second_wave->duration / second_wave->res)) {
4443  return ks_error("%s: can't append waves with different dwell periods.", __FUNCTION__);
4444  }
4445 
4446  status = ks_eval_append_two_waveforms(first_wave->waveform, second_wave->waveform, first_wave->res, second_wave->res);
4447  KS_RAISE(status);
4448 
4449  /* fill in params */
4450  first_wave->res += second_wave->res;
4451  first_wave->duration += second_wave->duration;
4452  first_wave->area += second_wave->area;
4453  if (second_wave->max_amp > first_wave->max_amp) {
4454  first_wave->max_amp = second_wave->max_amp;
4455  }
4456  if (second_wave->abs_max_amp > first_wave->abs_max_amp) {
4457  first_wave->abs_max_amp = second_wave->abs_max_amp;
4458  }
4459  if (second_wave->abs_max_slew > first_wave->abs_max_slew) {
4460  first_wave->abs_max_slew = second_wave->abs_max_slew;
4461  }
4462  if (second_wave->min_amp < first_wave->min_amp) {
4463  first_wave->min_amp = second_wave->min_amp;
4464  }
4465 
4466  return SUCCESS;
4467 
4468 }
float abs_max_slew
Definition: KSFoundation.h:760
STATUS ks_error(const char *format,...) __attribute__((format(printf
Common error message function for HOST and TGT
float min_amp
Definition: KSFoundation.h:758
float area
Definition: KSFoundation.h:757
float abs_max_amp
Definition: KSFoundation.h:761
STATUS ks_eval_append_two_waveforms(KS_WAVEFORM first_waveform, KS_WAVEFORM second_waveform, int res1, int res2)
Definition: KSFoundation_host.c:4473
float max_amp
Definition: KSFoundation.h:759
#define KS_RAISE(status)
Definition: KSFoundation.h:190
int res
Definition: KSFoundation.h:746
KS_WAVEFORM waveform
Definition: KSFoundation.h:748
int duration
Definition: KSFoundation.h:747

◆ ks_eval_append_two_waveforms()

STATUS ks_eval_append_two_waveforms ( KS_WAVEFORM  first_waveform,
KS_WAVEFORM  second_waveform,
int  res1,
int  res2 
)
4473  {
4474  if ((res1 + res2) > KS_MAXWAVELEN) {
4475  return ks_error("%s: cannot append - the combined waveform exceeds KS_MAXWAVELEN.", __FUNCTION__);
4476  }
4477  memcpy( &(first_waveform[res1]), second_waveform, sizeof(float) * res2);
4478  return SUCCESS;
4479 }
STATUS ks_error(const char *format,...) __attribute__((format(printf
Common error message function for HOST and TGT
#define KS_MAXWAVELEN
Definition: KSFoundation.h:254

◆ ks_eval_concatenate_waves()

STATUS ks_eval_concatenate_waves ( int  num_waves,
KS_WAVE target_wave,
KS_WAVE **  waves_to_append 
)
4484  {
4485  int idx;
4486  STATUS status;
4487 
4488  for(idx = 0; idx < num_waves; idx++) {
4489  status = ks_eval_append_two_waves(target_wave, waves_to_append[idx]);
4490  KS_RAISE(status);
4491  }
4492 
4493  return SUCCESS;
4494 }
STATUS ks_eval_append_two_waves(KS_WAVE *first_wave, KS_WAVE *second_wave)
Definition: KSFoundation_host.c:4438
#define KS_RAISE(status)
Definition: KSFoundation.h:190

◆ 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
4499  {
4500 
4501  STATUS status;
4502  char tmpstr[1000];
4503  int kspacelines_noacc;
4504  /* same code as ks_eval_dixon_dualreadtrap */
4505  const float ampmax_phys = FMin(3, phygrd.xfs, phygrd.yfs, phygrd.zfs);
4506  const int ramptimemax_phys = IMax(3, phygrd.xrt, phygrd.yrt, phygrd.zrt);
4507  const float slewrate_phys = ampmax_phys / ramptimemax_phys;
4508  float slewrate_phasers; /* lower slew rate for phasers before/after the train */
4509  float ampmax_phasers; /* lower ampmax for phasers before/after the train */
4510 
4511  /* for now spherical model such that out of spec slewrate or amplitude cannot happen (for the phasers), even with prospective moco */
4512  if (epi->zphaser.res > 1) {
4513  slewrate_phasers = FMin(2, slewrate, slewrate_phys / sqrt(3));
4514  ampmax_phasers = FMin(2, ampmax, ampmax_phys / sqrt(3));
4515  } else {
4516  slewrate_phasers = FMin(2, slewrate, slewrate_phys / sqrt(2));
4517  ampmax_phasers = FMin(2, ampmax, ampmax_phys / sqrt(2));
4518  }
4519 
4520  if (desc == NULL || desc[0] == ' ') {
4521  return ks_error("%s: description (2nd arg) cannot be NULL or begin with a space", __FUNCTION__);
4522  }
4523 
4524  /************************** Step 1: resolution checking ************************/
4525 
4526  /* For EPI, we don't allow negative 'nover'. For lower k-space pFourier control, we use ks_phaseencoding_generate_epi() */
4527  if (epi->blipphaser.nover < 0) {
4528  return ks_error("%s(%s): negative 'blipphaser.nover' not allowed for EPI. See ks_phaseencoding_generate_epi()", __FUNCTION__, desc);
4529  }
4530  if (abs(epi->read.nover) > 0) {
4531  return ks_error("%s(%s): partial Fourier in read is not supported", __FUNCTION__, desc);
4532  }
4533  if (epi->minbliparea < 0 || epi->minbliparea > 1000) {
4534  return ks_error("%s(%s): field 'minbliparea' must be in range [0,1000]", __FUNCTION__, desc);
4535  }
4536  if (epi->read.res % 2) {
4537  return ks_error("%s(%s): Read res must be even", __FUNCTION__, desc);
4538  }
4539 
4540  /************************** Step 2: blip phaser (DE/REphasers) ***************************/
4541  epi->blipphaser.nacslines = 0; /* ACS lines are forbidden for EPI */
4542  epi->blipphaser.shotresalign = TRUE; /* make .res is divisible by 2*R in ks_eval_phaser_adjustres() */
4543  /* blip DE/REphaser (KS_PHASER) */
4544  sprintf(tmpstr, "%s.blipphaser", desc);
4545  status = ks_eval_phaser_constrained(&epi->blipphaser, tmpstr, ampmax_phasers, slewrate_phasers, 0);
4546  KS_RAISE(status);
4547 
4548  /************************** Step 3: z phaser ***************************/
4549  epi->blipphaser.shotresalign = FALSE; /* do not force .res to be divisible by 2*R in ks_eval_phaser_adjustres() */
4550  /* blip DE/REphaser (KS_PHASER) */
4551  sprintf(tmpstr, "%s.zphaser", desc);
4552  if ((epi->blipphaser.nover > 0) && (epi->zphaser.nover > 0)) {
4553  return ks_error("%s: Cannot have partial Fourier in both ky (nover=%d) and kz (nover=%d)", __FUNCTION__, epi->blipphaser.nover, epi->zphaser.nover);
4554  }
4555  if (epi->zphaser.res > 1) {
4556  status = ks_eval_phaser_constrained(&epi->zphaser, tmpstr, ampmax_phasers, slewrate_phasers, 0);
4557  KS_RAISE(status);
4558  } else {
4559  ks_init_phaser(&epi->zphaser);
4560  }
4561 
4562  /************************** Step 4: ETL ***************************/
4563 
4564  if (epi->blipphaser.nover > 0) {
4565  kspacelines_noacc = epi->blipphaser.res / 2 + epi->blipphaser.nover;
4566  } else {
4567  kspacelines_noacc = epi->blipphaser.res;
4568  }
4569 
4570  epi->etl = kspacelines_noacc / epi->blipphaser.R;
4571 
4572  if (epi->etl < 1) {
4573  return ks_error("%s(%s): R (or shots) must be in range [1,%d]", __FUNCTION__, desc, kspacelines_noacc);
4574  }
4575 
4576  /********************* Step 5: EPI blips ********************/
4577 
4578  /* blip area [(G/cm)*usec] */
4579  if (epi->etl > 1) {
4580 
4582 
4583  /* The blip area should not be too small to reduce discretization errors. This is based on observations doing moment calcs in WTools. */
4584  if (epi->blip.area < epi->minbliparea)
4585  epi->blipoversize = (epi->minbliparea / epi->blip.area); /* design a bigger blip, and use .blipoversize in ks_scan_epi_shotcontrol() to reduce the amp correspondingly */
4586  else
4587  epi->blipoversize = 1.0;
4588 
4590  } else {
4591  epi->blip.area = 0.0;
4592  }
4593  /* Get ramp & plateau time as well as amplitude of the blip */
4594  sprintf(tmpstr, "%s.blip", desc);
4595 
4596  status = ks_eval_trap_constrained(&epi->blip, tmpstr, ampmax, slewrate, 0);
4597  KS_RAISE(status);
4598 
4599  /************************** Step 6: CAIPI-blips **************************/
4600  if (epi->etl > 1) {
4601  sprintf(tmpstr, "%s.caipiblip", desc);
4602  status = ks_eval_trap_constrained(&epi->caipiblip, tmpstr, ampmax, slewrate, 0);
4603  KS_RAISE(status);
4604  }
4605 
4606  /************************** Step 7: Read width and amp **************************/
4607 
4608  /* rampsampling (normal case): We don't want to acquire data during half the blips duration (straddling two readouts) */
4609  int half_max_blip_dur = IMax(2, epi->blip.duration, epi->caipiblip.duration) / 2;
4610  if (epi->read.rampsampling)
4611  epi->read.acqdelay = half_max_blip_dur;
4612 
4613  sprintf(tmpstr, "%s.read", desc);
4614  status = ks_eval_readtrap_constrained(&epi->read, tmpstr, ampmax, slewrate);
4615  KS_RAISE(status);
4616 
4617  if (!epi->read.rampsampling) {
4618  /* non-rampsampling (unusual case) */
4619  if (epi->read.grad.ramptime < half_max_blip_dur) {
4620  /* blip-duration limited: Prolong the read gradient ramps to fit the blip, reduce PNS and acoustic noise */
4621  epi->read.grad.ramptime = half_max_blip_dur;
4622  epi->read.acqdelay = epi->read.grad.ramptime;
4623  epi->read.grad.duration = epi->read.grad.plateautime + 2 * epi->read.grad.ramptime;
4624  epi->read.grad.area = (epi->read.grad.plateautime + epi->read.grad.ramptime) * epi->read.grad.amp;
4625  epi->read.area2center = epi->read.grad.area / 2;
4626  }
4627  }
4628 
4629  if (epi->epi_readout_mode == KS_EPI_FLYBACK) {
4630  sprintf(tmpstr, "%s.read_flyback", desc);
4631  epi->read_flyback.area = epi->read.grad.area;
4632  if (ks_eval_trap_constrained(&epi->read_flyback, desc, ampmax, slewrate, 0) == FAILURE){
4633  return FAILURE;
4634  };
4635  }
4636 
4637  /************************** Step 8: read phaser (DE/REphasers) **************************/
4638 
4639  /* readphaser (KS_TRAP) */
4640  epi->readphaser.area = -epi->read.area2center;
4641 
4642  if (!areSame(epi->readphaser.area, 0.0)) {
4643  sprintf(tmpstr, "%s.readphaser", desc);
4644  status = ks_eval_trap_constrained(&epi->readphaser, tmpstr, ampmax_phasers, slewrate_phasers, 0);
4645  KS_RAISE(status);
4646  }
4647 
4648 
4649 
4650  /**************************** Step 9: Convenience info ****************************/
4651 
4652  ks_eval_epi_setinfo(epi);
4653 
4654 
4655  return SUCCESS;
4656 
4657 } /* ks_eval_epi_constrained */
float blipoversize
Definition: KSFoundation.h:1945
KS_TRAP blip
Definition: KSFoundation.h:1935
int plateautime
Definition: KSFoundation.h:672
int R
Definition: KSFoundation.h:1723
int res
Definition: KSFoundation.h:1721
KS_TRAP read_flyback
Definition: KSFoundation.h:1933
#define areSame(a, b)
Definition: KSFoundation.h:144
float ks_calc_fov2gradareapixel(float fov)
Calculates the gradient area needed to move one pixel in k-space
Definition: KSFoundation_common.c:492
int nacslines
Definition: KSFoundation.h:1724
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:4661
STATUS ks_error(const char *format,...) __attribute__((format(printf
Common error message function for HOST and TGT
KS_PHASER blipphaser
Definition: KSFoundation.h:1937
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:1952
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:384
KS_PHASER zphaser
Definition: KSFoundation.h:1938
PHYS_GRAD phygrd
KS_TRAP grad
Definition: KSFoundation.h:1561
void ks_init_phaser(KS_PHASER *phaser)
Resets a KS_PHASER sequence object to its default value (KS_INIT_PHASER)
Definition: KSFoundation_host.c:118
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:1036
int shotresalign
Definition: KSFoundation.h:1729
int etl
Definition: KSFoundation.h:1941
int res
Definition: KSFoundation.h:1552
KS_READTRAP read
Definition: KSFoundation.h:1932
float minbliparea
Definition: KSFoundation.h:1946
int epi_readout_mode
Definition: KSFoundation.h:1939
KS_TRAP caipiblip
Definition: KSFoundation.h:1936
float area
Definition: KSFoundation.h:670
#define KS_RAISE(status)
Definition: KSFoundation.h:190
float amp
Definition: KSFoundation.h:669
int nover
Definition: KSFoundation.h:1722
KS_TRAP readphaser
Definition: KSFoundation.h:1934
int nover
Definition: KSFoundation.h:1554
int rampsampling
Definition: KSFoundation.h:1553
Definition: KSFoundation.h:2329
int duration
Definition: KSFoundation.h:673
float area2center
Definition: KSFoundation.h:1559
float fov
Definition: KSFoundation.h:1720
int acqdelay
Definition: KSFoundation.h:1555
int ramptime
Definition: KSFoundation.h:671

◆ 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

4661  {
4662  const int readfact = (epi->epi_readout_mode == KS_EPI_SPLITODDEVEN) ? 2 : 1;
4663 
4664  epi->duration = (epi->etl * readfact) * epi->read.grad.duration + \
4665  ((epi->etl * readfact) - 1) * (epi->read_spacing + epi->read_flyback.duration) + \
4666  IMax(3, epi->readphaser.duration, epi->blipphaser.grad.duration, epi->zphaser.grad.duration) + \
4667  IMax(3, epi->readphaser.duration, epi->blipphaser.grad.duration, epi->zphaser.grad.duration);
4668 
4669  if (abs(epi->blipphaser.nover)) {
4670  /* partial Fourier */
4671  epi->time2center = ((epi->read.grad.duration + epi->read_flyback.duration + epi->read_spacing) * readfact * (epi->blipphaser.nover / epi->blipphaser.R)) - (epi->read_spacing + epi->read_flyback.duration) / 2;
4672  } else {
4673  epi->time2center = ((epi->read.grad.duration + epi->read_flyback.duration + epi->read_spacing) * readfact * (epi->etl / 2)) - (epi->read_spacing + epi->read_flyback.duration) / 2;
4674  }
4675  epi->time2center += IMax(3, epi->readphaser.duration, epi->blipphaser.grad.duration, epi->zphaser.grad.duration);
4676 
4677  return SUCCESS;
4678 }
int R
Definition: KSFoundation.h:1723
KS_TRAP read_flyback
Definition: KSFoundation.h:1933
KS_TRAP grad
Definition: KSFoundation.h:1719
KS_PHASER blipphaser
Definition: KSFoundation.h:1937
KS_PHASER zphaser
Definition: KSFoundation.h:1938
KS_TRAP grad
Definition: KSFoundation.h:1561
int duration
Definition: KSFoundation.h:1943
int etl
Definition: KSFoundation.h:1941
KS_READTRAP read
Definition: KSFoundation.h:1932
Definition: KSFoundation.h:2329
int epi_readout_mode
Definition: KSFoundation.h:1939
int nover
Definition: KSFoundation.h:1722
int time2center
Definition: KSFoundation.h:1944
KS_TRAP readphaser
Definition: KSFoundation.h:1934
int read_spacing
Definition: KSFoundation.h:1942
int duration
Definition: KSFoundation.h:673

◆ 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
4683  {
4684  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 };
4685  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 };
4686  int epi_initnewgeo = 1;
4687  extern SCAN_INFO scan_info[SLTAB_MAX];
4688  extern int opcoax;
4689  extern int opplane;
4690  extern int opslquant;
4691  extern int obl_debug;
4692  int stronggradient_flag = 0;
4693  int premiersystem_flag = 0;
4694  const float premierslewrate_max = 0.0150; /* 0.0150 (G/cm)/us = SR150 (T/m)/s */
4695  const float stronggradient_ampmax = 3.5; /* 35 mT/m. Maximum gradient amplitude to avoid too high rBW at k-space center */
4696 
4697  /* relationships below have been tested in the range [16,256] */
4698  if (xres < 16)
4699  xres = 16;
4700  if (xres > 256)
4701  xres = 256;
4702 
4703 
4704  if (quietnessfactor < 1.0) {
4705  return ks_error("%s: The quietness factor must be >= 1.0", __FUNCTION__);
4706  }
4707 
4708 
4709  inittargets(&epiloggrd, &epiphygrd);
4710 
4711  /* we need to know the system limits, but we don't want to be dependent on GERequired:GEReq_init_gradspecs() and ks_srfact
4712  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
4713  the EPI readout (and EPI blips), while the ampmax and slewrate for the EPI train is controlled only by this function */
4714  if (obloptimize_epi(&epiloggrd, &epiphygrd, scan_info, (opslquant),
4715  (opplane), (opcoax), PSD_OBL_OPTIMAL, (obl_debug), &epi_initnewgeo, cfsrmode) == FAILURE) {
4716  return ks_error("%s: obloptimize_epi() failed", __FUNCTION__);
4717  }
4718 
4719  /* ampmax */
4720 #if EPIC_RELEASE >= 27
4721  stronggradient_flag = (cfgcoiltype == PSD_XRMB_COIL || cfgcoiltype == PSD_HRMW_COIL); /* 750 or Premier. 50+ mT/m */
4722  premiersystem_flag = (cfgcoiltype == PSD_HRMW_COIL);
4723 #else
4724  stronggradient_flag = cfgcoiltype == PSD_XRMB_COIL; /* 750 */
4725 #endif
4726 
4727  if (stronggradient_flag) {
4728  *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) */
4729  } else {
4730  /* 750w and others */
4731  if (xres > 128)
4732  *ampmax = 0.005 * xres + 1.36; /* allow the amp to increase slowly for higher res than 128 */
4733  else
4734  *ampmax = 2.0;
4735  }
4736  *ampmax = FMin(2, *ampmax, epiphygrd.xfs);
4737 
4738 
4739  /* slewrate */
4740  if (stronggradient_flag) {
4741  /* 750 or Premier. 50+ mT/m */
4742  if (xres <= 48){
4743  *slewrate = 250e-4; /* SR250 */
4744  } else {
4745  *slewrate = 1.0e-4 / (2.4e-5 * xres + 0.003); /* SR250 (xres 48) -> SR110 (xres 256). 1/x function */
4746  }
4747  if (premiersystem_flag && *slewrate > premierslewrate_max) {
4748  *slewrate = premierslewrate_max;
4749  }
4750  } else {
4751  /* 750w. 33 mT/m, SR120 (and all others) */
4752  if (xres <= 48)
4753  *slewrate = 170e-4; /* SR170 */
4754  else
4755  *slewrate = 120e-4; /* SR120 */
4756 
4757  /* use the relative ampmax reduction dependent on slice angulation to instead reduce the slewrate on 750w.
4758  ks_syslimits_ampmax2(&epiloggrd) returns 1.0 for axial scans,
4759  but for oblique scans the value is reduced. Although this should cap the amplitude, we use
4760  is here to instead reduce the slewrate, since we need some way to make the slewrate lower for (double)
4761  oblique scans, or maybe when the XGRAD is not parallel to the physical X-axis (R/L) */
4762 
4763  *slewrate *= epiloggrd.tx_xy / epiloggrd.xfs;
4764  }
4765 
4766  /* but if user wants a quieter scan, reduce the slewate */
4767  *slewrate /= quietnessfactor;
4768 
4769 
4770  return SUCCESS;
4771 
4772 } /* 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)
SCAN_INFO scan_info[]
#define PSD_HRMW_COIL
Definition: KSFoundation.h:230
int obl_debug
Definition: GERequired.e:261
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
4776  {
4777 
4778  STATUS status;
4779  float ampmax, slewrate;
4780 
4781  status = ks_eval_epi_maxamp_slewrate(&ampmax, &slewrate, epi->read.res, quietnessfactor);
4782  KS_RAISE(status);
4783 
4784  return ks_eval_epi_constrained(epi, desc, ampmax, slewrate);
4785 
4786 } /* 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:4683
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:4499
int res
Definition: KSFoundation.h:1552
KS_READTRAP read
Definition: KSFoundation.h:1932
#define KS_RAISE(status)
Definition: KSFoundation.h:190

◆ ks_eval_echotrain()

STATUS ks_eval_echotrain ( KS_ECHOTRAIN *const  echotrain)

Evaluates the echotrain design

Ensures the echotrain design is resonable and counts the number of unique KS_READTRAPS and KS_READWAVES used. The array of KS_READCONTROL controls[KS_MAXUNIQUE_READ] and eval'd KS_READWAVES/KS_READTRAPS are the inputs.

Parameters
[in,out]echotrain- A pointer to a KS_ECHOTRAIN structure
Return values
STATUSSUCCESS or FAILURE
4790  {
4791  int readout = 0;
4792 
4793  echotrain->numtraps = -1;
4794  echotrain->numwaves = -1;
4795 
4796  int kspaces[KS_MAX_NUM_KSPACES];
4797  int kspace_encodes_per_shot[KS_MAX_NUM_KSPACES];
4798 
4799  int s;
4800  int i;
4801  echotrain->numstates = 0;
4802  for (s=0; s < KS_READCONTROL_MAXSTATE; s++){
4803  if (echotrain->controls[0].state[s].kspace_index == KS_NOTSET) {
4804  break;
4805  } else {
4806  echotrain->numstates++;
4807  }
4808  }
4809 
4810  for (s = 0; s < echotrain->numstates; s++) {
4811 
4812  echotrain->numkspaces[s] = 0;
4813  for (i = 0; i < KS_MAX_NUM_KSPACES; i++) {
4814  kspaces[i] = 0;
4815  kspace_encodes_per_shot[i] = 0;
4816  }
4817 
4818  for (readout = 0; readout < KS_MAXUNIQUE_READ; readout++) { /* readouts */
4819 
4820  /* We assume that the first readout with a uninitialized type marks the end of the array */
4821  if (echotrain->controls[readout].pg.read_type == KS_READ_NOTSET) {
4822  break;
4823  }
4824 
4825  KS_READCONTROL* control = &echotrain->controls[readout];
4826  KS_READ_TYPE type = control->pg.read_type;
4827  int object_index = control->pg.object_index;
4828  if (type == KS_READ_TRAP) {
4829  if (object_index > echotrain->numtraps) {
4830  echotrain->numtraps = object_index;
4831  }
4832  } else if (type == KS_READ_WAVE) {
4833  if (control->state[s].wave_state == KS_NOTSET) {
4834  return KS_THROW("Readout %d shot state %d has no wave_state. You probably forgot to set it", readout, s);
4835  }
4836  if (object_index > echotrain->numwaves) {
4837  echotrain->numwaves = object_index;
4838  }
4839  /*TODO if (echotrain->readwaves[object_index].grad.base.nstates != echotrain->numstates) {
4840  return ks_error("echotrain->readwaves[%i]: mismatch in wavestates [echotrain: %i != readwave: %i]", object_index, echotrain->numstates, echotrain->readwaves[object_index].grad.base.nstates);
4841  }*/
4842  }
4843 
4844  KS_READCONTROL_STATE * state = &control->state[s];
4845  /* Attempt finding kspace_index in the kspaces array */
4846  for(i = 0; i < echotrain->numkspaces[s]; i++) {
4847  if(kspaces[i] == state->kspace_index) {
4848  break;
4849  }
4850  }
4851  if (i == echotrain->numkspaces[s]) {
4852  /* not found: append at the end */
4853  kspaces[i] = state->kspace_index;
4854  echotrain->numkspaces[s]++;
4855  if (echotrain->numkspaces[s] > KS_MAX_NUM_KSPACES) {
4856  return KS_THROW("Echotrain supports up to %d unique kspaces.", KS_MAX_NUM_KSPACES);
4857  }
4858  }
4859  /* increase the count of how many times each kspace_index occurs in the echotrain */
4860  kspace_encodes_per_shot[kspaces[i]]++;
4861 
4862  } /* readout loop */
4863 
4864  if (echotrain->numkspaces[s] > echotrain->numkspaces[0]) {
4865  return KS_THROW("ariable numkspaces per readstate not supported -- state[0] = %i : state[%i] = %i.", echotrain->numkspaces[0], s, echotrain->numkspaces[s]);
4866  }
4867  for (i = 0; i < echotrain->numkspaces[s]; i++) {
4868  if(kspace_encodes_per_shot[kspaces[i]] != kspace_encodes_per_shot[kspaces[0]]) {
4869  return KS_THROW("Variable encodes per shot for kspaces is not supported (%d != %d)", kspace_encodes_per_shot[kspaces[i]], kspace_encodes_per_shot[kspaces[0]]);
4870  }
4871  }
4872 
4873  } /* state loop */
4874 
4875  echotrain->numtraps++;
4876  echotrain->numwaves++;
4877  echotrain->numreadouts = readout;
4878 
4879  return SUCCESS;
4880 } /* ks_eval_echotrain */
Definition: KSFoundation.h:1958
Wraps pulsegen information and state of each readout in an echotrain
Definition: KSFoundation.h:1998
int object_index
Definition: KSFoundation.h:1985
#define KS_NOTSET
Definition: KSFoundation.h:115
KS_READ_TYPE
The read object type, used in KS_READCONTROL_PULSEGEN
Definition: KSFoundation.h:1957
KS_READCONTROL_PULSEGEN pg
Definition: KSFoundation.h:2001
KS_READCONTROL_STATE state[KS_READCONTROL_MAXSTATE]
Definition: KSFoundation.h:2000
int numwaves
Definition: KSFoundation.h:2019
#define KS_MAXUNIQUE_READ
Definition: KSFoundation.h:264
int wave_state
Definition: KSFoundation.h:1972
KS_READ_TYPE read_type
Definition: KSFoundation.h:1984
Definition: KSFoundation.h:1960
Definition: KSFoundation.h:1959
Stores information used to adapt an array of readouts (KS_REATRAP/KS_READWAVE) during the scan...
Definition: KSFoundation.h:1970
int kspace_index
Definition: KSFoundation.h:1971
int numkspaces[KS_READCONTROL_MAXSTATE]
Definition: KSFoundation.h:2021
int numreadouts
Definition: KSFoundation.h:2020
#define KS_MAX_NUM_KSPACES
Definition: KSFoundation.h:266
#define KS_THROW(format,...)
Definition: KSFoundation.h:181
KS_READCONTROL controls[KS_MAXUNIQUE_READ]
Definition: KSFoundation.h:2015
#define KS_READCONTROL_MAXSTATE
Definition: KSFoundation.h:259
int numtraps
Definition: KSFoundation.h:2018
int numstates
Definition: KSFoundation.h:2022

◆ ks_echotrain_get_max_kspace_index()

int ks_echotrain_get_max_kspace_index ( KS_ECHOTRAIN echotrain)

Retrieve the total number of echoes this echotrain will produce

Parameters
echotrain- A pointer to a KS_ECHOTRAIN structure that has been eval'ed
Return values
int- The maximum kspace(echo) index.
1181  {
1182  int readout;
1183  int kspace_index;
1184  int max_kspace_index = 0;
1185  int s;
1186  for(s = 0; s < echotrain->numstates; s++) {
1187  for(readout = 0; readout < echotrain->numreadouts; readout++) {
1188  kspace_index = echotrain->controls[readout].state[s].kspace_index;
1189  if (kspace_index > max_kspace_index) {
1190  max_kspace_index = kspace_index;
1191  }
1192  }
1193  }
1194  return max_kspace_index;
1195 } /* ks_echotrain_get_max_kspace_index() */
KS_READCONTROL_STATE state[KS_READCONTROL_MAXSTATE]
Definition: KSFoundation.h:2000
int kspace_index
Definition: KSFoundation.h:1971
int numreadouts
Definition: KSFoundation.h:2020
KS_READCONTROL controls[KS_MAXUNIQUE_READ]
Definition: KSFoundation.h:2015
int numstates
Definition: KSFoundation.h:2022

◆ ks_get_maxsize_finfo()

PSD_FILTER_GEN ks_get_maxsize_finfo ( PSD_FILTER_GEN  my_psd_filter_spec[])

Loops through all filter slots and returns the one that has the most time points (length)

Parameters
[in]my_psd_filter_spec- typically GE's global array of filters
Return values
PSD_FILTER_GEN- the filter with the maximum length

◆ ks_grad_heat_reset()

void ks_grad_heat_reset ( )
5038  {
5040  grad_heat_handle.play = NULL;
5041  grad_heat_handle.nargs = 0;
5042  grad_heat_handle.args = NULL;
5043 }
STATUS(* play)(const INT max_encode_mode, int nargs, void **args)
Definition: KSFoundation_host.c:5017
KS_SEQ_COLLECTION * seqcollection_p
Definition: KSFoundation_host.c:5016
int nargs
Definition: KSFoundation_host.c:5018
void ** args
Definition: KSFoundation_host.c:5019
struct _grad_heat_handle grad_heat_handle
Definition: KSFoundation_host.c:5023

◆ ks_grad_heat_init()

void ks_grad_heat_init ( KS_SEQ_COLLECTION seqcollection_p,
STATUS(*)(const INT max_encode_mode, int nargs, void **args)  play,
int  nargs,
void **  args 
)
5028  {
5029  grad_heat_handle.seqcollection_p = seqcollection_p;
5030  grad_heat_handle.play = play;
5031  grad_heat_handle.nargs = nargs;
5032  grad_heat_handle.args = args;
5033 }
STATUS(* play)(const INT max_encode_mode, int nargs, void **args)
Definition: KSFoundation_host.c:5017
KS_SEQ_COLLECTION * seqcollection_p
Definition: KSFoundation_host.c:5016
int nargs
Definition: KSFoundation_host.c:5018
void ** args
Definition: KSFoundation_host.c:5019
struct _grad_heat_handle grad_heat_handle
Definition: KSFoundation_host.c:5023

◆ ks_eval_rflimits()

int ks_eval_rflimits ( KS_SAR sar,
KS_SEQ_COLLECTION seqcollection 
)

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

This function uses one KS_SEQ_COLLECTION struct holding the RF 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_hwlimits() 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
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)
5703  {
5704  extern int tmin;
5705  int old_tmin;
5706  int i, j, k;
5707  int rfindx = 0;
5708  int numuniquerf = 0;
5709  int nettime = 0;
5710  int newtime_rfamp = 0;
5711  int newtime_gradheat = 0;
5712  int newtime_sar = 0;
5713  int newtime_allconstraints = 0;
5714  int dummy;
5715  double ave_sar = 0;
5716  double peak_sar = 0;
5717  double cave_sar = 0; /* Coil SAR */
5718  double b1rms = 0;
5719 
5720  STATUS status;
5721  if (seqcollection == NULL) {
5722  ks_error("%s: 2nd arg is NULL (KS_SEQ_COLLECTION)", __FUNCTION__);
5723  return KS_NOTSET;
5724  } else if (seqcollection->numseq < 1) {
5725  ks_error("%s: No sequence modules in 2nd arg (KS_SEQ_COLLECTION)", __FUNCTION__);
5726  return KS_NOTSET;
5727  }
5728 
5729  /********** copy to common struct arrays for grad and RF from the sequence collection **********/
5730  for (i = 0; i < seqcollection->numseq; i++) {
5731  for (j = 0; j < seqcollection->seqctrlptr[i]->gradrf.numrf; j++) {
5732  numuniquerf += seqcollection->seqctrlptr[i]->gradrf.rfptr[j]->rfpulse.num;
5733  }
5734  }
5735 
5737  if (nettime <= 0) {
5738  return nettime;
5739  }
5740 
5741  RF_PULSE *myrfpulse = (RF_PULSE*)alloca(numuniquerf * sizeof(RF_PULSE));
5742  float *myrfflip = (float*)alloca(numuniquerf * sizeof(float));
5743 
5744  for (i = 0; i < seqcollection->numseq; i++) {
5745 
5746  for (j = 0; j < seqcollection->seqctrlptr[i]->gradrf.numrf; j++) {
5748 
5749  /* Point to the current KS_RF (e.g. excitation, refocusing etc) */
5750  KS_RF *myrf = seqcollection->seqctrlptr[i]->gradrf.rfptr[j];
5751 
5752  for (k = 0; k < myrf->rfpulse.num; k++) { /* or myrf->rfpulse.num */
5753 
5754  /* Deep copy of RF_PULSE structure (incl pointers to KS_RF fields .flip and .amp etc) */
5755  myrfpulse[rfindx] = myrf->rfpulse;
5756 
5757  myrfflip[rfindx] = myrf->flip * fabs(myrf->rfwave.locs[k].ampscale);
5758 
5759  /* Update the rfpulse.act_fa pointers so that are unique to each instance. */
5760  myrfpulse[rfindx].act_fa = &(myrfflip[rfindx]);
5761 
5762  /* Each RF pulse instance is unique within a module so the total number is equal to the number
5763  of times this sequence module is played out in the time period considered. */
5764  myrfpulse[rfindx].num = seqcollection->seqctrlptr[i]->nseqinstances;
5765 
5766  rfindx++;
5767 
5768  } /* for each RF instance */
5769 
5770  } /* for each unique KS_RF */
5771 
5772  }
5773 
5774  /* Protection against not understood values of minseqcable_t or minseqbusbar_t
5775  which can be -2147483648 (neg INT max). This is nonsense and out of valid CV range (by 1 value)
5776  and leads to download failure. Need to find where minseqcable_t and minseqbusbar_t are set.
5777  */
5778  if (minseqcable_t < 0 || minseqcable_t > 100 * nettime) {
5779  minseqcable_t = 0;
5780  }
5781  if (minseqbusbar_t < 0 || minseqbusbar_t > 100 * nettime) {
5782  minseqbusbar_t = 0;
5783  }
5784 
5785 
5786  /********** RF amplifier **********/
5787  /* tmin is used in minseqrfamp, this will fail if tmin is set to zero*/
5788  old_tmin = tmin;
5789  tmin = nettime;
5790  status = minseqrfamp(&newtime_rfamp, numuniquerf, myrfpulse, L_SCAN);
5791  tmin = old_tmin;
5792  ks_dbg("%s: minseqrfamp = %dus; nettime = %dus", __FUNCTION__, newtime_rfamp, nettime);
5793  if (status != SUCCESS) {
5794  ks_error("%s: minseqrfamp() failed - Please reduce FA", __FUNCTION__);
5795  return KS_NOTSET;
5796  }
5797 
5798  /********** RF SAR **********/
5799 #if EPIC_RELEASE >= 24
5800  status = maxsar(&newtime_sar,
5801  &dummy, &ave_sar, &cave_sar, &peak_sar, &b1rms,
5802  numuniquerf, myrfpulse, L_SCAN, nettime);
5803 #else
5804  status = maxsar(&newtime_sar,
5805  &dummy, &ave_sar, &cave_sar, &peak_sar,
5806  numuniquerf, myrfpulse, L_SCAN, nettime);
5807 #endif
5808 
5809  if (status != SUCCESS) {
5810  ks_error("%s: maxsar() failed", __FUNCTION__);
5811  return KS_NOTSET;
5812  }
5813 
5814  if (sar != NULL) {
5815  sar->average = ave_sar;
5816  sar->coil = cave_sar;
5817  sar->peak = peak_sar;
5818  sar->b1rms = b1rms;
5819  }
5820 
5821  newtime_allconstraints = IMax(3, nettime, newtime_rfamp, newtime_sar);
5822 
5823  if (sar == NULL) {
5824  /* Change description of optr only if `sar` (1st arg) == NULL (c.f. GEReq_eval_TR()->ks_eval_mintr() )
5825  This is because when we use ks_eval_mintr() we have .duration = .min_duration and hence nettime is equal
5826  to the bare sum of sequence module durations */
5827  char tmpstr[100];
5828 
5829  if (newtime_allconstraints == nettime) {
5830  sprintf(tmpstr, "TR [Not SAR/heat limited (%d)]", nettime);
5831  } else if (newtime_allconstraints == newtime_gradheat) {
5832  sprintf(tmpstr, "TR [Grad. heat limited (%d/%d)]", nettime, newtime_allconstraints);
5833  } else if (newtime_allconstraints == newtime_rfamp) {
5834  sprintf(tmpstr, "TR [RF amp limited (%d/%d)]", nettime, newtime_allconstraints);
5835  } else if (newtime_allconstraints == newtime_sar) {
5836  sprintf(tmpstr, "TR [SAR limited (%d/%d)]", nettime, newtime_allconstraints);
5837  }
5838  cvdesc(optr, tmpstr); /* _optr declared as extern above */
5839  }
5840 
5841  /* return the maximum value (rounded up to nearest divisible by 8) */
5842  return RUP_GRD(newtime_allconstraints);
5843 
5844 } /* ks_eval_rflimits */
STATUS minseqrfamp(INT *Minseqrfamp, const INT numPulses, const RF_PULSE *rfpulse, const INT entry)
STATUS maxsar(INT *Maxseqsar, INT *Maxslicesar, DOUBLE *Avesar, DOUBLE *Cavesar, DOUBLE *Pksar, DOUBLE *B1rms, const INT numPulses, const RF_PULSE *rfpulse, const INT entry, const INT tr_val)
int minseqbusbar_t
double b1rms
Definition: KSFoundation.h:1285
KS_SEQLOC * locs
Definition: KSFoundation.h:752
KS_RF * rfptr[KS_MAXUNIQUE_RF]
Definition: KSFoundation.h:1055
#define KS_NOTSET
Definition: KSFoundation.h:115
double average
Definition: KSFoundation.h:1282
RF_PULSE rfpulse
Definition: KSFoundation.h:1036
STATUS ks_error(const char *format,...) __attribute__((format(printf
Common error message function for HOST and TGT
double coil
Definition: KSFoundation.h:1283
int nseqinstances
Definition: KSFoundation.h:1225
KS_SEQ_CONTROL * seqctrlptr[KS_MAXUNIQUE_SEQUENCES]
Definition: KSFoundation.h:1268
int numrf
Definition: KSFoundation.h:1056
float ampscale
Definition: KSFoundation.h:464
float flip
Definition: KSFoundation.h:1028
KS_GRADRFCTRL gradrf
Definition: KSFoundation.h:1236
Composite sequence object for RF (with optional OMEGA & THETA pulses)
Definition: KSFoundation.h:1026
KS_WAVE rfwave
Definition: KSFoundation.h:1037
s64 ks_eval_seqcollection_gettotalduration(KS_SEQ_COLLECTION *seqcollection)
Returns the total duration of the sequence collection
Definition: KSFoundation_host.c:5965
void ks_eval_rf_relink(KS_RF *rf)
(Internal use) Relinks pointers between fields in a KS_RF object
Definition: KSFoundation_host.c:2869
KS_SEQ_COLLECTION seqcollection
Definition: ksepi.e:82
STATUS STATUS ks_dbg(const char *format,...) __attribute__((format(printf
Common debug message function for HOST and TGT
int minseqcable_t
double peak
Definition: KSFoundation.h:1284
int numseq
Definition: KSFoundation.h:1264

◆ ks_eval_gradlimits()

STATUS ks_eval_gradlimits ( int *  newtime,
KS_SEQ_COLLECTION seqcollection,
const LOG_GRAD *  log_grad,
STATUS(*)(const INT max_encode_mode, int nargs, void **args)  play,
int  nargs,
void **  args 
)
5646  {
5647  STATUS s;
5648 
5649  if (!seqcollection) {
5650  return KS_THROW("NULL pointer to sequence collection");
5651  }
5652 
5653  /* Run the gradient heating model */
5654  ks_grad_heat_init(seqcollection, play, nargs, args);
5655 
5656  gradHeatMethod = TRUE;
5657  enforce_minseqseg = PSD_ON;
5658 
5659  if (seqcollection->numseq < 1 || seqcollection->numplayouts < 1) {
5660  return KS_THROW("No sequence entries to evaluate");
5661  }
5662 
5663  if (!seqcollection->seqctrlptr[0]) {
5664  return KS_THROW("NULL pointer to the first sequence entry");
5665  }
5666 
5667  /*ks_dbg("Running gradlimits on index %d", seqcollection->seqctrlptr[0]->handle.index);*/
5668 
5669  s = minseq(newtime,
5670  NULL, 0, NULL, 0, NULL, 0,
5671  log_grad, seqcollection->seqctrlptr[0]->handle.index,
5672  GRAD_UPDATE_TIME, 0 /* oldtime, unused */, 0, 0);
5673 
5674  /* revert to the old method for subsequent calls to minseq
5675  that are executed in prescan's entry points.
5676  */
5677  gradHeatMethod = FALSE;
5678  enforce_minseqseg = PSD_OFF;
5679 
5681 
5682  if (s != SUCCESS) {
5683  KS_THROW("minseq failed");
5684 #ifndef SIM
5685  /* Don't return if in WTools */
5686  return FAILURE;
5687 #endif
5688 
5689  }
5690 
5692 
5693  return SUCCESS;
5694 }
int gradHeatMethod
int index
Definition: KSFoundation.h:1078
void ks_grad_heat_init(KS_SEQ_COLLECTION *seqcollection_p, STATUS(*play)(const INT max_encode_mode, int nargs, void **args), int nargs, void **args)
Definition: KSFoundation_host.c:5025
KS_SEQ_CONTROL * seqctrlptr[KS_MAXUNIQUE_SEQUENCES]
Definition: KSFoundation.h:1268
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:82
int hwlimitsdone
Definition: KSFoundation.h:1267
int numplayouts
Definition: KSFoundation.h:1270
void ks_grad_heat_reset()
Definition: KSFoundation_host.c:5038
KS_SEQ_HANDLE handle
Definition: KSFoundation.h:1235
int enforce_minseqseg
#define KS_THROW(format,...)
Definition: KSFoundation.h:181
int numseq
Definition: KSFoundation.h:1264

◆ ks_eval_hwlimits()

STATUS ks_eval_hwlimits ( int *  newtime,
KS_SEQ_COLLECTION seqcollection,
const LOG_GRAD *  log_grad,
STATUS(*)(const INT max_encode_mode, int nargs, void **args)  play,
int  nargs,
void **  args 
)
5852  {
5853 
5854  STATUS s;
5855 
5856  s = ks_eval_gradlimits(newtime, seqcollection, log_grad, play, nargs, args);
5857  KS_RAISE(s);
5858 
5859  int newtime_rf;
5860 
5862  play(AVERAGE_POWER, nargs, args);
5863  newtime_rf = ks_eval_rflimits(NULL, seqcollection);
5864  KS_RAISE(newtime_rf < 0 ? FAILURE : SUCCESS);
5865  *newtime = IMax(2, *newtime, newtime_rf);
5866 
5868  play(MAXIMUM_POWER, nargs, args);
5869  newtime_rf = ks_eval_rflimits(NULL, seqcollection);
5870  KS_RAISE(newtime_rf < 0 ? FAILURE : SUCCESS);
5871  *newtime = IMax(2, *newtime, newtime_rf);
5872 
5873  return SUCCESS;
5874 }
STATUS ks_eval_gradlimits(int *newtime, KS_SEQ_COLLECTION *seqcollection, const LOG_GRAD *log_grad, STATUS(*play)(const INT max_encode_mode, int nargs, void **args), int nargs, void **args)
Definition: KSFoundation_host.c:5642
#define KS_RAISE(status)
Definition: KSFoundation.h:190
int ks_eval_rflimits(KS_SAR *sar, KS_SEQ_COLLECTION *seqcollection)
Returns the time required to play out a certain number of sequence modules over some interval...
Definition: KSFoundation_host.c:5703
KS_SEQ_COLLECTION seqcollection
Definition: ksepi.e:82
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:5929

◆ ks_eval_getduration()

s64 ks_eval_getduration ( KS_SEQ_COLLECTION seqcollection,
STATUS(*)(const INT max_encode_mode, int nargs, void **args)  play,
int  nargs,
void **  args 
)
5881  {
5883  play(AVERAGE_POWER, nargs, args);
5885 }
s64 ks_eval_seqcollection_gettotalduration(KS_SEQ_COLLECTION *seqcollection)
Returns the total duration of the sequence collection
Definition: KSFoundation_host.c:5965
KS_SEQ_COLLECTION seqcollection
Definition: ksepi.e:82
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:5929

◆ ks_eval_mintr()

int ks_eval_mintr ( int  nslices,
KS_SEQ_COLLECTION seqcollection,
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_hwlimits() 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);
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]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)
5890  {
5891 
5892  /* must be run before each call to function pointer `play_loop()` to set all `seqctrl.nseqinstances` to 0 */
5894 
5895  play_loop(nslices, nargs, args); /* => seqctrl.nseqinstances = # times each seq. module has been played out */
5896 
5898 
5899 } /* ks_eval_mintr() */
s64 ks_eval_seqcollection_gettotalduration(KS_SEQ_COLLECTION *seqcollection)
Returns the total duration of the sequence collection
Definition: KSFoundation_host.c:5965
KS_SEQ_COLLECTION seqcollection
Definition: ksepi.e:82
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:5929

◆ ks_eval_maxslicespertr()

int ks_eval_maxslicespertr ( int  TR,
KS_SEQ_COLLECTION seqcollection,
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]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
5905  {
5906  (void)seqcollection;
5907 
5908  int max_slquant1 = 0;
5909  int i, acqtime;
5910  for (i = 1; i < 1024; i++) {
5911 
5912  acqtime = play_loop(i, nargs, args);
5913 
5914  if (acqtime == KS_NOTSET) {
5915  return KS_NOTSET;
5916  }
5917  if (acqtime > TR) {
5918  return max_slquant1;
5919  }
5920  max_slquant1 = i;
5921  }
5922 
5923  return max_slquant1;
5924 }
#define KS_NOTSET
Definition: KSFoundation.h:115
KS_SEQ_COLLECTION seqcollection
Definition: ksepi.e:82

◆ 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`
4921  {
4922 #ifndef IPG
4923  /* only do this on HOST */
4924 
4925  if (seqctrl == NULL) {
4926  return ks_error("%s: arg 1 is NULL", __FUNCTION__);
4927  } else if (mindur > 0 && seqctrl->ssi_time <= 0) {
4928  return ks_error("%s: %s - seqctrl.ssi_time must be > 0 before setting minimum duration", __FUNCTION__, seqctrl->description);
4929  } else if (mindur > 0 && seqctrl->ssi_time % 4) {
4930  return ks_error("%s: %s - seqctrl.ssi_time must be divisible by GRAD_UPDATE_TIME", __FUNCTION__, seqctrl->description);
4931  } else if (mindur < 0) {
4932  return ks_error("%s: %s - min duration (arg 2) must be >= 0", __FUNCTION__, seqctrl->description);
4933  } else if (mindur % 4) {
4934  return ks_error("%s: %s - min duration (arg 2) must be divisible by GRAD_UPDATE_TIME", __FUNCTION__, seqctrl->description);
4935  } else {
4936  if (mindur == 0) {
4937  seqctrl->min_duration = 0;
4938  } else {
4939  /* we add 4us (GRAD_UPDATE_TIME) to avoid waveform-after-seqcore errors */
4940  seqctrl->min_duration = RUP_GRD(mindur + seqctrl->ssi_time + GRAD_UPDATE_TIME);
4941  }
4942  seqctrl->duration = seqctrl->min_duration;
4943  }
4944 #endif
4945 
4946  return SUCCESS;
4947 }
STATUS ks_error(const char *format,...) __attribute__((format(printf
Common error message function for HOST and TGT
int duration
Definition: KSFoundation.h:1227
int min_duration
Definition: KSFoundation.h:1224
int ssi_time
Definition: KSFoundation.h:1226
KS_DESCRIPTION description
Definition: KSFoundation.h:1234

◆ 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
4952  {
4953 #ifndef IPG
4954  /* only do this on HOST */
4955 
4956  if (seqctrl == NULL) {
4957  return ks_error("%s: arg 1 is NULL", __FUNCTION__);
4958  } else if (dur < seqctrl->min_duration) {
4959  return ks_error("%s: duration (arg 2) must be >= min duration (%d", __FUNCTION__, seqctrl->min_duration);
4960  } else {
4961  seqctrl->duration = RUP_GRD(dur);
4962  }
4963 #endif
4964 
4965  return SUCCESS;
4966 }
STATUS ks_error(const char *format,...) __attribute__((format(printf
Common error message function for HOST and TGT
int duration
Definition: KSFoundation.h:1227
int min_duration
Definition: KSFoundation.h:1224

◆ 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
4971  {
4972 #ifndef IPG
4973  /* only do this on HOST */
4974 
4975  int i;
4976  if (seqcollection != NULL) {
4977  for (i = 0; i < seqcollection->numseq; i++) {
4978  if (seqcollection->seqctrlptr[i]->min_duration % 4) {
4979  return ks_error("%s: Field min_duration for sequence module #%d is not divisible by GRAD_UPDATE_TIME", __FUNCTION__, i);
4980  }
4982  }
4983  }
4984 #endif
4985 
4986  return SUCCESS;
4987 }
STATUS ks_error(const char *format,...) __attribute__((format(printf
Common error message function for HOST and TGT
int duration
Definition: KSFoundation.h:1227
KS_SEQ_CONTROL * seqctrlptr[KS_MAXUNIQUE_SEQUENCES]
Definition: KSFoundation.h:1268
int min_duration
Definition: KSFoundation.h:1224
KS_SEQ_COLLECTION seqcollection
Definition: ksepi.e:82
int numseq
Definition: KSFoundation.h:1264

◆ 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
4992  {
4993 #ifndef IPG
4994  /* only do this on HOST */
4995 
4996  int i;
4997  if (seqcollection != NULL) {
4998  for (i = 0; i < seqcollection->numseq; i++) {
4999  if (seqcollection->seqctrlptr[i]->min_duration % 4) {
5000  return ks_error("%s: Field min_duration for sequence module #%d is not divisible by GRAD_UPDATE_TIME", __FUNCTION__, i);
5001  }
5004  }
5005  }
5006  }
5007 #endif
5008 
5009  return SUCCESS;
5010 }
STATUS ks_error(const char *format,...) __attribute__((format(printf
Common error message function for HOST and TGT
int duration
Definition: KSFoundation.h:1227
KS_SEQ_CONTROL * seqctrlptr[KS_MAXUNIQUE_SEQUENCES]
Definition: KSFoundation.h:1268
int min_duration
Definition: KSFoundation.h:1224
KS_SEQ_COLLECTION seqcollection
Definition: ksepi.e:82
int numseq
Definition: KSFoundation.h:1264

◆ 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
5929  {
5930  int i, j;
5931  if (seqcollection != NULL) {
5932  for (i = 0; i < seqcollection->numseq; i++) {
5933 #ifndef IPG
5935 
5936  if (!ctrl) { continue; }
5937 
5938  ctrl->nseqinstances = 0;
5939  for (j = 0; j < ctrl->gradrf.numrf; ++j) {
5943  }
5944  for (j = 0; j < ctrl->gradrf.numtrap; ++j) {
5946  }
5947  for (j = 0; j < ctrl->gradrf.numwave; ++j) {
5949  }
5950  for (j = 0; j < ctrl->gradrf.numwait; ++j) {
5952  }
5953 #endif
5954  }
5955  for (i = 0; i < seqcollection->numplayouts; i++) {
5956  seqcollection->seqplayouts[i] = NULL;
5957  }
5959  }
5960 }
KS_RF * rfptr[KS_MAXUNIQUE_RF]
Definition: KSFoundation.h:1055
int numtrap
Definition: KSFoundation.h:1058
KS_SEQ_CONTROL * seqplayouts[KS_MAX_SEQUENCE_PLAYOUTS]
Definition: KSFoundation.h:1271
KS_RT_SCALE_LOG rtscaling
Definition: KSFoundation.h:550
int nseqinstances
Definition: KSFoundation.h:1225
KS_SEQ_CONTROL * seqctrlptr[KS_MAXUNIQUE_SEQUENCES]
Definition: KSFoundation.h:1268
KS_RT_SCALE_LOG rtscaling
Definition: KSFoundation.h:681
int numrf
Definition: KSFoundation.h:1056
KS_GRADRFCTRL gradrf
Definition: KSFoundation.h:1236
KS_RT_SCALE_LOG rtscaling
Definition: KSFoundation.h:755
KS_WAVE thetawave
Definition: KSFoundation.h:1039
int numwait
Definition: KSFoundation.h:1064
KS_WAVE * waveptr[KS_MAXUNIQUE_WAVE]
Definition: KSFoundation.h:1059
KS_WAVE rfwave
Definition: KSFoundation.h:1037
KS_WAIT * waitptr[KS_MAXUNIQUE_WAIT]
Definition: KSFoundation.h:1063
KS_TRAP * trapptr[KS_MAXUNIQUE_TRAP]
Definition: KSFoundation.h:1057
int numwave
Definition: KSFoundation.h:1060
KS_SEQ_COLLECTION seqcollection
Definition: ksepi.e:82
KS_WAVE omegawave
Definition: KSFoundation.h:1038
Definition: KSFoundation.h:1223
int numplayouts
Definition: KSFoundation.h:1270
void ks_init_rtscalelog(KS_RT_SCALE_LOG *const rtscale)
Resets a KS_RT_SCALE_LOG instance
Definition: KSFoundation_common.c:3506
int numseq
Definition: KSFoundation.h:1264

◆ 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
6620  {
6621  int i;
6622 
6623  /* duration based on the sequence collection struct */
6624  fprintf(fp, "\n----------------------------------------------\n");
6625  fprintf(fp, "Sequence modules in the sequence collection:\n");
6626  for (i = 0; i < seqcollection->numseq; i++) {
6627  fprintf(fp, "%s: %dx %d us = %u\n", seqcollection->seqctrlptr[i]->description, seqcollection->seqctrlptr[i]->nseqinstances, seqcollection->seqctrlptr[i]->duration,
6629  }
6630  fprintf(fp, "Sum of durations: %lld us\n", (s64) (ks_eval_seqcollection_gettotalduration(seqcollection)));
6631  fprintf(fp, "----------------------------------------------\n\n");
6632  fflush(fp);
6633 
6634  return;
6635 } /* ks_print_seqcollection() */
int duration
Definition: KSFoundation.h:1227
int nseqinstances
Definition: KSFoundation.h:1225
KS_SEQ_CONTROL * seqctrlptr[KS_MAXUNIQUE_SEQUENCES]
Definition: KSFoundation.h:1268
s64 ks_eval_seqcollection_gettotalduration(KS_SEQ_COLLECTION *seqcollection)
Returns the total duration of the sequence collection
Definition: KSFoundation_host.c:5965
KS_SEQ_COLLECTION seqcollection
Definition: ksepi.e:82
KS_DESCRIPTION description
Definition: KSFoundation.h:1234
int numseq
Definition: KSFoundation.h:1264

◆ 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
6659  {
6660  int i;
6661 
6662  fprintf(fp, "=============================================\n\n");
6663 
6664  if (desc != NULL) {
6665  fprintf(fp, "\nScan info %s (%d slices):\n", desc, nslices);
6666  } else {
6667  fprintf(fp, "\nScan info (%d slices):\n", nslices);
6668  }
6669  fprintf(fp, "---------------------------------------------\n");
6670  for (i = 0; i < nslices; i++) {
6671  fprintf(fp, "%.3f \t%.3f \t%.3f\n", scan_info[i].oprloc, scan_info[i].opphasoff, scan_info[i].optloc);
6672  }
6673  fprintf(fp, "=============================================\n\n");
6674 
6675 }
SCAN_INFO scan_info[]

◆ ks_eval_seqcollection_gettotalduration()

s64 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_hwlimits() for more details.

Parameters
[in]seqcollectionCollection of all sequence modules (KS_SEQ_COLLECTION). C.f. ks_eval_addtoseqcollection()
Return values
intTime in [us]
5965  {
5966  int i;
5967  s64 nettime = 0;
5968 
5969  /* duration based on the sequence collection struct */
5970  for (i = 0; i < seqcollection->numseq; i++) {
5971  /*
5972  if (seqcollection->seqctrlptr[i]->duration > 0 && seqcollection->seqctrlptr[i]->nseqinstances == 0) {
5973  ks_error("%s: Sequence module #%d was not played out in sliceloop using ks_scan_playsequence()", __FUNCTION__, i);
5974  return KS_NOTSET;
5975  }
5976  */
5977  nettime += (s64)seqcollection->seqctrlptr[i]->duration * (s64)seqcollection->seqctrlptr[i]->nseqinstances;
5978  }
5979 
5980  if (nettime == 0) {
5981  ks_error("%s: Sum of durations of sequence modules used is 0", __FUNCTION__);
5982  }
5983 
5984  return nettime;
5985 } /* 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:1227
int nseqinstances
Definition: KSFoundation.h:1225
KS_SEQ_CONTROL * seqctrlptr[KS_MAXUNIQUE_SEQUENCES]
Definition: KSFoundation.h:1268
KS_SEQ_COLLECTION seqcollection
Definition: ksepi.e:82
int numseq
Definition: KSFoundation.h:1264

◆ ks_eval_seqcollection_gettotalminduration()

s64 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_hwlimits() for more details

Parameters
[in]seqcollectionCollection of all sequence modules (KS_SEQ_COLLECTION). C.f. ks_eval_addtoseqcollection()
Return values
intTime in [us]
5988  {
5989  int i;
5990  s64 nettime = 0;
5991 
5992  /* duration based on the sequence collection struct */
5993  for (i = 0; i < seqcollection->numseq; i++) {
5994  /*
5995  if (seqcollection->seqctrlptr[i]->min_duration > 0 && seqcollection->seqctrlptr[i]->nseqinstances == 0) {
5996  ks_error("%s: Sequence module #%d was not played out in sliceloop using ks_scan_playsequence()", __FUNCTION__, i);
5997  return KS_NOTSET;
5998  }
5999  */
6001  }
6002 
6003  if (nettime == 0) {
6004  ks_error("%s: Sum of min_durations of sequence modules used is 0", __FUNCTION__);
6005  }
6006 
6007  return nettime;
6008 } /* 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:1225
KS_SEQ_CONTROL * seqctrlptr[KS_MAXUNIQUE_SEQUENCES]
Definition: KSFoundation.h:1268
int min_duration
Definition: KSFoundation.h:1224
KS_SEQ_COLLECTION seqcollection
Definition: ksepi.e:82
int numseq
Definition: KSFoundation.h:1264

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

Parameters
[out]rfpulsePointer to rfpulse[] array
[in]seqcollectionPointer to the sequence collection struct for the sequence
Return values
STATUSSUCCESS or FAILURE
6011  {
6012  int i, j, k;
6013 
6014  /* clear the activity bits for all (KS_MAXUNIQUE_RF) available RF pulse slots */
6015  for (i = 0; i < KS_MAXUNIQUE_RF; i++) {
6016  rfpulse[i].activity = 0;
6017  }
6018 
6019  i = 0;
6020  for (k = 0; k < seqcollection->numseq; k++) {
6021 
6022  for (j = 0; j < seqcollection->seqctrlptr[k]->gradrf.numrf; j++) {
6023 
6024  if (i >= KS_MAXUNIQUE_RF) {
6025  return ks_error("%s: too many RF pulses, recompilation is needed with an increased KS_MAXUNIQUE_RF",
6026  __FUNCTION__);
6027  }
6028 
6031  }
6032  }
6033 
6034  return SUCCESS;
6035 
6036 } /* ks_eval_seqcollection2rfpulse */
#define KS_MAXUNIQUE_RF
Definition: KSFoundation.h:255
KS_RF * rfptr[KS_MAXUNIQUE_RF]
Definition: KSFoundation.h:1055
RF_PULSE rfpulse[RF_FREE]
Definition: grad_rf_empty.h:55
RF_PULSE rfpulse
Definition: KSFoundation.h:1036
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:1268
int numrf
Definition: KSFoundation.h:1056
KS_GRADRFCTRL gradrf
Definition: KSFoundation.h:1236
void ks_eval_rf_relink(KS_RF *rf)
(Internal use) Relinks pointers between fields in a KS_RF object
Definition: KSFoundation_host.c:2869
KS_SEQ_COLLECTION seqcollection
Definition: ksepi.e:82
int numseq
Definition: KSFoundation.h:1264

◆ 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_hwlimits()), 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
4883  {
4884  GRAD_PULSE g;
4885 
4886  /* we have to compute it here because g.amp is a pointer */
4887  trp->heat_scaled_amp[gradchoice] = trp->amp*trp->heat_scaling_factor[gradchoice];
4888 
4889 
4890  g.ptype = G_TRAP;
4891  g.attack = &trp->ramptime;
4892  g.decay = &trp->ramptime;
4893  g.pw = &trp->plateautime;
4894  g.amps = NULL;
4895  g.amp = &trp->heat_scaled_amp[gradchoice];
4896  g.ampd = NULL;
4897  g.ampe = NULL;
4898  g.gradfile = NULL;
4899  g.num = trp->gradnum[gradchoice];
4900  g.scale = 1.0; /* scale. < 1 for phase enc. */
4901  g.time = NULL;
4902  g.tdelta = 0; /* Time delta in microseconds in between multiple occurances of the pulse */
4903  g.powscale = 1.0; /* might want to do loggrd.xfs/loggrd.tx_xyz instead? */
4904 
4905  /* the following are set by minseq***() later */
4906  g.power = 0.0;
4907  g.powpos = 0.0;
4908  g.powneg = 0.0;
4909  g.powabs = 0.0;
4910  g.amptran = 0.0;
4911  g.pwm = 1;
4912  g.bridge = 0;
4913  g.intabspwmcurr = 0;
4914 
4915  return g;
4916 }
int plateautime
Definition: KSFoundation.h:672
float heat_scaling_factor[3]
Definition: KSFoundation.h:676
float heat_scaled_amp[3]
Definition: KSFoundation.h:675
float amp
Definition: KSFoundation.h:669
int gradnum[3]
Definition: KSFoundation.h:674
int ramptime
Definition: KSFoundation.h:671

◆ ks_create_suffixed_description()

void ks_create_suffixed_description ( char *const  out,
const char *const  prefix,
const char *  suffix,
  ... 
)

◆ ks_read_header_pool()

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

ADDTITLEHERE

ADDDESCHERE

Parameters
exam_numberADDTEXTHERE
series_numberADDTEXTHERE
run_numberADDTEXTHERE
Returns
void

◆ ks_rt_scale_log_get()

float ks_rt_scale_log_get ( const KS_RT_SCALE_LOG log,
int  instance_idx,
int  playout_idx 
)
4981  {
4982 
4983  if (instance_idx < 0 || instance_idx >=log->num_instances || !log->ampscales) {
4984  return 1.0f;
4985  }
4986  if (playout_idx >=log->num_completed_playouts) {
4987  KS_THROW("playout number (%d) must be less than %d", playout_idx, log->num_completed_playouts);
4988  return 1.0f;
4989  }
4990 
4991  /* negative -> last */
4992  if (playout_idx < 0) {
4993  playout_idx = log->num_completed_playouts - 1;
4994  }
4995 
4996  const float ampscale = log->ampscales[playout_idx*log->num_instances + instance_idx];
4997  const float absscale = fabs(ampscale);
4998 
4999  if (absscale <= 0 || (absscale <= sqrt(3) && absscale > 1.0f/(1 << 15))) {
5000  return ampscale;
5001  }
5002 
5003  /* If it is too big, too small (but non zero) or NaN we should end up here.
5004  This check is necessary because entries might be left uninitialized if
5005  no scaling is applied during the relevant playout. */
5006  KS_THROW("abnormal amplitude scale (%f), maybe it was not set", ampscale);
5007  return 1.0f;
5008 }
int num_completed_playouts
Definition: KSFoundation.h:477
int num_instances
Definition: KSFoundation.h:476
float * ampscales
Definition: KSFoundation.h:472
#define KS_THROW(format,...)
Definition: KSFoundation.h:181

◆ ks_rt_scale_log_get_state()

int ks_rt_scale_log_get_state ( const KS_RT_SCALE_LOG log,
int  instance_idx,
int  playout_idx 
)
5013  {
5014 
5015  if (instance_idx < 0 || instance_idx >=log->num_instances) {
5016  return 0;
5017  }
5018  if (playout_idx >=log->num_completed_playouts) {
5019  KS_THROW("playout number (%d) must be less than %d", playout_idx, log->num_completed_playouts);
5020  return 0;
5021  }
5022 
5023  /* negative -> last */
5024  if (playout_idx < 0) {
5025  playout_idx = log->num_completed_playouts - 1;
5026  }
5027 
5028  return log->states[playout_idx*log->num_instances + instance_idx];
5029 }
int num_completed_playouts
Definition: KSFoundation.h:477
int num_instances
Definition: KSFoundation.h:476
int * states
Definition: KSFoundation.h:473
#define KS_THROW(format,...)
Definition: KSFoundation.h:181

◆ ks_rt_scale_log_set()

void ks_rt_scale_log_set ( KS_RT_SCALE_LOG log,
int  instance_idx,
float  ampscale 
)
4890  {
4891 
4892  if (!log) {
4893  KS_THROW("null input");
4894  return;
4895  }
4896 
4897  if (log->num_completed_playouts > 0 && log->num_instances < 1) {
4898  KS_THROW("inconsistent state");
4899  return;
4900  }
4901 
4902  if (instance_idx < 0 || instance_idx >=log->num_instances) {
4903  KS_THROW("instance number (%d) out of range [0,%d]", instance_idx, log->num_instances);
4904  return;
4905  }
4906 
4907  const int prev_buffer_size = log->buffer_size;
4908  log->buffer_size = (log->num_completed_playouts + 1)*log->num_instances;
4909 
4910  float *p = (float*)realloc(log->ampscales, log->buffer_size*sizeof(float));
4911  if (!p) {
4912  KS_THROW("Reallocation failed");
4913  return;
4914  }
4915 
4916  log->ampscales = p;
4917  int i = prev_buffer_size;
4918  for (; i < log->buffer_size; i++) {
4919  /* Initialize to the last used ampscale for the same instance, if available */
4920  const int last_idx = i - log->num_instances;
4921  log->ampscales[i] = last_idx < 0 ? 1.0f : log->ampscales[last_idx];
4922  }
4923  log->ampscales[log->num_completed_playouts*log->num_instances + instance_idx] = ampscale;
4924 }
int num_completed_playouts
Definition: KSFoundation.h:477
int buffer_size
Definition: KSFoundation.h:474
int num_instances
Definition: KSFoundation.h:476
float * ampscales
Definition: KSFoundation.h:472
#define KS_THROW(format,...)
Definition: KSFoundation.h:181

◆ ks_rt_scale_log_set_state()

void ks_rt_scale_log_set_state ( KS_RT_SCALE_LOG log,
int  instance_idx,
int  state 
)
4929  {
4930 
4931  if (!log) {
4932  KS_THROW("null input");
4933  return;
4934  }
4935 
4936  if (log->num_completed_playouts > 0 && log->num_instances < 1) {
4937  KS_THROW("inconsistent state");
4938  return;
4939  }
4940 
4941  if (instance_idx < 0 || instance_idx >=log->num_instances) {
4942  KS_THROW("instance number (%d) out of range [0,%d]", instance_idx, log->num_instances);
4943  return;
4944  }
4945 
4946  const int prev_buffer_size = log->buffer_size_states;
4947  log->buffer_size_states = (log->num_completed_playouts + 1) * log->num_instances;
4948 
4949  int* p = (int*)realloc(log->states, log->buffer_size_states * sizeof(int));
4950  if (!p) {
4951  KS_THROW("Reallocation failed");
4952  return;
4953  }
4954 
4955  log->states = p;
4956  int i = prev_buffer_size;
4957  for (; i < log->buffer_size_states; i++) {
4958  /* Initialize to the last used state for the same instance, if available */
4959  const int last_idx = i - log->num_instances;
4960  log->states[i] = last_idx < 0 ? 0 : log->states[last_idx];
4961  }
4962 
4963  log->states[log->num_completed_playouts * log->num_instances + instance_idx] = state;
4964 }
int num_completed_playouts
Definition: KSFoundation.h:477
int num_instances
Definition: KSFoundation.h:476
int * states
Definition: KSFoundation.h:473
int buffer_size_states
Definition: KSFoundation.h:475
#define KS_THROW(format,...)
Definition: KSFoundation.h:181

◆ ks_rt_scale_log_next()

void ks_rt_scale_log_next ( KS_RT_SCALE_LOG log)
4969  {
4970  if (!log) {
4971  KS_THROW("null input");
4972  return;
4973  }
4974 
4975  log->num_completed_playouts = IMax(2, log->num_completed_playouts + 1, 1);
4976 }
int num_completed_playouts
Definition: KSFoundation.h:477
#define KS_THROW(format,...)
Definition: KSFoundation.h:181

◆ 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
6128  {
6129  n--;
6130  n |= n >> 1;
6131  n |= n >> 2;
6132  n |= n >> 4;
6133  n |= n >> 8;
6134  n |= n >> 16;
6135  n++;
6136  return n;
6137 }

◆ 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
6142  {
6143  return (((val + 999) / 1000) * 1000);
6144 }

◆ 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
6149  {
6150 
6151  echortf->decimation = tsp / 2;
6152  echortf->bw = 1.0e3 / (tsp * 2.0);
6153  echortf->tsp = tsp; /* dwell time [us] */
6154  echortf->tdaq = duration;
6155  echortf->outputs = duration / echortf->tsp;
6156  echortf->fslot = KS_NOTSET; /* set this to an unusable value so we are forced to call setfilter() or GEReq_predownload_setfilter() */
6157  echortf->prefills = 0;
6158  echortf->taps = -1;
6159 
6160  if ((floor(echortf->tsp) < echortf->tsp) || (duration % (int) echortf->tsp)) {
6161  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");
6162  }
6163  if (echortf->outputs % 2) {
6164  return ks_error("ks_calc_filter: the number of sample outputs must be even");
6165  }
6166  return SUCCESS;
6167 }
#define KS_NOTSET
Definition: KSFoundation.h:115
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
6197  {
6198  int tsp;
6199  int sysmintsp = 2;
6200 
6201  tsp = ((int) floor((float) HALF_KHZ_USEC / bw / (float) sysmintsp + 0.5)) * sysmintsp;
6202 
6203  if (tsp < sysmintsp)
6204  tsp = sysmintsp;
6205 
6206  return tsp;
6207 }
#define HALF_KHZ_USEC
Definition: KSFoundation.h:236

◆ 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]
6212  {
6213 
6214  return ( (float) HALF_KHZ_USEC / (float) tsp );
6215 
6216 }
#define HALF_KHZ_USEC
Definition: KSFoundation.h:236

◆ 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]
6272  {
6273  float ramp_area = trap->amp * trap->ramptime / 2.0;
6274  float plateau_area = trap->amp * trap->plateautime;
6275  float slewrate = trap->amp / trap->ramptime;
6276  int time2area;
6277  if (area < ramp_area) {
6278  /* Target area reached on ramp */
6279  time2area = (int) sqrtf(area * 2.0 / slewrate);
6280  } else if (area < (ramp_area + plateau_area)) {
6281  /* Target area reached on plateau*/
6282  float area_remaining = area - ramp_area;
6283  time2area = trap->ramptime + (area_remaining / trap->amp);
6284  } else {
6285  /* Target area reached on ramp down */
6286  float area_remaining = area - ramp_area - plateau_area;
6287  time2area = (trap->amp - sqrtf(trap->amp*trap->amp - 2*area_remaining*slewrate) ) / slewrate;
6288  }
6289  return time2area;
6290 }
int plateautime
Definition: KSFoundation.h:672
float amp
Definition: KSFoundation.h:669
int ramptime
Definition: KSFoundation.h:671

◆ 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]
6221  {
6222  int tsp;
6223  if (bw > 0) {
6224  tsp = (int) ks_calc_bw2tsp(bw); /* convert rBW to dwell time [us] */
6225  return (ks_calc_tsp2bw(tsp)); /* round rBW to exacly match the dwell time */
6226  } else {
6227  return bw;
6228  }
6229 }
float ks_calc_tsp2bw(int tsp)
Convert dwell time to receiver bandwidth
Definition: KSFoundation_host.c:6212
int ks_calc_bw2tsp(float bw)
Convert receiver bandwidth to dwell time
Definition: KSFoundation_host.c:6197

◆ ks_calc_lower_rbw()

float ks_calc_lower_rbw ( float  rbw)

ADDTITLEHERE

ADDDESCHERE

Parameters
[in]rbwADDTEXTHERE
Return values
floatADDTEXTHERE
6234  {
6235  if (isNotSet(rbw)) {
6236  return rbw;
6237  }
6238  int tsp = ks_calc_bw2tsp(rbw);
6239  return ks_calc_nearestbw(500.0f / (tsp + 2));
6240 }
float ks_calc_nearestbw(float bw)
Round receiver bandwidth to nearest valid value
Definition: KSFoundation_host.c:6221
int ks_calc_bw2tsp(float bw)
Convert receiver bandwidth to dwell time
Definition: KSFoundation_host.c:6197
#define isNotSet(a)
Definition: KSFoundation.h:164

◆ ks_calc_higher_rbw()

float ks_calc_higher_rbw ( float  rbw)

ADDTITLEHERE

ADDDESCHERE

Parameters
[in]rbwADDTEXTHERE
Return values
floatADDTEXTHERE
6245  {
6246  int tsp = ks_calc_bw2tsp(rbw);
6247  if (tsp == 2 || isNotSet(rbw)) {
6248  return rbw;
6249  } else {
6250  return ks_calc_nearestbw(500.0f / (tsp - 2));
6251  }
6252 }
float ks_calc_nearestbw(float bw)
Round receiver bandwidth to nearest valid value
Definition: KSFoundation_host.c:6221
int ks_calc_bw2tsp(float bw)
Convert receiver bandwidth to dwell time
Definition: KSFoundation_host.c:6197
#define isNotSet(a)
Definition: KSFoundation.h:164

◆ 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

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]
6257  {
6258  float max_rbw = -1.0f;
6259  int dwelltime;
6260  for (dwelltime = 2; max_rbw < 0.0; dwelltime += 2) {
6261  float required_amp = 1.0/(GAM * fov * 0.1 * dwelltime * 0.000001);
6262  if (required_amp <= ampmax) {
6263  max_rbw = 500.0f / ((float) dwelltime);
6264  }
6265  }
6266  return max_rbw;
6267 }

◆ 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
459  {
460  float minfov;
461 
462  minfov = ((float) (2 * (1.0e3 / (tsp * 2.0)) * 1000 / GAM) * (10.0 / ampmax));
463 
464  return minfov;
465 }

◆ 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]
470  {
471  return (bw / (ks_syslimits_ampmax(loggrd) * (0.1) * GAM));
472 }
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:235

◆ 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
477  {
478  int mintsp;
479  int sysmintsp = 2;
480 
481  mintsp = (int) ceil(1.0e3 / (fov / ((float) (2 * 1000 / GAM) * (10.0 / ampmax)) * 2.0));
482 
483  if (mintsp < sysmintsp)
484  mintsp = mintsp;
485 
486  return mintsp;
487 }

◆ 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]
492  {
493  if (fov > 0) {
494  /* G/cm * us. Area needed to move one pixel in k-space corresponding to the input FOV */
495  return (1.0e6 / (fov / 10.0 * GAM));
496  } else {
497  return (0.0);
498  }
499 }

◆ ks_phaseencoding_memorypool_init()

STATUS ks_phaseencoding_memorypool_init ( KS_PHASEENCODING_COORD entries,
uint64_t  size 
)

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

Parameters
[in]entriesPointer to KS_PHASEENCODING_COORD array with phase encoding coordinates
[in]sizeSize of memory pool
Returns
void
580  {
581 #ifdef IPG
585 #else
590 #endif
591  return SUCCESS;
592 }
int is_cleared_on_tgt
Definition: KSFoundation_common.c:573
struct _phaseencoding_coord_pool_s _phaseencoding_coord_pool
Definition: KSFoundation_common.c:577
uint64_t size
Definition: KSFoundation_common.c:569
KS_PHASEENCODING_COORD * entries
Definition: KSFoundation_common.c:571
uint64_t used_size
Definition: KSFoundation_common.c:570
uint32_t epoch
Definition: KSFoundation_common.c:572

◆ ks_init_dynamic()

void ks_init_dynamic ( KS_DYNAMIC_STATE *  dynamic)

Initialise the dynamic structure

It is important to have dynamic in a known state at the start of any scan function.

Parameters
dynamicThe current state of the scan (used by ksscan)
Returns
void
504  {
505  KS_MAT4x4 physical;
506  memcpy(physical, dynamic->Mphysical, sizeof(KS_MAT4x4));
507  KS_DYNAMIC_STATE defdynamic = KS_INIT_DYNAMIC_STATE;
508  *dynamic = defdynamic;
509  memcpy(dynamic->Mphysical, physical, sizeof(KS_MAT4x4));
510 }
double KS_MAT4x4[16]
Definition: KSFoundation.h:356
#define KS_INIT_DYNAMIC_STATE
Definition: KSFoundation.h:1829

◆ ks_phaseencoding_isvalid()

int ks_phaseencoding_isvalid ( const KS_PHASEENCODING_PLAN phaseenc_plan_ptr)

ADDTITLEHERE

ADDDESCHERE

Parameters
phaseenc_plan_ptrADDTEXTHERE
Return values
intADDTEXTHERE
786  {
787 
788  const int valid_epoch =
789 #ifndef IPG
790  phaseenc_plan_ptr->epoch == _phaseencoding_coord_pool.epoch;
791 #else
792  1;
793 #endif
794 
795  return (!phaseenc_plan_ptr &&
796  phaseenc_plan_ptr->encodes_per_shot > 0 &&
797  phaseenc_plan_ptr->num_shots > 0 &&
798  valid_epoch);
799 }
int num_shots
Definition: KSFoundation.h:1789
struct _phaseencoding_coord_pool_s _phaseencoding_coord_pool
Definition: KSFoundation_common.c:577
int encodes_per_shot
Definition: KSFoundation.h:1788
uint32_t epoch
Definition: KSFoundation.h:1792
uint32_t epoch
Definition: KSFoundation_common.c:572

◆ ks_phaseencoding_get()

KS_PHASEENCODING_COORD ks_phaseencoding_get ( const KS_PHASEENCODING_PLAN phaseenc_plan_ptr,
int  encode,
int  shot 
)

Get [ky,kz] coordinate from KS_PHASEENCODING_PLAN for given encode and shot

If the .ky and .kz values returned by this function is = KS_NOTSET (-1), it indicates that this encode/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]encodePhase encoding index in the sequence echo train in range [0,encodes_per_shot-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>
597  {
598 
599 #ifdef IPG
600  int oktoget = (phaseenc_plan_ptr != NULL) && _phaseencoding_coord_pool.is_cleared_on_tgt;
601 #else
602  int oktoget = (phaseenc_plan_ptr != NULL) && phaseenc_plan_ptr->epoch == _phaseencoding_coord_pool.epoch;
603 #endif
604 
605  if (!oktoget ||
606  phaseenc_plan_ptr->encodes_per_shot == KS_NOTSET ||
607  !(phaseenc_plan_ptr->entries_offset) ||
608  shot >= phaseenc_plan_ptr->num_shots ||
609  encode < 0 || encode >= phaseenc_plan_ptr->encodes_per_shot) {
610 
612  return fake_coords;
613  }
614 
615  const int shot_start_index = phaseenc_plan_ptr->entries_offset - 1 + phaseenc_plan_ptr->encodes_per_shot * shot;
616 
617  return _phaseencoding_coord_pool.entries[shot_start_index + encode];
618 }
#define KS_INIT_PHASEENCODING_COORD
Definition: KSFoundation.h:306
int num_shots
Definition: KSFoundation.h:1789
int is_cleared_on_tgt
Definition: KSFoundation_common.c:573
uint32_t entries_offset
Definition: KSFoundation.h:1790
#define KS_NOTSET
Definition: KSFoundation.h:115
struct _phaseencoding_coord_pool_s _phaseencoding_coord_pool
Definition: KSFoundation_common.c:577
Struct holding a 3D k-space phase encoding location (ky,kz)
Definition: KSFoundation.h:1749
int encodes_per_shot
Definition: KSFoundation.h:1788
KS_PHASEENCODING_COORD * entries
Definition: KSFoundation_common.c:571
uint32_t epoch
Definition: KSFoundation.h:1792
uint32_t epoch
Definition: KSFoundation_common.c:572

◆ ks_phaseencoding_get_shot()

KS_PHASEENCODING_SHOTCOORDS ks_phaseencoding_get_shot ( const KS_PHASEENCODING_PLAN phaseenc_plan_ptr,
int  shot 
)

ADDTITLEHERE

ADDDESCHERE

Parameters
phaseenc_plan_ptrADDTEXTHERE
[in]shotADDTEXTHERE
Return values
KS_PHASEENCODING_SHOTCOORDSADDTEXTHERE
623  {
624 
625 #ifdef IPG
626  int oktoget = (phaseenc_plan_ptr != NULL) && _phaseencoding_coord_pool.is_cleared_on_tgt;
627 #else
628  int oktoget = (phaseenc_plan_ptr != NULL) && phaseenc_plan_ptr->epoch == _phaseencoding_coord_pool.epoch;
629 #endif
630 
631  if (!oktoget ||
632  phaseenc_plan_ptr->encodes_per_shot == KS_NOTSET ||
633  !(phaseenc_plan_ptr->entries_offset) ||
634  shot < 0 ||
635  shot >= phaseenc_plan_ptr->num_shots) {
636 
638  return fake_shot;
639  }
640 
641  const int shot_start_index = phaseenc_plan_ptr->entries_offset - 1 + phaseenc_plan_ptr->encodes_per_shot * shot;
642 
643  KS_PHASEENCODING_SHOTCOORDS shot_coords = {
644  phaseenc_plan_ptr->encodes_per_shot,
645  _phaseencoding_coord_pool.entries + shot_start_index
646  };
647  return shot_coords;
648 }
int num_shots
Definition: KSFoundation.h:1789
int is_cleared_on_tgt
Definition: KSFoundation_common.c:573
ADDTITLEHERE
Definition: KSFoundation.h:1773
uint32_t entries_offset
Definition: KSFoundation.h:1790
#define KS_NOTSET
Definition: KSFoundation.h:115
struct _phaseencoding_coord_pool_s _phaseencoding_coord_pool
Definition: KSFoundation_common.c:577
int encodes_per_shot
Definition: KSFoundation.h:1788
KS_PHASEENCODING_COORD * entries
Definition: KSFoundation_common.c:571
#define KS_INIT_PHASEENCODING_SHOTCOORDS
Definition: KSFoundation.h:1778
uint32_t epoch
Definition: KSFoundation.h:1792
uint32_t epoch
Definition: KSFoundation_common.c:572

◆ ks_repeat_peplan()

STATUS ks_repeat_peplan ( KS_PHASEENCODING_PLAN peplan,
const KS_PHASEENCODING_REPEAT_DESIGN *const  repeat 
)

ADDTITLEHERE

ADDDESCHERE

Parameters
peplanADDTEXTHERE
repeatADDTEXTHERE
Return values
STATUSADDTEXTHERE
8143  {
8144  if (repeat->num_repeats < 2 || repeat->mode == NO_REPEAT) {
8145  return SUCCESS;
8146  }
8147 
8148  /* Figure out size of new plan */
8149  int new_encodes_per_shot = peplan->encodes_per_shot;
8150  int new_num_shots = peplan->num_shots;
8151  switch (repeat->mode) {
8153  case SEQUENTIAL_LONGER_SHOTS: {
8154  new_encodes_per_shot *= repeat->num_repeats;
8155  break;
8156  }
8158  case SEQUENTIAL_EXTRA_SHOTS: {
8159  new_num_shots *= repeat->num_repeats;
8160  break;
8161  }
8162  default:
8163  return KS_THROW("Repeat mode (%d) not supported", (int)repeat->mode);
8164  break;
8165  }
8166 
8167  /* Allocate new plan */
8168  KS_PHASEENCODING_PLAN new_peplan = *peplan;
8169  new_peplan.encodes_per_shot = new_encodes_per_shot;
8170  new_peplan.num_shots = new_num_shots;
8171  STATUS status = ks_phaseencoding_alloc(&new_peplan, new_encodes_per_shot, new_num_shots);
8172  KS_RAISE(status);
8173 
8174  /* Copy according to mode */
8175  int shot, encode, rep;
8176  for (shot = 0; shot < peplan->num_shots; shot++) {
8177  for (encode = 0; encode < peplan->encodes_per_shot; encode++) {
8178  KS_PHASEENCODING_COORD coord = ks_phaseencoding_get(peplan, encode, shot);
8179 
8180  int encode_index = encode;
8181  int shot_index = shot;
8182  for (rep = 0; rep < repeat->num_repeats; rep++) {
8183  switch (repeat->mode) {
8184  case INTERLEAVED_LONGER_SHOTS: {
8185  encode_index = encode * repeat->num_repeats + rep;
8186  break;
8187  } case SEQUENTIAL_LONGER_SHOTS: {
8188  encode_index = encode + peplan->encodes_per_shot * rep;
8189  break;
8190  } case INTERLEAVED_EXTRA_SHOTS: {
8191  shot_index = shot * repeat->num_repeats + rep;
8192  break;
8193  } case SEQUENTIAL_EXTRA_SHOTS: {
8194  shot_index = shot + peplan->num_shots * rep;
8195  break;
8196  } default: {
8197  return KS_THROW("Repeat mode (%d) not implemented", (int)repeat->mode);
8198  break;
8199  }
8200  }
8201 
8202  ks_phaseencoding_set(&new_peplan, encode_index, shot_index, coord.ky, coord.kz);
8203  }
8204  }
8205  }
8206  *peplan = new_peplan;
8207  return SUCCESS;
8208 }
KS_PHASEENCODING_COORD ks_phaseencoding_get(const KS_PHASEENCODING_PLAN *phaseenc_plan_ptr, int encode, int shot)
Get [ky,kz] coordinate from KS_PHASEENCODING_PLAN for given encode and shot
Definition: KSFoundation_common.c:597
Definition: KSFoundation.h:2245
int num_shots
Definition: KSFoundation.h:1789
ADDTITLEHERE
Definition: KSFoundation.h:1787
STATUS ks_phaseencoding_alloc(KS_PHASEENCODING_PLAN *phaseenc_plan_ptr, int etl, int num_shots) WARN_UNUSED_RESULT
Reallocate memory for KS_PHASEENCODING_PLAN entries
Definition: KSFoundation_common.c:746
void ks_phaseencoding_set(KS_PHASEENCODING_PLAN *phaseenc_plan_ptr, int encode, int shot, int ky, int kz)
Set [ky,kz] coordinate from KS_PHASEENCODING_PLAN for given encode and shot
Definition: KSFoundation_common.c:653
s16 ky
Definition: KSFoundation.h:1750
Struct holding a 3D k-space phase encoding location (ky,kz)
Definition: KSFoundation.h:1749
int encodes_per_shot
Definition: KSFoundation.h:1788
Definition: KSFoundation.h:2247
KS_REPEAT_MODE mode
Definition: KSFoundation.h:2258
#define KS_RAISE(status)
Definition: KSFoundation.h:190
Definition: KSFoundation.h:2246
s16 kz
Definition: KSFoundation.h:1751
Definition: KSFoundation.h:2244
#define KS_THROW(format,...)
Definition: KSFoundation.h:181
Definition: KSFoundation.h:2243
int num_repeats
Definition: KSFoundation.h:2259

◆ ks_phaseencoding_set()

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

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

If the .ky and .kz values passed to this function is = KS_NOTSET (-1), it indicates that this encode/shot combination should have zero phase encoding and should be ignored. However, ignored encode/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 encode/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]encodePhase encoding index in the sequence echo train in range [0,encodes_per_shot-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
653  {
654 
655 #ifdef IPG
656  int oktoset = (phaseenc_plan_ptr != NULL) && _phaseencoding_coord_pool.is_cleared_on_tgt;
657 #else
658  int oktoset = (phaseenc_plan_ptr != NULL) && phaseenc_plan_ptr->epoch == _phaseencoding_coord_pool.epoch;
659 #endif
660 
661  if (!oktoset ||
662  phaseenc_plan_ptr->encodes_per_shot == KS_NOTSET ||
663  !(phaseenc_plan_ptr->entries_offset) ||
664  shot >= phaseenc_plan_ptr->num_shots ||
665  encode < 0 || encode >= phaseenc_plan_ptr->encodes_per_shot) {
666  return;
667  }
668 
669 
670  const int shot_start_index = phaseenc_plan_ptr->entries_offset - 1 + phaseenc_plan_ptr->encodes_per_shot * shot;
671 
672  _phaseencoding_coord_pool.entries[shot_start_index + encode].ky = ky;
673  _phaseencoding_coord_pool.entries[shot_start_index + encode].kz = kz;
674 }
int num_shots
Definition: KSFoundation.h:1789
int is_cleared_on_tgt
Definition: KSFoundation_common.c:573
uint32_t entries_offset
Definition: KSFoundation.h:1790
#define KS_NOTSET
Definition: KSFoundation.h:115
struct _phaseencoding_coord_pool_s _phaseencoding_coord_pool
Definition: KSFoundation_common.c:577
s16 ky
Definition: KSFoundation.h:1750
int encodes_per_shot
Definition: KSFoundation.h:1788
KS_PHASEENCODING_COORD * entries
Definition: KSFoundation_common.c:571
s16 kz
Definition: KSFoundation.h:1751
uint32_t epoch
Definition: KSFoundation.h:1792
uint32_t epoch
Definition: KSFoundation_common.c:572

◆ 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
700  {
702  int shot, encode;
703 #ifdef IPG
704 #define PHASEENCODING_PLAN_FILENAME "ks_phaseencodingtable.tgt.txt"
705 #else
706 #define PHASEENCODING_PLAN_FILENAME "ks_phaseencodingtable.host.txt"
707 #endif
708 
709 #ifdef PSD_HW
710  FILE *fp = fopen("/usr/g/mrraw/" PHASEENCODING_PLAN_FILENAME, "w");
711 #else
712  FILE *fp = fopen("./" PHASEENCODING_PLAN_FILENAME, "w");
713 #endif
714 
715  fprintf(fp, "numshots (vertical): %d, etl (horizontal): %d\n\n", phaseenc_plan_ptr->num_shots, phaseenc_plan_ptr->encodes_per_shot);
716 
717  if (phaseenc_plan_ptr->encodes_per_shot == KS_NOTSET) {
718  fclose(fp);
719  return; /* we haven't set it up yet */
720  }
721 
722  for (shot = 0; shot < phaseenc_plan_ptr->num_shots; shot++) {
723  for (encode = 0; encode < phaseenc_plan_ptr->encodes_per_shot; encode++) {
724  coord = ks_phaseencoding_get(phaseenc_plan_ptr, encode, shot);
725  if (coord.ky != KS_NOTSET) {
726  fprintf(fp, "[%03d", coord.ky);
727  } else {
728  fprintf(fp, "[***");
729  }
730  if (coord.kz != KS_NOTSET) {
731  fprintf(fp, ",%03d] ", coord.kz);
732  } else {
733  fprintf(fp, ",***] ");
734  }
735  }
736  fprintf(fp,"\n");
737  }
738 
739  fclose(fp);
740 
741 }
#define KS_INIT_PHASEENCODING_COORD
Definition: KSFoundation.h:306
int num_shots
Definition: KSFoundation.h:1789
#define KS_NOTSET
Definition: KSFoundation.h:115
s16 ky
Definition: KSFoundation.h:1750
Struct holding a 3D k-space phase encoding location (ky,kz)
Definition: KSFoundation.h:1749
int encodes_per_shot
Definition: KSFoundation.h:1788
#define PHASEENCODING_PLAN_FILENAME
s16 kz
Definition: KSFoundation.h:1751
KS_PHASEENCODING_COORD ks_phaseencoding_get(const KS_PHASEENCODING_PLAN *phaseenc_plan_ptr, int encode, int shot)
Get [ky,kz] coordinate from KS_PHASEENCODING_PLAN for given encode and shot
Definition: KSFoundation_common.c:597

◆ ks_phaseencoding_alloc()

STATUS ks_phaseencoding_alloc ( 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
746  {
747  int i;
748  if (_phaseencoding_coord_pool.size <= 0) {
749  return KS_THROW("Phase encoding memory pool has not been allocated. Did you forget to call ks_phaseencoding_memorypool_init() ?");
750  }
751  if (!phaseenc_plan_ptr) {
752  return ks_error("%s: phaseenc_plan_ptr is NULL", __FUNCTION__);
753  }
754 
755  if (etl < 1 || num_shots < 1) {
756  return KS_THROW("Both etl (%d) and num_shots (%d) needs to be positive", etl, num_shots);
757  }
758 
759  const uint64_t size = etl * num_shots;
760 
762  return KS_THROW("Memory pool full (size is %llu), adjust its size to be at least %llu entries"
763  " with ks_phaseencoding_memorypool_init()",
766  }
767 
768  phaseenc_plan_ptr->entries_offset = _phaseencoding_coord_pool.used_size + 1;
770 
771  phaseenc_plan_ptr->encodes_per_shot = etl;
772  phaseenc_plan_ptr->num_shots = num_shots;
773  phaseenc_plan_ptr->epoch = _phaseencoding_coord_pool.epoch;
774 
775  for (i = 0; i < etl * num_shots; i++) {
776  _phaseencoding_coord_pool.entries[phaseenc_plan_ptr->entries_offset - 1 + i].ky = KS_NOTSET;
777  _phaseencoding_coord_pool.entries[phaseenc_plan_ptr->entries_offset - 1 + i].kz = KS_NOTSET;
778  }
779 
780  return SUCCESS;
781 }
int num_shots
Definition: KSFoundation.h:1789
uint32_t entries_offset
Definition: KSFoundation.h:1790
#define KS_NOTSET
Definition: KSFoundation.h:115
struct _phaseencoding_coord_pool_s _phaseencoding_coord_pool
Definition: KSFoundation_common.c:577
uint64_t size
Definition: KSFoundation_common.c:569
STATUS ks_error(const char *format,...)
Definition: KSFoundation_common.c:78
s16 ky
Definition: KSFoundation.h:1750
int encodes_per_shot
Definition: KSFoundation.h:1788
KS_PHASEENCODING_COORD * entries
Definition: KSFoundation_common.c:571
s16 kz
Definition: KSFoundation.h:1751
uint32_t epoch
Definition: KSFoundation.h:1792
uint64_t used_size
Definition: KSFoundation_common.c:570
#define KS_THROW(format,...)
Definition: KSFoundation.h:181
uint32_t epoch
Definition: KSFoundation_common.c:572

◆ 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,
int  encodes_per_shot 
)

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)
[in]encodes_per_shotHow many readouts are acquired for each shot
Returns
void
902  {
903  int etl = encodes_per_shot;
904  int num_shots;
905  int shot, ky, kz;
906  int encode;
907  STATUS status;
908 
909  strncpy(phaseenc_plan_ptr->description, desc, KS_DESCRIPTION_LENGTH - 1);
910  phaseenc_plan_ptr->description[KS_DESCRIPTION_LENGTH - 1] = 0;
911 
912  /* don't allow empty description or a description with leading space */
913  if (phaseenc_plan_ptr->description == NULL || phaseenc_plan_ptr->description[0] == ' ') {
914  return ks_error("%s: description of phase encoding plan (2nd arg) cannot be NULL or leading space", __FUNCTION__);
915  }
916 
917  num_shots = (zphaser != NULL) ? (phaser->numlinestoacq * zphaser->numlinestoacq) : phaser->numlinestoacq;
918 
919  status = ks_phaseencoding_alloc(phaseenc_plan_ptr, etl, num_shots);
920  KS_RAISE(status);
921 
922  shot = 0;
923  if (zphaser != NULL) { /* 3D */
924  for (kz = 0; kz < zphaser->numlinestoacq; kz++) {
925  for (ky = 0; ky < phaser->numlinestoacq / encodes_per_shot; ky++) {
926  for (encode = 0; encode < encodes_per_shot; encode++) {
927  ks_phaseencoding_set(phaseenc_plan_ptr, encode, shot++, phaser->linetoacq[ky], zphaser->linetoacq[kz]);
928  }
929  }
930  }
931  } else { /* 2D */
932  for (ky = 0; ky < phaser->numlinestoacq; ky++) {
933  ks_phaseencoding_set(phaseenc_plan_ptr, 0, shot++, phaser->linetoacq[ky], KS_NOTSET);
934  }
935  }
936 
937  return SUCCESS;
938 }
void ks_phaseencoding_set(KS_PHASEENCODING_PLAN *phaseenc_plan_ptr, int encode, int shot, int ky, int kz)
Set [ky,kz] coordinate from KS_PHASEENCODING_PLAN for given encode and shot
Definition: KSFoundation_common.c:653
#define KS_NOTSET
Definition: KSFoundation.h:115
int numlinestoacq
Definition: KSFoundation.h:1727
STATUS ks_error(const char *format,...)
Definition: KSFoundation_common.c:78
int linetoacq[KS_MAX_PHASEDYN]
Definition: KSFoundation.h:1728
#define KS_DESCRIPTION_LENGTH
Definition: KSFoundation.h:253
KS_DESCRIPTION description
Definition: KSFoundation.h:1791
#define KS_RAISE(status)
Definition: KSFoundation.h:190
STATUS ks_phaseencoding_alloc(KS_PHASEENCODING_PLAN *phaseenc_plan_ptr, int etl, int num_shots)
Reallocate memory for KS_PHASEENCODING_PLAN entries
Definition: KSFoundation_common.c:746

◆ 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
943  {
944  int etl = 1;
945  int ky, kz, i;
946  float radius_ky, radius_kz, center_ky, center_kz;
947  float x1, x2;
948  STATUS status;
949  int inellipse; /* Number of ky,kz points in the ellipse */
950 
951  strncpy(phaseenc_plan_ptr->description, desc, KS_DESCRIPTION_LENGTH - 1);
952  phaseenc_plan_ptr->description[KS_DESCRIPTION_LENGTH - 1] = 0;
953 
954  /* don't allow empty description or a description with leading space */
955  if (phaseenc_plan_ptr->description == NULL || phaseenc_plan_ptr->description[0] == ' ') {
956  return ks_error("%s: description of phase encoding plan (2nd arg) cannot be NULL or leading space", __FUNCTION__);
957  }
958 
959  if (zphaser == NULL) { /* We can't do ellipse k-space coverage (ky,kz) in 2D */
960  return ks_phaseencoding_generate_simple(phaseenc_plan_ptr, desc, phaser, NULL, 1);
961  }
962 
963 
964  radius_ky = phaser->res/2.0;
965  center_ky = phaser->res/2.0 - 0.5;
966 
967  radius_kz = zphaser->res/2.0;
968  center_kz = zphaser->res/2.0 - 0.5;
969 
970  for (i = 0; i < 2; i++) { /* i = 0: check how many. i = 1: set up coords */
971 
972  if (i == 1) {
973  status = ks_phaseencoding_alloc(phaseenc_plan_ptr, etl, inellipse);
974  KS_RAISE(status);
975  }
976  inellipse = 0;
977 
978  for (kz = 0; kz < zphaser->numlinestoacq; kz++) {
979  for (ky = 0; ky < phaser->numlinestoacq; ky++) {
980  x1 = (phaser->linetoacq[ky] - center_ky) / (float) radius_ky;
981  x2 = (zphaser->linetoacq[kz] - center_kz) / (float) radius_kz;
982  if ((x1 * x1 + x2 * x2) <= 1.0) {
983  if (i == 1) {
984  ks_phaseencoding_set(phaseenc_plan_ptr, 0, inellipse, phaser->linetoacq[ky], zphaser->linetoacq[kz]);
985  }
986  inellipse++;
987  }
988  } /* ky */
989  } /* kz */
990 
991  } /* i */
992 
993  return SUCCESS;
994 }
void ks_phaseencoding_set(KS_PHASEENCODING_PLAN *phaseenc_plan_ptr, int encode, int shot, int ky, int kz)
Set [ky,kz] coordinate from KS_PHASEENCODING_PLAN for given encode and shot
Definition: KSFoundation_common.c:653
int res
Definition: KSFoundation.h:1721
int numlinestoacq
Definition: KSFoundation.h:1727
STATUS ks_error(const char *format,...)
Definition: KSFoundation_common.c:78
STATUS ks_phaseencoding_generate_simple(KS_PHASEENCODING_PLAN *phaseenc_plan_ptr, const char *const desc, KS_PHASER *phaser, KS_PHASER *zphaser, int encodes_per_shot)
Generation of a KS_PHASEENCODING_PLAN for any sequence having only one echo (or same phasenc step for...
Definition: KSFoundation_common.c:902
int linetoacq[KS_MAX_PHASEDYN]
Definition: KSFoundation.h:1728
#define KS_DESCRIPTION_LENGTH
Definition: KSFoundation.h:253
KS_DESCRIPTION description
Definition: KSFoundation.h:1791
#define KS_RAISE(status)
Definition: KSFoundation.h:190
STATUS ks_phaseencoding_alloc(KS_PHASEENCODING_PLAN *phaseenc_plan_ptr, int etl, int num_shots)
Reallocate memory for KS_PHASEENCODING_PLAN entries
Definition: KSFoundation_common.c:746

◆ ks_phaseencoding_generate_2d_singleshot_cal()

STATUS ks_phaseencoding_generate_2d_singleshot_cal ( KS_PHASEENCODING_PLAN phaseenc_plan_ptr,
int  yres 
)

ADDTITLEHERE

ADDDESCHERE

Parameters
[out]phaseenc_plan_ptrPointer to KS_PHASEENCODING_PLAN
yresADDTEXTHERE
Return values
STATUSSUCCESS or FAILURE
9744  {
9745  STATUS status;
9746  const int minimum_etl = 6;
9747 
9748  const int etl = phaseenc_plan_ptr->encodes_per_shot;
9749 
9750  if (etl < minimum_etl) {
9751  return ks_error("%s: etl must be >= %d", __FUNCTION__, minimum_etl);
9752  }
9753 
9754  /* allocate KS_PHASEENCODING_PLAN table (all entries will be initialized to KS_NOTSET) */
9755  status = ks_phaseencoding_alloc(phaseenc_plan_ptr, etl, 1);
9756  KS_RAISE(status);
9757 
9758  const int step = 1; /* Could be set to -1 too. We had logic to choose before. */
9759 
9760  const float ky_center_main = (yres-1)/2.0f;
9761  const float ky_center_cal = (etl-1)/2.0f;
9762  int i = 0;
9763  int ky = ky_center_main - step * ky_center_cal;
9764  if (ky < 0) {
9765  ky = 0;
9766  }
9767  for (; i < etl && i < yres; i++){
9768  ks_phaseencoding_set(phaseenc_plan_ptr, i, -1, ky, KS_NOTSET);
9769  ky += step;
9770  }
9771  /* In case yres < etl */
9772  for (i = yres; i < etl; i++){
9773  ks_phaseencoding_set(phaseenc_plan_ptr, i, -1, KS_NOTSET, KS_NOTSET);
9774  }
9775 
9776  return SUCCESS;
9777 }
STATUS ks_phaseencoding_alloc(KS_PHASEENCODING_PLAN *phaseenc_plan_ptr, int etl, int num_shots) WARN_UNUSED_RESULT
Reallocate memory for KS_PHASEENCODING_PLAN entries
Definition: KSFoundation_common.c:746
#define KS_NOTSET
Definition: KSFoundation.h:115
STATUS ks_error(const char *format,...) __attribute__((format(printf
Common error message function for HOST and TGT
void ks_phaseencoding_set(KS_PHASEENCODING_PLAN *phaseenc_plan_ptr, int encode, int shot, int ky, int kz)
Set [ky,kz] coordinate from KS_PHASEENCODING_PLAN for given encode and shot
Definition: KSFoundation_common.c:653
int encodes_per_shot
Definition: KSFoundation.h:1788
#define KS_RAISE(status)
Definition: KSFoundation.h:190

◆ ks_comp_kcoord_r_t()

int ks_comp_kcoord_r_t ( const void *  a,
const void *  b 
)

ADDTITLEHERE

ADDDESCHERE

Parameters
aADDTEXTHERE
bADDTEXTHERE
Return values
intADDTEXTHERE
8215  {
8216  KS_KCOORD* k_a =(KS_KCOORD*)a;
8217  KS_KCOORD* k_b =(KS_KCOORD*)b;
8218  if (k_a->r > k_b->r) {
8219  return 1;
8220  } else if (k_a->r < k_b->r) {
8221  return -1;
8222  }
8223 
8224  if (k_a->t > k_b->t) {
8225  return 1;
8226  } else if (k_a->t < k_b->t) {
8227  return -1;
8228  }
8229 
8230  return 0;
8231 }
float t
Definition: KSFoundation.h:2204
float r
Definition: KSFoundation.h:2203
ADDTITLEHERE
Definition: KSFoundation.h:2200

◆ ks_comp_kcoord_z_y()

int ks_comp_kcoord_z_y ( const void *  a,
const void *  b 
)

ADDTITLEHERE

ADDDESCHERE

Parameters
aADDTEXTHERE
bADDTEXTHERE
Return values
intADDTEXTHERE
8293  {
8294  KS_KCOORD* k_a =(KS_KCOORD*)a;
8295  KS_KCOORD* k_b =(KS_KCOORD*)b;
8296  if (k_a->z > k_b->z) {
8297  return 1;
8298  } else if (k_a->z < k_b->z) {
8299  return -1;
8300  }
8301 
8302  if (k_a->y > k_b->y) {
8303  return 1;
8304  } else if (k_a->y < k_b->y) {
8305  return -1;
8306  }
8307 
8308  return 0;
8309 }
int z
Definition: KSFoundation.h:2202
ADDTITLEHERE
Definition: KSFoundation.h:2200
int y
Definition: KSFoundation.h:2201

◆ ks_comp_kcoord_y_z()

int ks_comp_kcoord_y_z ( const void *  a,
const void *  b 
)

ADDTITLEHERE

ADDDESCHERE

Parameters
aADDTEXTHERE
bADDTEXTHERE
Return values
intADDTEXTHERE
8314  {
8315  KS_KCOORD* k_a =(KS_KCOORD*)a;
8316  KS_KCOORD* k_b =(KS_KCOORD*)b;
8317  if (k_a->y > k_b->y) {
8318  return 1;
8319  } else if (k_a->y < k_b->y) {
8320  return -1;
8321  }
8322 
8323  if (k_a->z > k_b->z) {
8324  return 1;
8325  } else if (k_a->z < k_b->z) {
8326  return -1;
8327  }
8328 
8329  return 0;
8330 }
int z
Definition: KSFoundation.h:2202
ADDTITLEHERE
Definition: KSFoundation.h:2200
int y
Definition: KSFoundation.h:2201

◆ ks_comp_kview_r_t()

int ks_comp_kview_r_t ( const void *  a,
const void *  b 
)

ADDTITLEHERE

ADDDESCHERE

Parameters
aADDTEXTHERE
bADDTEXTHERE
Return values
intADDTEXTHERE
8335  {
8336  KS_VIEW* k_a =(KS_VIEW*)a;
8337  KS_VIEW* k_b =(KS_VIEW*)b;
8338  if (k_a->coord->r > k_b->coord->r) {
8339  return 1;
8340  } else if (k_a->coord->r < k_b->coord->r) {
8341  return -1;
8342  }
8343 
8344  if (k_a->coord->t > k_b->coord->t) {
8345  return 1;
8346  } else if (k_a->coord->t < k_b->coord->t) {
8347  return -1;
8348  }
8349 
8350  return 0;
8351 }
float t
Definition: KSFoundation.h:2204
KS_KCOORD * coord
Definition: KSFoundation.h:2215
float r
Definition: KSFoundation.h:2203
ADDTITLEHERE
Definition: KSFoundation.h:2214

◆ ks_comp_kview_y_z()

int ks_comp_kview_y_z ( const void *  a,
const void *  b 
)

ADDTITLEHERE

ADDDESCHERE

Parameters
aADDTEXTHERE
bADDTEXTHERE
Return values
intADDTEXTHERE
8378  {
8379  KS_VIEW* k_a =(KS_VIEW*)a;
8380  KS_VIEW* k_b =(KS_VIEW*)b;
8381  if (k_a->coord->y > k_b->coord->y) {
8382  return 1;
8383  } else if (k_a->coord->y < k_b->coord->y) {
8384  return -1;
8385  }
8386 
8387  if (k_a->coord->z > k_b->coord->z) {
8388  return 1;
8389  } else if (k_a->coord->z < k_b->coord->z) {
8390  return -1;
8391  }
8392  return 0;
8393 }
KS_KCOORD * coord
Definition: KSFoundation.h:2215
int z
Definition: KSFoundation.h:2202
ADDTITLEHERE
Definition: KSFoundation.h:2214
int y
Definition: KSFoundation.h:2201

◆ ks_comp_kview_e_t()

int ks_comp_kview_e_t ( const void *  a,
const void *  b 
)

ADDTITLEHERE

ADDDESCHERE

Parameters
aADDTEXTHERE
bADDTEXTHERE
Return values
intADDTEXTHERE

◆ ks_comp_kview_e_z()

int ks_comp_kview_e_z ( const void *  a,
const void *  b 
)

ADDTITLEHERE

ADDDESCHERE

Parameters
aADDTEXTHERE
bADDTEXTHERE
Return values
intADDTEXTHERE

◆ ks_cal_from_nacslines()

int ks_cal_from_nacslines ( int  R,
int  nacslines 
)

ADDTITLEHERE

ADDDESCHERE

Parameters
[in]RADDTEXTHERE
[in]nacslinesADDTEXTHERE
Return values
intADDTEXTHERE
9784  {
9785  if (R == 1) return nacslines;
9786  return nacslines + nacslines / (R - 1) + 1;
9787 }

◆ ks_get_ry_from_kcoords()

int ks_get_ry_from_kcoords ( KS_KCOORD coord,
const int  num_coords 
)

Checks all KCOORDS to find the largest gap in ky.

Parameters
[in]coord- Will be sorted
[in]num_coords
Returns
int Largest gap (Ry)

◆ ks_get_rz_from_kcoords()

int ks_get_rz_from_kcoords ( KS_KCOORD coord,
const int  num_coords 
)

Checks all KCOORDS to find the largest gap in kz.

Parameters
[in]coord- Will be sorted
[in]num_coords
Returns
int Largest gap (Rz)

◆ ks_get_matrix_from_kcoords()

void ks_get_matrix_from_kcoords ( KS_KCOORD K,
const int  num_coords,
int *  y,
int *  z 
)

Checks all KCOORDS to find the largest span in ky and/or kz.

Parameters
[in]K- Will be sorted
[in]num_coords- number of entries in K
[out]y- matrix size in y
[out]z- matrix size in z
8636  {
8637  if (!K) {
8638  KS_THROW("K is NULL");
8639  return;
8640  }
8641 
8642  if (y == NULL && z == NULL) {
8643  KS_THROW("y and z cannot both be null");
8644  return;
8645  }
8646 
8647  if (y != NULL) {
8648  qsort(K, num_coords, sizeof(KS_KCOORD), &ks_comp_kcoord_dist_y_z);
8649  *y = K[num_coords - 1].y;
8650  if (*y > 0) {
8651  (*y)++; /* Increment since coordinates are left-aligned */
8652  } else {
8653  *y = -(*y);
8654  }
8655  *y *= 2;
8656  }
8657 
8658  if (z != NULL) {
8659  qsort(K, num_coords, sizeof(KS_KCOORD), &ks_comp_kcoord_dist_z_y);
8660  *z = K[num_coords - 1].z;
8661  if (*z > 0) {
8662  (*z)++;
8663  } else {
8664  *z = -(*z);
8665  }
8666  *z *= 2;
8667  }
8668 
8669  ks_dbg("Matrix [Y, Z] = [%d, %d]", y == NULL ? 0 : *y, z == NULL ? 0 : *z);
8670  return;
8671 }
int ks_comp_kcoord_dist_z_y(const void *a, const void *b)
Definition: KSFoundation_host.c:8236
int z
Definition: KSFoundation.h:2202
int ks_comp_kcoord_dist_y_z(const void *a, const void *b)
Definition: KSFoundation_host.c:8265
STATUS STATUS ks_dbg(const char *format,...) __attribute__((format(printf
Common debug message function for HOST and TGT
ADDTITLEHERE
Definition: KSFoundation.h:2200
#define KS_THROW(format,...)
Definition: KSFoundation.h:181
int y
Definition: KSFoundation.h:2201

◆ ks_generate_3d_coords_caipi()

STATUS ks_generate_3d_coords_caipi ( KS_KSPACE_ACQ kacq,
int  Ny,
int  Nz,
int  nover_y,
int  nover_z,
int  Ry,
int  Rz,
int  cal_y,
int  cal_z,
KS_COVERAGE  cal_coverage,
KS_COVERAGE  acq_coverage,
int  dy,
int  dz,
int  step_y,
int  step_z 
)

Generates Cartesian Coordinates with or without CAPIRINHA undersampling.

Parameters
[out]kacqKS_KSPACE_ACQ object to be populated with coords, num_coords, and matrix_size
[in]NyNumber of lines along ky (without acceleration)
[in]NzNumber of lines accross Nz (without acceleration).
[in]nover_yNumber of lines beyond Ny/2 to acquire when using partial Fourier. If nover_y < 0 remove low ky. If nover_y > 0 remove high ky. If nover_y = 0 no Partial Fourier in y.
[in]nover_zNumber of lines beyond Nz/2 to acquire when using partial Fourier. If nover_z < 0 remove low ky. If nover_z > 0 remove high ky. If nover_z = 0 no Partial Fourier in z.
[in]RyUndersampling factor in y
[in]RzUndersampling factor in z
[in]cal_yWidth of calibration region in y
[in]cal_zWidth of calibration region in z
[in]cal_coverageCalibration region coverage, (RECTANGULAR or ELLIPTICAL)
[in]acq_coverageAcquisition region coverage, (RECTANGULAR or ELLIPTICAL)
[in]dy(CAIPIRINHA) Amount of shifting of the y-coordinate between consecutive kz rows
[in]dzNot used
[in]step_y(CAIPIRINHA) Maximum y offset (N*dy) before it loops back to 0
[in]step_zNot used
Return values
STATUSSUCCESS or FAILURE
9801  {
9802 (void)(dz);
9803 (void)(step_z);
9804 if (Nz == KS_NOTSET) {
9805  Nz = 1;
9806 }
9807 
9808 
9809 
9810 
9811 KS_PF_EARLYLATE pf_ymode = KS_PF_NO;
9812 KS_PF_EARLYLATE pf_zmode = KS_PF_NO;
9813 
9814 if (nover_y < 0) {
9815  pf_ymode = KS_PF_EARLY; /* Remove low ky */
9816 } else if (nover_y > 0) {
9817  pf_ymode = KS_PF_LATE; /* Remove high ky */
9818 }
9819 
9820 
9821 
9822 
9823 if (nover_z < 0) {
9824  pf_zmode = KS_PF_EARLY; /* Remove low kz */
9825 } else if (nover_z > 0) {
9826  pf_zmode = KS_PF_LATE; /* Remove high kz */
9827 }
9828 
9829 
9830 
9831 
9832 nover_y = abs(nover_y);
9833 nover_z = abs(nover_z);
9834 
9835 if (Nz == 1) {
9836  pf_zmode = KS_PF_NO;
9837  cal_coverage = RECTANGULAR;
9838  acq_coverage = RECTANGULAR;
9839  cal_z = 1;
9840  dy = 0;
9841  Rz = 1;
9842 }
9843 
9844 
9845 
9846 
9847 KS_KCOORD* K = (KS_KCOORD*)realloc(kacq->coords, (Nz*Ny/(Ry*Rz) + 2*cal_y*cal_z) * sizeof(KS_KCOORD));
9848 if (!K) {
9849  return KS_THROW("Failed reallocation");
9850 }
9851 
9852 
9853 
9854 
9855 kacq->coords = K;
9856 kacq->num_coords = 0;
9857 
9858 int y = 0;
9859 int z = 0;
9860 int row, col;
9861 int offset = 0;
9862 int entry = 0;
9863 
9864 float ky_center = (Ny - 1) / 2.0;
9865 float kz_center = (Nz - 1) / 2.0;
9866 
9867 /* Generate acq coordinates honoring acceleration. Cal region is excluded */
9868 cal_z = IMax(2, cal_z, 1);
9869 int y_cal_low = RDN_FACTOR((Ny - cal_y) / 2, Ry);
9870 int y_cal_hi = y_cal_low + cal_y;
9871 int z_cal_low = RDN_FACTOR((Nz - cal_z) / 2, Rz);
9872 int z_cal_hi = z_cal_low + cal_z;
9873 float y_max = (Ny-1)/2.0;
9874 float z_max = (Nz-1)/2.0;
9875 int skipped = 0;
9876 for (row = 0; row < Nz; row += Rz) { /* row/col maps to ky-kz plane */
9877  z = row;
9878 
9879  /* PF z */
9880  if ((pf_zmode == KS_PF_LATE && z >= (Nz/2 + nover_z)) ||
9881  (pf_zmode == KS_PF_EARLY && z < (Nz/2 - nover_z))) { continue; }
9882 
9883  for (col = 0; col < Ny; col += Ry) {
9884  y = col + offset;
9885  if ((pf_ymode == KS_PF_LATE && y >= (Ny/2 + nover_y)) ||
9886  (pf_ymode == KS_PF_EARLY && y < (Ny/2 - nover_y))) { continue; }
9887 
9888  if (y >= Ny) {
9889  continue;
9890  }
9891  float y1 = Ny > 1 ? (y - Ny/2 + .5) / y_max /* Normalized global coordinate [-1, 1] */
9892  : 0;
9893  float z1 = Nz > 1 ? (z - Nz/2 + .5) / z_max /* Normalized global coordinate [-1, 1] */
9894  : 0;
9895  KS_KCOORD c = {y - Ny/2,
9896  z - Nz/2,
9897  (float)sqrt(y1*y1 + z1*z1),
9898  (float)atan2(z1, y1)
9899  };
9900  if (acq_coverage == ELLIPTICAL && c.r > 1.0) {
9901  continue;
9902  }
9903  if (Ry > 1 || Rz > 1) {
9904  if (y >= y_cal_low &&
9905  y < y_cal_hi &&
9906  z >= z_cal_low &&
9907  z < z_cal_hi) {
9908  if (cal_coverage == RECTANGULAR) {
9909  skipped++;
9910  continue;
9911  }
9912  /* Local coordinates (local as in cal region) */
9913  float l_y = ((float)y + .5 - ky_center ) / (cal_y / 2);
9914  float l_z = ((float)z + .5 - kz_center ) / (cal_z / 2);
9915  float l_r = l_y * l_y + l_z * l_z; /* Local, no need for sqrt here as it only checked against unity */
9916  if (l_r < 1.0) {
9917  skipped++;
9918  continue;
9919  }
9920  }
9921  }
9922  K[entry++] = c;
9923  }
9924  offset += dy;
9925  offset = offset % (step_y+1);
9926 
9927 }
9928 
9929 
9930 
9931 
9932 /* Fill cal region */
9933 if (Ry > 1 || Rz > 1) {
9934  for (y = y_cal_low; y < y_cal_hi; y++) {
9935  for (z = z_cal_low; z < z_cal_hi; z++) {
9936  if (z < 0 || z >= Nz || y < 0 || y >= Ny) {
9937  ks_error("%s: cal region is outside the accelerated region - (y,z) = (%d,%d)", __FUNCTION__, y, z);
9938  continue;
9939  }
9940  if (cal_coverage == ELLIPTICAL) { /* Local coordinates */
9941  float l_y = (float)(y + .5 - ky_center) / (cal_y / 2.0);
9942  float l_z = (float)(z + .5 - kz_center) / (cal_z / 2.0);
9943  float l_r = l_y * l_y + l_z * l_z; /* No need for sqrt here as it only checked against unity */
9944  if (l_r > 1.0) { /* Outside ellipse */
9945  continue;
9946  }
9947  }
9948  float y1 = Ny > 1 ? (y - Ny/2 + .5) / y_max /* Normalized global coordinate [-1, 1] */
9949  : 0;
9950  float z1 = Nz > 1 ? (z - Nz/2 + .5) / z_max /* Normalized global coordinate [-1, 1] */
9951  : 0;
9952  KS_KCOORD c = {y - Ny/2,
9953  z - Nz/2,
9954  (float)sqrt(y1*y1 + z1*z1),
9955  (float)atan2(z1, y1)
9956  };
9957 
9958  K[entry++] = c;
9959  }
9960  }
9961 }
9962 
9963 kacq->num_coords = entry;
9964 kacq->matrix_size[YGRAD] = Ny;
9965 kacq->matrix_size[ZGRAD] = Nz;
9966 return SUCCESS;
9967 } /*ks_generate_3d_coords_caipi*/
Definition: KSFoundation.h:388
#define KS_NOTSET
Definition: KSFoundation.h:115
KS_KCOORD * coords
Definition: KSFoundation.h:2228
STATUS ks_error(const char *format,...) __attribute__((format(printf
Common error message function for HOST and TGT
int matrix_size[3]
Definition: KSFoundation.h:2230
Definition: KSFoundation.h:386
KS_PF_EARLYLATE
Definition: KSFoundation.h:386
float r
Definition: KSFoundation.h:2203
Definition: KSFoundation.h:2094
Definition: KSFoundation.h:2095
Definition: KSFoundation.h:387
ADDTITLEHERE
Definition: KSFoundation.h:2200
int num_coords
Definition: KSFoundation.h:2229
#define KS_THROW(format,...)
Definition: KSFoundation.h:181
KS_KSPACE_ACQ kacq
Definition: ksepi_implementation.e:355

◆ ks_generate_3d_coords_radial()

STATUS ks_generate_3d_coords_radial ( KS_KSPACE_ACQ kacq,
int  tiny_level,
int  Ny,
int  Nz,
int  Ry,
int  Rz,
KS_RADIAL_SAMPLING_MODE  radial_mode,
KS_RADIAL_SAMPLING_COVERAGE  radial_sampling_coverage 
)

Generates Radial Coordinates.

If radial_sampling_mode is CARTESIAN, this function will call ks_generate_3d_coords_simple(kacq, Ny, Nz, 0, 0, Ry, Rz, 0, 0, RECTANGULAR, RECTANGULAR);

For 2D radial, Nz<=1. For 3D Kooshball radial, radial_mode must be RADIAL_THROUGH_CENTER and Nz>1. For 3D Stack-of-stars radial, radial_mode must be RADIAL_SOS and Nz>1.

For golden angle sampling (tiny_level != 0), the angular increment between consecutive spokes are generated using the following formula for 2D and stack of stars (Wundrak et al. https://doi.org/10.1002/mrm.25831):

phi_step = 1.0 /(KS_GOLDEN_MEANS_1D+tiny_level-1)

...and for kooshball this formula is followed (Fyrdahl et al. https://doi.org/10.1007/s10334-020-00859-z):

phi_step = 1.0/(pow(KS_GOLDEN_MEANS_2D_2,-1) + tiny_level-1.0)

theta_step = phi_step/(1+KS_GOLDEN_MEANS_2D_1)

For SAMPLE_360 radial_sampling_coverage the spokes are generated on the half circle/sphere but every other spoke is inverted.

In kacq.coords the angles are stored as signed 16 bit integers that can be converted to floats in the range -180:180 degrees using KS_ANGLE_PER_INT_16_MAX_180

Parameters
[out]kacqKS_KSPACE_ACQ object to be populated with coords, num_coords, and matrix_size
[in]tiny_levelTiny level for golden angle radial sampling (0 means uniform sampling instead of golden angle).
[in]NyNumber of lines along ky (without acceleration)
[in]NzNumber of lines accross Nz (without acceleration).
[in]RyUndersampling factor in coordinate 1 (phi)
[in]RzUndersampling factor in coordinte 2 (kz or theta)
[in]radial_modeSampling mode (Cartesian, radial through center, or stack-of-stars)
[in]radial_sampling_coverageWhether the spokes should originate from a half circle/sphere or full circel/sphere
Return values
STATUSSUCCESS or FAILURE
10023  {
10024 
10025 int entry = 0;
10026 double phi = 0;
10027 double phi_step;
10028 double delta_phi;
10029 double theta = 0;
10030 double theta_step;
10031 double xx, yy, zz;
10032 int kz = 0;
10033 int spoke = 0;
10034 
10035 /* Cartesian */
10036 if(radial_mode == CARTESIAN){
10037  return ks_generate_3d_coords_simple(kacq, Ny, Nz, 0, 0, Ry, Rz, 0, 0, RECTANGULAR, RECTANGULAR);
10038 }
10039 
10040 
10041 
10042 
10043 /* Through center (2d or kooshball) */
10044 else if(radial_mode == RADIAL_THROUGH_CENTER){
10045 
10046  kacq->num_coords = ceil(fabs(Ny * Nz * PI/2.0 / Ry / Rz)) ;
10047 
10048  KS_KCOORD* K = (KS_KCOORD*)realloc(kacq->coords, kacq->num_coords * sizeof(KS_KCOORD));
10049  if (!K) {
10050  return KS_THROW("Failed (re)allocation");
10051  }
10052  kacq->coords = K;
10053  for (entry = 0; entry < kacq->num_coords; entry++) {
10054 
10055  if(Nz<=1){ /* 2D radial */
10056  if(tiny_level==0){
10057  delta_phi = 180.0 / kacq->num_coords;
10058  phi = entry*delta_phi;
10059  theta = 0;
10060  }
10061  else{
10062  phi_step = 1.0 /(KS_GOLDEN_MEANS_1D+tiny_level-1);
10063  phi = 180.0 * (phi_step * entry - floor(phi_step * entry));
10064  theta = 0;
10065  }
10066  if (radial_sampling_coverage == SAMPLE_360 && entry%2){ /* cover full circle */
10067  phi = phi + 180;
10068  }
10069  }
10070  else{ /* Kooshball */
10071  if(tiny_level==0){
10072  zz = fabs((double)entry - (double) kacq->num_coords-1.0)/ (double) kacq->num_coords;
10073  xx = cos(pow( kacq->num_coords*PI,0.5)*asin(zz))*pow(1-pow(zz,2),0.5);
10074  yy = sin(pow( kacq->num_coords*PI,0.5)*asin(zz))*pow(1-pow(zz,2),0.5);
10075 
10076  theta = 90.0-(acos(zz) * 180.0 / PI);
10077  phi = ((yy > 0) - (yy < 0))*acos(xx/pow(pow(xx,2)+pow(yy,2),0.5))* 180.0 / PI;
10078 
10079  }
10080  else{
10081  phi_step = KS_GOLDEN_MEANS_2D_2;
10082  phi_step = 1.0/(pow(KS_GOLDEN_MEANS_2D_2,-1) + (double) tiny_level-1.0);
10083  phi = 360.0 * (phi_step * entry - floor(phi_step * entry));
10084  theta_step = phi_step/(1+KS_GOLDEN_MEANS_2D_1);
10085  theta = asin(theta_step * entry - floor(theta_step * entry)) * 180.0 / PI ;
10086  }
10087  if (radial_sampling_coverage == SAMPLE_360 && entry%2){ /* cover full sphere */
10088  theta = theta + 180;
10089  }
10090  }
10091 
10092  KS_KCOORD c = {(s16) (phi/KS_ANGLE_PER_INT_16_MAX_180),
10093  (s16) (theta/KS_ANGLE_PER_INT_16_MAX_180),
10094  (float) entry,
10095  (float) entry};
10096  K[entry]= c;
10097  }
10098 }
10099 
10100 
10101 
10102 
10103 /* Stack of stars */
10104 else if(radial_mode == RADIAL_SOS){
10105 
10106  kacq->num_coords = ceil(Ny * PI/2.0 / Ry / Rz)* Nz ;
10107 
10108  KS_KCOORD* K = (KS_KCOORD*)realloc(kacq->coords, kacq->num_coords * sizeof(KS_KCOORD));
10109  if (!K) {
10110  return KS_THROW("Failed (re)allocation");
10111  }
10112  kacq->coords = K;
10113  for (entry = 0; entry < kacq->num_coords; entry++) {
10114  if(tiny_level==0){
10115  delta_phi = 180.0 / ceil(Ny * PI/2.0 / Ry / Rz);
10116  phi = spoke*delta_phi;
10117  }
10118  else{
10119  phi_step = 1.0 /(KS_GOLDEN_MEANS_1D+tiny_level-1);
10120  phi = 180.0 * (phi_step * spoke - floor(phi_step * spoke));
10121  }
10122 
10123  if (radial_sampling_coverage == SAMPLE_360 && spoke%2){ /* cover full circle */
10124  phi = phi + 180;
10125  }
10126 
10127  KS_KCOORD c = {(s16) (phi/KS_ANGLE_PER_INT_16_MAX_180),
10128  (s16) (kz - Nz/2),
10129  (float) spoke,
10130  (float) spoke};
10131  K[entry]= c;
10132 
10133  if(kz < Nz - 1){
10134  kz++;
10135  }
10136  else{
10137  kz = 0;
10138  spoke++;
10139  }
10140  }
10141 
10142 }
10143 
10144 else{
10145  return KS_THROW("Requested radial mode (%d) does not exist.", radial_mode);
10146 }
10147 
10148 kacq->matrix_size[YGRAD] = Ny;
10149 kacq->matrix_size[ZGRAD] = Nz;
10150 
10151 return SUCCESS;
10152 } /* ks_generate_3d_coords_radial */
#define KS_GOLDEN_MEANS_2D_2
Definition: KSFoundation.h:63
KS_KCOORD * coords
Definition: KSFoundation.h:2228
Definition: KSFoundation.h:2271
int matrix_size[3]
Definition: KSFoundation.h:2230
#define KS_GOLDEN_MEANS_2D_1
Definition: KSFoundation.h:62
STATUS ks_generate_3d_coords_simple(KS_KSPACE_ACQ *kacq, int Ny, int Nz, int nover_y, int nover_z, int Ry, int Rz, int cal_y, int cal_z, KS_COVERAGE cal_coverage, KS_COVERAGE acq_coverage)
Generates Cartesian Coordinates.
Definition: KSFoundation_host.c:9972
Definition: KSFoundation.h:2272
#define KS_ANGLE_PER_INT_16_MAX_180
Definition: KSFoundation.h:64
#define KS_GOLDEN_MEANS_1D
Definition: KSFoundation.h:61
Definition: KSFoundation.h:2095
ADDTITLEHERE
Definition: KSFoundation.h:2200
int num_coords
Definition: KSFoundation.h:2229
#define KS_THROW(format,...)
Definition: KSFoundation.h:181
Definition: KSFoundation.h:2285
KS_KSPACE_ACQ kacq
Definition: ksepi_implementation.e:355
Definition: KSFoundation.h:2273

◆ ks_generate_3d_coords_simple()

STATUS ks_generate_3d_coords_simple ( KS_KSPACE_ACQ kacq,
int  Ny,
int  Nz,
int  nover_y,
int  nover_z,
int  Ry,
int  Rz,
int  cal_y,
int  cal_z,
KS_COVERAGE  cal_coverage,
KS_COVERAGE  acq_coverage 
)

Generates Cartesian Coordinates.

Calls ks_generate_3d_coords_caipi with the CAIPIRINHA variables, dy, dz, step_y, and step_z set to 0.

Parameters
[out]kacqKS_KSPACE_ACQ object to be populated with coords, num_coords, and matrix_size
[in]NyNumber of lines along ky (without acceleration)
[in]NzNumber of lines accross Nz (without acceleration).
[in]nover_yNumber of lines beyond Ny/2 to acquire when using partial Fourier. If nover_y < 0 remove low ky. If nover_y > 0 remove high ky. If nover_y = 0 no Partial Fourier in y.
[in]nover_zNumber of lines beyond Nz/2 to acquire when using partial Fourier. If nover_z < 0 remove low ky. If nover_z > 0 remove high ky. If nover_z = 0 no Partial Fourier in z.
[in]RyUndersampling factor in y
[in]RzUndersampling factor in z
[in]cal_yWidth of calibration region in y
[in]cal_zWidth of calibration region in z
[in]cal_coverageCalibration region coverage, (RECTANGULAR or ELLIPTICAL)
[in]acq_coverageAcquisition region coverage, (RECTANGULAR or ELLIPTICAL)
Return values
STATUSSUCCESS or FAILURE
9978  {
9979  return ks_generate_3d_coords_caipi(kacq, Ny, Nz, nover_y, nover_z, Ry, Rz, cal_y, cal_z, cal_coverage, acq_coverage, 0, 0, 0, 0);
9980 }/* ks_generate_3d_coords_simple */
STATUS ks_generate_3d_coords_caipi(KS_KSPACE_ACQ *kacq, int Ny, int Nz, int nover_y, int nover_z, int Ry, int Rz, int cal_y, int cal_z, KS_COVERAGE cal_coverage, KS_COVERAGE acq_coverage, int dy, int dz, int step_y, int step_z)
Generates Cartesian Coordinates with or without CAPIRINHA undersampling.
Definition: KSFoundation_host.c:9792
KS_KSPACE_ACQ kacq
Definition: ksepi_implementation.e:355

◆ ks_generate_2d_coords_cal()

STATUS ks_generate_2d_coords_cal ( KS_KSPACE_ACQ kacq,
int  num_desired_coords,
int  yres 
)

ADDTITLEHERE

ADDDESCHERE

Return values
STATUSSUCCESS or FAILURE
9985  {
9986  int max_y = RUP_FACTOR(num_desired_coords, 2);
9987 
9988  STATUS status;
9989 
9990  status = ks_generate_3d_coords_simple(kacq, max_y, KS_NOTSET, 0, 0, 1, 1, 0, 0, RECTANGULAR, RECTANGULAR);
9991  KS_RAISE(status);
9992 
9993  kacq->matrix_size[YGRAD] = yres;
9994  if (kacq->num_coords < num_desired_coords) {
9995  return KS_THROW("Could not generate enough cal coords (%d < %d)", kacq->num_coords, num_desired_coords);
9996  }
9997 
9998  /* Make sure we get the most central coordinates */
10000 
10001  if (kacq->num_coords > num_desired_coords) {
10002  KS_THROW("kacq->num_coords > num_desired_coords (%d > %d). Expected if num_desired_coords is odd", kacq->num_coords, num_desired_coords);
10003  kacq->num_coords = num_desired_coords;
10004  KS_KCOORD* tmp = (KS_KCOORD*)realloc(kacq->coords, kacq->num_coords);
10005  if (tmp) {
10006  kacq->coords = tmp;
10007  } else {
10008  return KS_THROW("Reallocation failed");
10009  }
10010  }
10011  qsort(kacq->coords, kacq->num_coords, sizeof(KS_KCOORD), &ks_comp_kcoord_y_z);
10012  return SUCCESS;
10013 }
#define KS_NOTSET
Definition: KSFoundation.h:115
KS_KCOORD * coords
Definition: KSFoundation.h:2228
int matrix_size[3]
Definition: KSFoundation.h:2230
int ks_comp_kcoord_y_z(const void *a, const void *b)
ADDTITLEHERE
Definition: KSFoundation_host.c:8314
STATUS ks_generate_3d_coords_simple(KS_KSPACE_ACQ *kacq, int Ny, int Nz, int nover_y, int nover_z, int Ry, int Rz, int cal_y, int cal_z, KS_COVERAGE cal_coverage, KS_COVERAGE acq_coverage)
Generates Cartesian Coordinates.
Definition: KSFoundation_host.c:9972
#define KS_RAISE(status)
Definition: KSFoundation.h:190
Definition: KSFoundation.h:2095
int ks_comp_kcoord_dist_y_z(const void *a, const void *b)
Definition: KSFoundation_host.c:8265
ADDTITLEHERE
Definition: KSFoundation.h:2200
int num_coords
Definition: KSFoundation.h:2229
#define KS_THROW(format,...)
Definition: KSFoundation.h:181
KS_KSPACE_ACQ kacq
Definition: ksepi_implementation.e:355

◆ ks_generate_3d_coords_poisson_disc()

STATUS ks_generate_3d_coords_poisson_disc ( KS_KSPACE_ACQ kacq,
int  Ny,
int  Nz,
float  min_spacing,
float  max_spacing,
KS_COVERAGE  cal_coverage,
int  cal_y,
int  cal_z,
RADIUS_PATTERN  pattern 
)

ADDTITLEHERE

ADDDESCHERE

Return values
STATUSSUCCESS or FAILURE
10298  {
10299  const int num_attempts = 50;
10300  const float ky_center = (Ny - 1) / 2.0;
10301  const float kz_center = (Nz - 1) / 2.0;
10302  const float ky_radius = Ny / 2.0;
10303  const float kz_radius = Nz / 2.0;
10304  const int max_queue = Ny*Nz;
10305  int queue_size = 0;
10306  int y, z;
10307 
10308  KS_KCOORD* queue = (KS_KCOORD*)malloc(max_queue * sizeof(KS_KCOORD));
10309 
10310 
10311  KS_KCOORD* K = (KS_KCOORD*)realloc(kacq->coords, Nz*Ny * sizeof(KS_KCOORD)); /* Output */
10312  if (!K) {
10313  return KS_THROW("Failed reallocation");
10314  }
10315  kacq->coords = K;
10316  int* grid = (int*) calloc (Ny*Nz,sizeof(int)); /* Initialized to zero */
10317  int idx = 0;
10318  int num_coords = 0;
10319 
10320 
10321  int y_corners[4] = {0, 0, Ny-1, Ny-1};
10322  int z_corners[4] = {0, Nz-1, Nz-1, 0};
10323 
10324  for (idx = 0; idx < 4; idx++) {
10325  y = y_corners[idx];
10326  z = z_corners[idx];
10327  float y2 = (y + .5 - ky_center) / ky_radius; /* Global coordinate */
10328  float z2 = (z + .5 - kz_center) / kz_radius; /* Global coordinate */
10329  KS_KCOORD coord = {y, z, (float)sqrt((y2 * y2) + (z2 * z2)), (float)atan2(z2, y2)};
10330  queue[queue_size++] = coord;
10331  K[num_coords++] = coord;
10332  set_grid(grid, Nz, coord.y, coord.z);
10333  }
10334 
10335 
10336  /* Fully sampled region */
10337  int y_cal_low = (Ny - cal_y) / 2;
10338  int y_cal_hi = y_cal_low + cal_y;
10339  int z_cal_low = (Nz - cal_z) / 2;
10340  int z_cal_hi = z_cal_low + cal_z;
10341  for (y = y_cal_low; y < y_cal_hi; y++) {
10342  for (z = z_cal_low; z < z_cal_hi; z++) {
10343  if (cal_coverage == ELLIPTICAL) {
10344  float l_y = (y + .5 - ky_center) / (cal_y / 2);
10345  float l_z = (z + .5 - kz_center) / (cal_z / 2);
10346  float l_r = sqrt(l_y * l_y + l_z * l_z);
10347  if (l_r > 1.0) {
10348  continue;
10349  }
10350  }
10351  float y2 = (y + .5 - ky_center) / ky_radius;
10352  float z2 = (z + .5 - kz_center) / kz_radius;
10353 
10354  KS_KCOORD coord = {y, z, (float)sqrt((y2 * y2) + (z2 * z2)), (float)atan2(z2, y2)};
10355  queue[queue_size++] = coord;
10356  K[num_coords++] = coord;
10357  set_grid(grid, Nz, coord.y, coord.z);
10358  }
10359  }
10360 
10361 
10362  while (queue_size > 0) {
10363  /* Pick a random element in active list */
10364  int active_idx = rand() % queue_size;
10365  KS_KCOORD active_coord = queue[active_idx];
10366 
10367  float spacing = ks_poisson_disc_min_spacing(active_coord.y, active_coord.z, ky_center, kz_center, min_spacing, max_spacing, pattern);
10368 
10369  int success = 0;
10370  int attempt;
10371  for (attempt = 0; attempt < num_attempts; attempt++) {
10372  /* Spawn a candidate in the circular annulus around the active coordinate */
10373  float spawn_angle = rand_float_in_range(-3.14159265, 3.14159265);
10374  float spawn_distance = rand_float_in_range(spacing, 2*spacing);
10375 
10376  float cand_y = active_coord.y + 0.5 + spawn_distance * sin(spawn_angle);
10377  float cand_z = active_coord.z + 0.5 + spawn_distance * cos(spawn_angle);
10378 
10379  /* Snap the candidate to the grid */
10380  float y2 = ((int)cand_y + .5 - ky_center) / ky_radius;
10381  float z2 = ((int)cand_z + .5 - kz_center) / kz_radius;
10382  KS_KCOORD cand = {(int)(cand_y), (int)(cand_z), (float)sqrt((y2 * y2) + (z2 * z2)), (float)atan2(z2, y2) };
10383  if (cand.y < 0 || cand.y > (Ny-1) ||
10384  cand.z < 0 || cand.z > (Nz-1) ) {
10385  continue;
10386  }
10387 
10388  /* Check if candidate is within active radius (it might have snapped closer than allowed) */
10389  if (sqrt(pow(active_coord.y - cand.y,2) + pow(active_coord.z - cand.z,2)) < spacing) {
10390  continue;
10391  }
10392  /* Retrieve the radius at the candidate position */
10393  float radius_candidate = ks_poisson_disc_min_spacing(cand.y, cand.z, ky_center, kz_center, min_spacing, max_spacing, pattern);
10394  int conflict = check_conflicts(grid, Ny, Nz, cand, radius_candidate);
10395  if (conflict != 1) {
10396  set_grid(grid, Nz, cand.y, cand.z);
10397  K[num_coords++] = cand;
10398  queue[queue_size++] = cand;
10399  success++;
10400  break;
10401  }
10402  }
10403 
10404  if (success == 0) {
10405  remove_element(queue, active_idx, queue_size--);
10406  }
10407 
10408  }
10409 
10410  free(queue);
10411  free(grid);
10412  K = (KS_KCOORD*)realloc(kacq->coords, num_coords* sizeof(KS_KCOORD)); /* Shrink */
10413  if (!K) {
10414  return KS_THROW("Failed reallocation");
10415  }
10416  kacq->coords = K;
10417  kacq->num_coords = num_coords;
10418 
10419  for (idx = 0; idx < kacq->num_coords; idx++) {
10420  kacq->coords[idx].y -= Ny/2;
10421  kacq->coords[idx].z -= Nz/2;
10422  }
10423 
10424  kacq->matrix_size[YGRAD] = Ny;
10425  kacq->matrix_size[ZGRAD] = Nz > 1 ? Nz : KS_NOTSET;
10426 
10427  return SUCCESS;
10428 }
int check_conflicts(int *grid, int Ny, int Nz, KS_KCOORD cand, float cand_radius)
Definition: KSFoundation_host.c:10202
void set_grid(int *grid, int Nz, int y, int z)
Definition: KSFoundation_host.c:10157
#define KS_NOTSET
Definition: KSFoundation.h:115
float rand_float_in_range(float min, float max)
Definition: KSFoundation_host.c:10171
KS_KCOORD * coords
Definition: KSFoundation.h:2228
int matrix_size[3]
Definition: KSFoundation.h:2230
int z
Definition: KSFoundation.h:2202
float ks_poisson_disc_min_spacing(const int y, const int z, const float center_y, const float center_z, const float min_spacing, const float max_spacing, const RADIUS_PATTERN pattern)
Definition: KSFoundation_host.c:10179
Definition: KSFoundation.h:2094
ADDTITLEHERE
Definition: KSFoundation.h:2200
int num_coords
Definition: KSFoundation.h:2229
#define KS_THROW(format,...)
Definition: KSFoundation.h:181
KS_KSPACE_ACQ kacq
Definition: ksepi_implementation.e:355
int y
Definition: KSFoundation.h:2201
void remove_element(KS_KCOORD *coord_array, int idx, int size)
Definition: KSFoundation_host.c:10233

◆ ks_generate_3d_coords_poisson_disc_R()

STATUS ks_generate_3d_coords_poisson_disc_R ( KS_KSPACE_ACQ kacq,
int  Ny,
int  Nz,
float  R,
KS_COVERAGE  cal_coverage,
int  cal_y,
int  cal_z,
RADIUS_PATTERN  pattern 
)

ADDTITLEHERE

ADDDESCHERE

Return values
STATUSSUCCESS or FAILURE
10249  {
10250 
10251  const float min_spacing = 1.4;
10252  float left_spacing = min_spacing;
10253  float right_spacing = IMax(2,Nz/4, Ny/4);
10254  STATUS status;
10255 
10256  status = ks_generate_3d_coords_poisson_disc(kacq, Ny, Nz, min_spacing, right_spacing, cal_coverage, cal_y, cal_z, pattern);
10257  KS_RAISE(status);
10258 
10259  const int max_iter = 10;
10260  float cur_R = Ny*Nz/(kacq->num_coords);
10261  int iter = 0;
10262  float cur_spacing;
10263  /* Binary search */
10264  while (fabs(cur_R - R) > 0.1 && iter < max_iter) {
10265  cur_spacing = (right_spacing + left_spacing) / 2.0;
10266 
10267  status = ks_generate_3d_coords_poisson_disc(kacq, Ny, Nz, min_spacing, cur_spacing, cal_coverage, cal_y, cal_z, pattern);
10268  KS_RAISE(status);
10269 
10270  cur_R = Ny*Nz/(float)(kacq->num_coords);
10271 
10272 
10273  if (cur_R > R) {
10274  right_spacing = cur_spacing;
10275  }
10276  if (cur_R < R) {
10277  left_spacing = cur_spacing;
10278  }
10279 
10280  iter++;
10281  }
10282 
10283  return SUCCESS;
10284 
10285 
10286 }
STATUS ks_generate_3d_coords_poisson_disc(KS_KSPACE_ACQ *kacq, int Ny, int Nz, float min_spacing, float max_spacing, KS_COVERAGE cal_coverage, int cal_y, int cal_z, RADIUS_PATTERN pattern)
ADDTITLEHERE
Definition: KSFoundation_host.c:10291
#define KS_RAISE(status)
Definition: KSFoundation.h:190
int num_coords
Definition: KSFoundation.h:2229
KS_KSPACE_ACQ kacq
Definition: ksepi_implementation.e:355

◆ ks_generate_3d_coords_epi()

STATUS ks_generate_3d_coords_epi ( KS_KSPACE_ACQ kacq,
const KS_EPI epitrain,
const ks_enum_epiblipsign  blipsign,
const int  Ry,
const int  caipi_delta 
)

ADDTITLEHERE

ADDDESCHERE

Return values
STATUSSUCCESS or FAILURE
10437  {
10438 
10439  /* Interleaves refers to the number of different shifts along ky */
10440  const int max_numileaves = epitrain->blipphaser.R;
10441  const int acc_numileaves = epitrain->blipphaser.R / Ry;
10442 
10443  if (epitrain->blipphaser.R < 1 || Ry < 1) {
10444  return KS_THROW("Acceleration factors must be positive");
10445  }
10446 
10447  if (acc_numileaves < 1) {
10448  return KS_THROW("The requested acceleration (%d) is greater than the maximum allowed (%d) by the EPI train", Ry, epitrain->blipphaser.R);
10449  }
10450 
10451  if (epitrain->blipphaser.R % Ry) {
10452  return KS_THROW("The maximum allowed acceleration by the EPI train must be divisible by the requested one");
10453  }
10454 
10455  /* Overallocate for now */
10456  const int Nz = epitrain->zphaser.res > 0 ? epitrain->zphaser.numlinestoacq : 1;
10457  KS_KCOORD* K = (KS_KCOORD*)realloc(kacq->coords, Nz*max_numileaves * sizeof(KS_KCOORD));
10458  if (!K) {
10459  return KS_THROW("Failed (re)allocation");
10460  }
10461  kacq->coords = K;
10462  kacq->num_coords = 0;
10463 
10464  const int base_kyview = blipsign == KS_EPI_NEGBLIPS ?
10465  epitrain->blipphaser.linetoacq[0] :
10466  epitrain->blipphaser.linetoacq[epitrain->blipphaser.numlinestoacq-1];
10467 
10468  const double ky_center = (epitrain->blipphaser.res-1)/2.0;
10469  const double kz_center = epitrain->zphaser.res > 0 ? (epitrain->zphaser.res-1)/2.0 : -1;
10470 
10471  int kzidx = 0, kyidx;
10472  int kzview = KS_NOTSET;
10473  int is_cal = 0;
10474  int shot = 0;
10475  do {
10476  if (epitrain->zphaser.res != KS_NOTSET) {
10477 
10478  kzview = epitrain->zphaser.linetoacq[kzidx];
10479 
10480  if (epitrain->zphaser.R > 1) {
10481  if (kzidx < epitrain->zphaser.numlinestoacq-1) {
10482  is_cal =
10483  epitrain->zphaser.nacslines > 0 &&
10484  abs(epitrain->zphaser.linetoacq[kzidx+1]- kzview) == 1;
10485  }
10486  } else {
10487  is_cal = abs(epitrain->zphaser.res/2.0 - kzview)*2.0 < epitrain->zphaser.nacslines;
10488  }
10489  }
10490 
10491  const int numileaves = is_cal ? max_numileaves : acc_numileaves;
10492  const int step = is_cal ? 1 : Ry;
10493 
10494  for (kyidx = 0; kyidx < numileaves; ++kyidx) {
10495  int shift = kyidx * step;
10496 
10497  if (epitrain->zphaser.res != KS_NOTSET) {
10498  /* CAIPIRINHA shift */
10499  shift = (shift + (kzview/epitrain->zphaser.R) * caipi_delta) % max_numileaves;
10500  }
10501 
10502  const int kyview = base_kyview + shift;
10503  /* Calculate the symmetric coordinates normalized to [-1,1].
10504  Note that, if kzview is -1, so is kz_center. This results in a z coordinate of zero. */
10505  const double y = (kyview - ky_center)/ky_center;
10506  const double z = (kzview - kz_center)/kz_center;
10507  KS_KCOORD c = {kyview - epitrain->blipphaser.res/2,
10508  kzview - epitrain->zphaser.res/2,
10509  (float)sqrt((y * y) + (z * z)),
10510  (float)atan2(z, y),
10511  };
10512 
10513  K[shot] = c;
10514  ++shot;
10515  }
10516 
10517  ++kzidx;
10518 
10519  } while(epitrain->zphaser.res != KS_NOTSET && kzidx < epitrain->zphaser.numlinestoacq);
10520 
10521  /* Downsize if necessary */
10522  K = (KS_KCOORD*)realloc(K, shot * sizeof(KS_KCOORD));
10523  if (!K) {
10524  return KS_THROW("Failed (re)allocation");
10525  }
10526 
10527  kacq->coords = K;
10528  kacq->num_coords = shot;
10529  kacq->matrix_size[YGRAD] = epitrain->blipphaser.res;
10530  kacq->matrix_size[ZGRAD] = epitrain->zphaser.res > 0 ? epitrain->zphaser.numlinestoacq : KS_NOTSET;
10531 
10532  return SUCCESS;
10533 }
int R
Definition: KSFoundation.h:1723
int res
Definition: KSFoundation.h:1721
int nacslines
Definition: KSFoundation.h:1724
#define KS_NOTSET
Definition: KSFoundation.h:115
KS_KCOORD * coords
Definition: KSFoundation.h:2228
KS_PHASER blipphaser
Definition: KSFoundation.h:1937
int numlinestoacq
Definition: KSFoundation.h:1727
int matrix_size[3]
Definition: KSFoundation.h:2230
KS_PHASER zphaser
Definition: KSFoundation.h:1938
int linetoacq[KS_MAX_PHASEDYN]
Definition: KSFoundation.h:1728
ADDTITLEHERE
Definition: KSFoundation.h:2200
int num_coords
Definition: KSFoundation.h:2229
#define KS_THROW(format,...)
Definition: KSFoundation.h:181
KS_KSPACE_ACQ kacq
Definition: ksepi_implementation.e:355
Definition: KSFoundation.h:2330

◆ ks_pe_linear()

void ks_pe_linear ( int *  view_order,
int  ETL 
)

ADDTITLEHERE

ADDDESCHERE

Parameters
[out]view_orderADDTEXTHERE
[in]ETLADDTEXTHERE
Returns
void
8676  {
8677  int i;
8678  for (i = 0; i < ETL; i++) {
8679  view_order[i] = i;
8680  }
8681 }

◆ ks_pe_linear_roll()

void ks_pe_linear_roll ( int *  view_order,
int  ETL,
int  c 
)

ADDTITLEHERE

ADDDESCHERE

Parameters
view_orderADDTEXTHERE
[in]ETLADDTEXTHERE
[in]cADDTEXTHERE
Returns
void
8686  {
8687  int base[ETL];
8688  int idx;
8689  for (idx = 0; idx < ETL; idx++) {
8690  base[idx] = idx;
8691  }
8692  int s;
8693  if (c < (ETL/2)) {
8694  s = ETL/2 - c;
8695  } else {
8696  s = 3*ETL/2 - c;
8697  }
8698 
8699  memcpy(&(view_order[0]), &(base[ETL-s]), sizeof(int) * s);
8700  memcpy(&(view_order[s]), &(base[0]), sizeof(int) * (ETL-s));
8701 }

◆ ks_pe_pivot_roll()

void ks_pe_pivot_roll ( int *  view_order,
int  ETL,
int  c 
)

ADDTITLEHERE

ADDDESCHERE

Parameters
view_orderADDTEXTHERE
[in]ETLADDTEXTHERE
[in]cADDTEXTHERE
Returns
void
8706  {
8707  int base[ETL];
8708  ks_pivot_specific_center_symk(base, ETL, 0);
8709  int s;
8710  if (c == 0) {
8711  s = 0;
8712  } else {
8713  if (ETL % 2 ) {
8714  if (c % 2) {
8715  s = ETL - c/2 - 1;
8716  } else {
8717  s = c / 2;
8718  }
8719  } else {
8720  if (c % 2) {
8721  s = ETL - c/2;
8722  } else {
8723  s = c / 2;
8724  }
8725  }
8726  }
8727 
8728  memcpy(&(view_order[0]), &(base[ETL-s]), sizeof(int) * s);
8729  memcpy(&(view_order[s]), &(base[0]), sizeof(int) * (ETL-s));
8730 }
void ks_pivot_specific_center_symk(int *view_order, int ETL, int c)
ADDTITLEHERE
Definition: KSFoundation_host.c:8813

◆ ks_pivot_linear_center_symk()

void ks_pivot_linear_center_symk ( int *  view_order,
int  ETL,
int  c 
)

ADDTITLEHERE

ADDDESCHERE

Parameters
view_orderADDTEXTHERE
[in]ETLADDTEXTHERE
[in]cADDTEXTHERE
Returns
void
8736  {
8737 
8738  typedef enum {
8739  UP,
8740  DOWN
8741  } UPDOWN;
8742 
8743  typedef enum {
8744  LEFT,
8745  RIGHT
8746  } LR;
8747  int odd_ETL = ETL % 2;
8748  int idx = 0;
8749 
8750  if (c == ETL-1 && odd_ETL != 1) {
8751  return ks_pivot_linear_center_symk(view_order, ETL, c-1);
8752  }
8753  UPDOWN centertype = c < (ETL/2) ? UP
8754  : DOWN;
8755 
8756  /* Pivoting from center (replace later) */
8757  int pivot_index = (ETL-1)/2;
8758  if (odd_ETL != 1 && centertype == DOWN) {
8759  pivot_index++;
8760  }
8761 
8762  LR s_dir = RIGHT;
8763  if (centertype == DOWN && odd_ETL != 1) {
8764  s_dir = LEFT;
8765  } else if (centertype == UP && odd_ETL == 1) {
8766  s_dir = LEFT;
8767  }
8768  int val = centertype == DOWN ? ETL-1
8769  : 0;
8770  for (idx = 0; idx < (ETL/2); idx++) {
8771  if (centertype == UP) {
8772  if (s_dir == LEFT) {
8773  view_order[pivot_index + idx] = val++;
8774  view_order[pivot_index - idx - 1] = val++;
8775  } else {
8776  view_order[pivot_index - idx] = val++;
8777  view_order[pivot_index + idx + 1] = val++;
8778  }
8779  } else {
8780  if (s_dir == LEFT) {
8781  view_order[pivot_index + idx] = val--;
8782  view_order[pivot_index - idx - 1] = val--;
8783  } else {
8784  view_order[pivot_index - idx] = val--;
8785  view_order[pivot_index + idx + 1] = val--;
8786  }
8787  }
8788  }
8789 
8790  if (odd_ETL == 1) {
8791  if (centertype == UP) {
8792  view_order[ETL-1] = ETL-1;
8793  } else {
8794  view_order[0] = 0;
8795  }
8796 
8797  }
8798 
8799 
8800  /* Linear sweep */
8801  const int linear_start = odd_ETL ? abs(ETL/2 - c)
8802  : abs(ETL/2 - 1 - c);
8803  const int num_linear = ETL - 2*linear_start;
8804  for (idx=0; idx < num_linear; idx++) {
8805  view_order[linear_start + idx] = centertype == UP ? idx
8806  : linear_start*2 + idx;
8807  }
8808 }
void ks_pivot_linear_center_symk(int *view_order, int ETL, int c)
ADDTITLEHERE
Definition: KSFoundation_host.c:8736

◆ ks_pe_permute_pclo_rad()

STATUS ks_pe_permute_pclo_rad ( int *  view_order,
const int  etl,
const int  center 
)

ADDTITLEHERE

ADDDESCHERE

Parameters
view_orderADDTEXTHERE
[in]etlADDTEXTHERE
[in]centerADDTEXTHERE
Return values
STATUSSUCCESS or FAILURE
9266  {
9267  if (view_order == NULL) {
9268  return KS_THROW("View order is NULL");
9269  }
9270  if (center < 0) {
9271  return KS_THROW("Center encode (%d) must be in [0, %d)", center, etl);
9272  }
9273  if (center > etl-1) {
9274  return KS_THROW("Center encode (%d) must be smaller than encodes per shot (%d)", center, etl);
9275  }
9276  int i;
9277  if (center >= (etl + 1)/2) {
9278 
9279  KS_RAISE(ks_pe_permute_pclo_rad(view_order, etl, etl-center-1));
9280  for (i=0; i < etl; i++) {
9281  view_order[i] = etl - 1 - view_order[i];
9282  }
9283  return SUCCESS;
9284  }
9285  const int num_inner = CEIL_DIV(center,2);
9286  const int num_outer = CEIL_DIV(center+1,2);
9287  const int num_linear = etl - num_inner - num_outer - 1;
9288  const int even_center_encode = center % 2 == 0;
9289 
9290  for (i = 0; i < num_inner; i++) {
9291  view_order[i] = center - 2*i;
9292  }
9293 
9294  view_order[i] = 0;
9295 
9296 
9297  for (i = 1; i <= num_outer; i++) {
9298  view_order[num_inner + i] = 2*i - even_center_encode;
9299  }
9300 
9301  for (i = 1; i <= num_linear; i++) {
9302  view_order[num_inner + num_outer + i] = num_inner + num_outer + i;
9303  }
9304 
9305 
9306  return SUCCESS;
9307 }
STATUS ks_pe_permute_pclo_rad(int *view_order, const int etl, const int center)
ADDTITLEHERE
Definition: KSFoundation_host.c:9266
#define KS_RAISE(status)
Definition: KSFoundation.h:190
#define KS_THROW(format,...)
Definition: KSFoundation.h:181

◆ ks_pivot_specific_center_symk()

void ks_pivot_specific_center_symk ( int *  view_order,
int  ETL,
int  c 
)

ADDTITLEHERE

ADDDESCHERE

Parameters
view_orderADDTEXTHERE
[in]ETLADDTEXTHERE
[in]cADDTEXTHERE
Returns
void
8813  {
8814  /* Some enums for YOUR convenience */
8815  typedef enum {
8816  UP,
8817  DOWN
8818  } UPDOWN;
8819 
8820  typedef enum {
8821  LEFT,
8822  RIGHT
8823  } LR;
8824 
8825  /* Is center a peak or valley? */
8826  UPDOWN centertype = c < (ETL/2) ? DOWN
8827  : UP;
8828  int pivot = centertype == DOWN ? (int)(ceil((ETL - c)/ 2.0) - 1)
8829  : (int)(ceil((c - 1)/ 2.0) + 1);
8830  if (centertype == UP) {
8831  pivot -= 1;
8832  }
8833  LR s_dir;
8834 
8835  if (ETL % 2) {
8836  s_dir = (c % 2) ? RIGHT : LEFT;
8837  } else {
8838  if (centertype == DOWN) {
8839  s_dir = (c % 2) ? LEFT : RIGHT;
8840  } else {
8841  s_dir = (c % 2) ? RIGHT : LEFT;
8842  }
8843  }
8844  int idx = 0;
8845  int val = centertype == DOWN ? 0 : (ETL-1);
8846  view_order[pivot] = (centertype == DOWN ? 0 : (ETL-1));
8847  for (; idx < pivot; idx++) {
8848  if (centertype == DOWN) {
8849  if (s_dir == LEFT) {
8850  view_order[pivot - idx - 1] = ++val;
8851  view_order[pivot + idx + 1] = ++val;
8852  } else {
8853  view_order[pivot + idx + 1] = ++val;
8854  view_order[pivot - idx - 1] = ++val;
8855  }
8856  } else {
8857  if (s_dir == LEFT) {
8858  view_order[pivot - idx - 1] = --val;
8859  view_order[pivot + idx + 1] = --val;
8860  } else {
8861  view_order[pivot + idx + 1] = --val;
8862  view_order[pivot - idx - 1] = --val;
8863  }
8864  }
8865  }
8866  for (idx = (pivot*2+1); idx < ETL; idx++) {
8867  view_order[idx] = (centertype == DOWN) ? idx
8868  : ((ETL-1-idx));
8869  }
8870 
8871  /* If center echo matches linear sweep */
8872  if (ETL/2 == c) {
8873  for (idx=0; idx < ETL; idx++) {
8874  view_order[idx] = idx;
8875  }
8876  }
8877 
8878 }

◆ ks_pivot_specific_center_radk()

void ks_pivot_specific_center_radk ( int *  view_order,
int  ETL,
int  c 
)

ADDTITLEHERE

ADDDESCHERE

Parameters
view_orderADDTEXTHERE
[in]ETLADDTEXTHERE
[in]cADDTEXTHERE
Returns
void
8883  {
8884  /* Some enums for YOUR convenience */
8885  typedef enum {
8886  UP,
8887  DOWN
8888  } UPDOWN;
8889 
8890  typedef enum {
8891  LEFT,
8892  RIGHT
8893  } LR;
8894 
8895  /* Is center a peak or valley? */
8896  UPDOWN centertype = c < (ETL/2) ? DOWN
8897  : UP;
8898  int pivot = centertype == DOWN ? (int)floor(( 1 + c) / 2.0)
8899  : (int)floor((ETL - c) / 2.0);
8900 
8901  LR s_dir;
8902  if (ETL % 2) { /* Odd ETL */
8903  s_dir = (c % 2) ? RIGHT : LEFT;
8904  } else { /* Even ETL */
8905  if (centertype == DOWN) {
8906  s_dir = (c % 2) ? LEFT : RIGHT;
8907  } else {
8908  s_dir = (c % 2) ? RIGHT : LEFT;
8909  }
8910  }
8911  int idx = 0;
8912  int val = centertype == DOWN ? 0 : (ETL-1);
8913  view_order[pivot] = (centertype == DOWN ? 0 : (ETL-1));
8914 
8915 
8916  for (; idx < pivot; idx++) {
8917  if (centertype == DOWN) {
8918  if (s_dir == LEFT) {
8919  view_order[pivot - idx - 1] = ++val;
8920  view_order[pivot + idx + 1] = ++val;
8921  } else {
8922  view_order[pivot + idx + 1] = ++val;
8923  view_order[pivot - idx - 1] = ++val;
8924  }
8925  } else {
8926  if (s_dir == LEFT) {
8927  view_order[pivot - idx - 1] = --val;
8928  view_order[pivot + idx + 1] = --val;
8929  } else {
8930  view_order[pivot + idx + 1] = --val;
8931  view_order[pivot - idx - 1] = --val;
8932  }
8933  }
8934  }
8935  for (idx = (pivot*2+1); idx < ETL; idx++) {
8936  view_order[idx] = (centertype == DOWN) ? idx
8937  : ((ETL-1-idx));
8938  }
8939 
8940 }

◆ ks_generate_peplan_from_kcoords()

STATUS ks_generate_peplan_from_kcoords ( const KS_KSPACE_ACQ kacq,
KS_PHASEENCODING_PLAN peplan,
KS_MTF_DIRECTION  mtf_direction,
KS_VIEW_SORT_ORDER  encode_shot_assignment_order,
KS_VIEW_MTF_PROFILE  mtf_profile,
const int  etl,
int  center,
const KS_COORDINATE_TYPE *const  coord_types 
)

Phase encoding plan generators

ADDDESCHERE

Parameters
[in]kacqADDTEXTHERE
[out]peplanADDTEXTHERE
[in]mtf_directionADDTEXTHERE
[in]encode_shot_assignment_orderADDTEXTHERE
[in]mtf_profileADDTEXTHERE
[in]etlADDTEXTHERE
[in]centerADDTEXTHERE
[in]coord_typesKS_COORDINATE_TYPE ADDTEXTHERE
Return values
STATUSSUCCESS or FAILURE
9518  {
9519  if (center == KS_NOTSET) {
9520  mtf_profile = LINEAR_SWEEP;
9522  const int center_lth_linsweep = ks_peplan_find_center_from_linear_sweep(kacq->coords, NULL, kacq->num_coords, mtf_direction, +1 /* lth */, tmp_dist.shots, 0 /* start_from */);
9523  const int center_htl_linsweep = ks_peplan_find_center_from_linear_sweep(kacq->coords, NULL, kacq->num_coords, mtf_direction, -1 /* htl */, tmp_dist.shots, 0 /* start_from */);
9524  if (center_lth_linsweep != center_htl_linsweep) {
9525  return KS_THROW("Ambigous center encode: center encode was not set and partial Fourier coords are supplied");
9526  }
9527  center = center_lth_linsweep;
9528  } else if (center < 0) {
9529  return KS_THROW("Center index cannot be negative (requested %d)", center);
9530  }
9531 
9532  if (center >= etl) {
9533  return KS_THROW("Center index must be in [0,etl-1] (center=%d, etl=%d)", center, etl);
9534  }
9535  if (kacq->num_coords <= 0) {
9536  return KS_THROW("num_coords is %d", kacq->num_coords);
9537  }
9538 
9539  if (kacq->coords == NULL) {
9540  return KS_THROW("No coordinates supplied");
9541  }
9542 
9543  if (center >= kacq->num_coords) {
9544  return KS_THROW("Center index must be in [0,num_coords-1] (center=%d, num_coords=%d)", center, kacq->num_coords);
9545  }
9546 
9547  if (mtf_profile == SPLIT_SHOTS_AMERICAN) {
9548  STATUS s = ks_generate_peplan_from_kcoords_american(kacq, peplan, mtf_direction, etl, center);
9549  return s;
9550  } else if (mtf_profile == SPLIT_SHOTS_DUTCH) {
9551  return ks_generate_peplan_from_kcoords_dutch(kacq, peplan, mtf_direction, etl, center);
9552  }
9553 
9555 
9556  int idx, readout, train;
9557  KS_KCOORD* K = kacq->coords;
9558  /* Check if K.z is the same for all KCOORDS. If it is then we don't set it later.*/
9559  int is3d = 0;
9560  for (idx = 0; idx < kacq->num_coords; idx++) {
9561  is3d |= (K[0].z != K[idx].z);
9562  }
9563 
9564  if ((is3d == 0) && ((mtf_direction == MTF_R) || (mtf_direction == MTF_R))) {
9565  return KS_THROW("Radial MTF is not supported for 2D acquisitions");
9566  }
9567 
9568  KS_COORDINATE_TYPE default_coord_types[] = {CARTESIAN_COORD, CARTESIAN_COORD};
9569  if (coord_types == NULL) {
9570  coord_types = default_coord_types;
9571  }
9572 
9573  /* Create views */
9574  KS_VIEW* views = (KS_VIEW*) malloc(kacq->num_coords * sizeof(KS_VIEW));
9575 
9576  for (idx = 0; idx < kacq->num_coords; idx++) {
9577  views[idx].coord = &(K[idx]);
9578  views[idx].encode = KS_NOTSET;
9579  views[idx].train = KS_NOTSET;
9580  }
9581 
9582  /*
9583  Calculate the view order (e.g. center out if center=0) using a pivot algorithm.
9584  The view order describes the T2 modulation in k-space.
9585  If you want to implement linear sweep, view_order is simply [0, 1, ..., shot_dist.encodes_per_shot-1]
9586  */
9587  int view_order[shot_dist.encodes_per_shot];
9588  memset(view_order, KS_NOTSET, sizeof(view_order));
9589  view_order[0] = KS_NOTSET;
9590  if ((mtf_direction == MTF_Y) || (mtf_direction == MTF_Z)) {
9591  switch (mtf_profile) {
9593  ks_pivot_specific_center_symk(view_order, shot_dist.encodes_per_shot, center);
9594  break;
9595  } case SPLIT_SHOTS_LCPO:
9596  case LCPO: {
9597  if (coord_types[0] == RADIAL_COORD || coord_types[1] == RADIAL_COORD) {
9598  ks_pivot_linear_center_symk(view_order, shot_dist.encodes_per_shot, center);
9599  } else {
9600  ks_lcpo_assign_encodes(views, kacq->num_coords, mtf_direction,
9601  mtf_profile == SPLIT_SHOTS_LCPO ? 1 : 0,
9602  etl, center);
9603  }
9604  break;
9605  } case LINEAR_ROLL: {
9606  ks_pe_linear_roll(view_order, shot_dist.encodes_per_shot, center);
9607  break;
9608  } case PIVOT_ROLL: {
9609  ks_pe_pivot_roll(view_order, shot_dist.encodes_per_shot, center);
9610  break;
9611  } case LINEAR_SWEEP: {
9612  ks_pe_linear(view_order, shot_dist.encodes_per_shot);
9613  break;
9614  }
9615  default:
9616  free(views);
9617  return KS_THROW("MTF profile %d not implemented", (int)mtf_profile);
9618  break;
9619  }
9620  } else if ((mtf_direction == MTF_R) || (mtf_direction == MTF_T)) {
9621 /* ks_pivot_specific_center_radk(view_order, shot_dist.encodes_per_shot, center); */
9622  ks_pe_permute_pclo_rad(view_order, shot_dist.encodes_per_shot, center);
9623 
9624  }
9625 
9626  if (view_order[0] != KS_NOTSET) {
9627  /* Permute has been set - assign encodes */
9628  /* Sort KCOORDS according to their radius or ky coordinate */
9629  switch (mtf_direction) {
9630  case MTF_R: {
9631  qsort(views, kacq->num_coords, sizeof(KS_VIEW), &ks_comp_kview_r_t);
9632  break;
9633  }
9634  case MTF_T: {
9635  qsort(views, kacq->num_coords, sizeof(KS_VIEW), &ks_comp_kview_t_r);
9636  break;
9637  }
9638  case MTF_Y: {
9639  qsort(views, kacq->num_coords, sizeof(KS_VIEW), &ks_comp_kview_y_z);
9640  break;
9641  }
9642  case MTF_Z: {
9643  qsort(views, kacq->num_coords, sizeof(KS_VIEW), &ks_comp_kview_z_y);
9644  break;
9645  }
9646  default:
9647  {
9648  return ks_error("Unknown mtf_direction: %i", mtf_direction);
9649  }
9650  }
9651 
9652  /* Assign each point an encode ("echo" in an echo train) according to the desired view order (center out etc)
9653  The linear indexing (idx) is a consequence of the sort above */
9654  const int readout_end = shot_dist.readout_start + shot_dist.encodes_per_shot;
9655  for (idx = 0, readout = shot_dist.readout_start; readout < readout_end; readout++) {
9656  for (train = 0; train < shot_dist.shots; train++) {
9657  int block = train < shot_dist.first_blocks ? 0 : 1;
9658  if (block == 1 && readout == (readout_end -1)) {
9659  continue;
9660  }
9661  if (idx < kacq->num_coords) {
9662  views[idx].encode = view_order[readout];
9663  }
9664  idx++;
9665  }
9666  }
9667  }
9668 
9669  /* Now each point has an encode (or TE if you prefer). It's time to sort the encodes (TE's) for the desired order. */
9670  switch (encode_shot_assignment_order) {
9671  case T_R:
9672  {
9673  /* Sort for encode->theta then r. This enforces a spoke in ky-kz when we later pick encodes in a train.
9674  Note that radial is not at all related to elliptical k-space coverage. */
9675  qsort(views, kacq->num_coords, sizeof(KS_VIEW), &ks_comp_kview_e_tr);
9676  break;
9677  }
9678  case R_T:
9679  {
9680  /* Sort for encode->r then theta. This enforces a ring in ky-kz when we later pick encodes in a train. */
9681  qsort(views, kacq->num_coords, sizeof(KS_VIEW), &ks_comp_kview_e_rt);
9682  break;
9683  }
9684  case Y_Z:
9685  {
9686  /* Sort for encode->kz. This enforces a column in ky-kz when we later pick encodes in a train */
9687  qsort(views, kacq->num_coords, sizeof(KS_VIEW), &ks_comp_kview_e_yz);
9688  break;
9689  }
9690  case Z_Y:
9691  {
9692  /* Sort for encode->ky. This enforces a row in ky-kz when we later pick encodes in a train */
9693  qsort(views, kacq->num_coords, sizeof(KS_VIEW), &ks_comp_kview_e_zy);
9694  break;
9695  }
9696 
9697  case Y_R:
9698  {
9699  /* Sort for encode->kz. This enforces a column in ky-kz when we later pick encodes in a train */
9700  qsort(views, kacq->num_coords, sizeof(KS_VIEW), &ks_comp_kview_e_yr);
9701  break;
9702  }
9703  case Z_R:
9704  {
9705  /* Sort for encode->ky. This enforces a row in ky-kz when we later pick encodes in a train */
9706  qsort(views, kacq->num_coords, sizeof(KS_VIEW), &ks_comp_kview_e_zr);
9707  break;
9708  }
9709 
9710  case SEC_T:
9711  {
9712  qsort(views, kacq->num_coords, sizeof(KS_VIEW), &ks_comp_kview_e_sec_t);
9713  break;
9714  }
9715 
9716  default:
9717  {
9718  return ks_error("Unknown encode_shot_assignment_order: %i", encode_shot_assignment_order);
9719  }
9720  }
9721 
9722  /* Make the actual phase encoding plan */
9723  STATUS status = ks_phaseencoding_alloc(peplan, etl, shot_dist.shots);
9724  KS_RAISE(status);
9725 
9726  int yshift = coord_types[0] == CARTESIAN_COORD ? kacq->matrix_size[YGRAD]/2 : 0;
9727  int zshift = coord_types[1] == CARTESIAN_COORD ? kacq->matrix_size[ZGRAD]/2 : 0;
9728 
9729  status = ks_peplan_assign_shots(peplan, views, kacq->num_coords, &shot_dist, 0, yshift, zshift, is3d);
9730  free(views);
9731 
9732  KS_RAISE(status);
9733 
9734  return SUCCESS;
9735 }
void ks_pivot_linear_center_symk(int *view_order, int ETL, int c)
ADDTITLEHERE
Definition: KSFoundation_host.c:8736
int shots
Definition: KSFoundation.h:2166
STATUS ks_peplan_assign_shots(KS_PHASEENCODING_PLAN *peplan, const KS_VIEW *const views, const int num_views, const KS_PEPLAN_SHOT_DISTRIBUTION *const shot_dist, const int first_shot_number, const int y_offset, const int z_offset, const int is3D)
Definition: KSFoundation_host.c:9041
STATUS ks_phaseencoding_alloc(KS_PHASEENCODING_PLAN *phaseenc_plan_ptr, int etl, int num_shots) WARN_UNUSED_RESULT
Reallocate memory for KS_PHASEENCODING_PLAN entries
Definition: KSFoundation_common.c:746
int ks_comp_kview_e_zy(const void *a, const void *b)
Definition: KSFoundation_host.c:8492
void ks_pe_pivot_roll(int *view_order, int ETL, int c)
ADDTITLEHERE
Definition: KSFoundation_host.c:8706
#define KS_NOTSET
Definition: KSFoundation.h:115
Definition: KSFoundation.h:2140
STATUS ks_lcpo_assign_encodes(KS_VIEW *views, const int num_coords, KS_MTF_DIRECTION mtf_direction, const int split_shots, const int etl, const int center)
Definition: KSFoundation_host.c:9312
int ks_comp_kview_t_r(const void *a, const void *b)
Definition: KSFoundation_host.c:8356
KS_KCOORD * coords
Definition: KSFoundation.h:2228
STATUS ks_error(const char *format,...) __attribute__((format(printf
Common error message function for HOST and TGT
int readout_start
Definition: KSFoundation.h:2169
Definition: KSFoundation.h:2105
int matrix_size[3]
Definition: KSFoundation.h:2230
STATUS ks_generate_peplan_from_kcoords_american(const KS_KSPACE_ACQ *kacq, KS_PHASEENCODING_PLAN *peplan, const KS_MTF_DIRECTION mtf_direction, const int etl, const int center)
Definition: KSFoundation_host.c:9078
int ks_comp_kview_e_yz(const void *a, const void *b)
Definition: KSFoundation_host.c:8509
void ks_pivot_specific_center_symk(int *view_order, int ETL, int c)
ADDTITLEHERE
Definition: KSFoundation_host.c:8813
Definition: KSFoundation.h:2138
void ks_pe_linear(int *view_order, int ETL)
ADDTITLEHERE
Definition: KSFoundation_host.c:8676
Definition: KSFoundation.h:2139
int ks_comp_kview_e_sec_t(const void *a, const void *b)
Definition: KSFoundation_host.c:8560
int ks_comp_kview_e_zr(const void *a, const void *b)
Definition: KSFoundation_host.c:8543
Definition: KSFoundation.h:2142
int ks_peplan_find_center_from_linear_sweep(KS_KCOORD *const K, KS_VIEW *views_in, const int num_coords, const KS_MTF_DIRECTION mtf_direction, const int sweep_sign, const int segment_size, const int start_encode)
ADDTITLEHERE
Definition: KSFoundation_host.c:8977
int encodes_per_shot
Definition: KSFoundation.h:2168
Definition: KSFoundation.h:1762
KS_PEPLAN_SHOT_DISTRIBUTION ks_peplan_distribute_shots(const int etl, const int num_coords, const int center)
ADDTITLEHERE
Definition: KSFoundation_host.c:8955
KS_KCOORD * coord
Definition: KSFoundation.h:2215
int first_blocks
Definition: KSFoundation.h:2170
STATUS ks_pe_permute_pclo_rad(int *view_order, const int etl, const int center)
ADDTITLEHERE
Definition: KSFoundation_host.c:9266
int ks_comp_kview_e_rt(const void *a, const void *b)
Definition: KSFoundation_host.c:8475
Definition: KSFoundation.h:2124
Definition: KSFoundation.h:2108
Definition: KSFoundation.h:2118
int z
Definition: KSFoundation.h:2202
ADDTITLEHERE
Definition: KSFoundation.h:2165
Definition: KSFoundation.h:2127
int ks_comp_kview_z_y(const void *a, const void *b)
Definition: KSFoundation_host.c:8398
Definition: KSFoundation.h:2143
Definition: KSFoundation.h:2145
#define KS_RAISE(status)
Definition: KSFoundation.h:190
int ks_comp_kview_r_t(const void *a, const void *b)
ADDTITLEHERE
Definition: KSFoundation_host.c:8335
int ks_comp_kview_y_z(const void *a, const void *b)
ADDTITLEHERE
Definition: KSFoundation_host.c:8378
int ks_comp_kview_e_tr(const void *a, const void *b)
Definition: KSFoundation_host.c:8458
KS_COORDINATE_TYPE
ADDTITLEHERE
Definition: KSFoundation.h:1761
Definition: KSFoundation.h:2121
Definition: KSFoundation.h:2141
Definition: KSFoundation.h:1763
ADDTITLEHERE
Definition: KSFoundation.h:2214
int encode
Definition: KSFoundation.h:2216
int ks_comp_kview_e_yr(const void *a, const void *b)
Definition: KSFoundation_host.c:8526
Definition: KSFoundation.h:2107
ADDTITLEHERE
Definition: KSFoundation.h:2200
int num_coords
Definition: KSFoundation.h:2229
#define KS_THROW(format,...)
Definition: KSFoundation.h:181
KS_KSPACE_ACQ kacq
Definition: ksepi_implementation.e:355
Definition: KSFoundation.h:2122
Definition: KSFoundation.h:2125
int train
Definition: KSFoundation.h:2217
void ks_pe_linear_roll(int *view_order, int ETL, int c)
ADDTITLEHERE
Definition: KSFoundation_host.c:8686
Definition: KSFoundation.h:2119
Definition: KSFoundation.h:2106
STATUS ks_generate_peplan_from_kcoords_dutch(const KS_KSPACE_ACQ *kacq, KS_PHASEENCODING_PLAN *peplan, KS_MTF_DIRECTION mtf_direction, const int etl, const int center)
Definition: KSFoundation_host.c:9227
Definition: KSFoundation.h:2144

◆ ks_generate_peplan_from_kcoords_american()

STATUS ks_generate_peplan_from_kcoords_american ( const KS_KSPACE_ACQ kacq,
KS_PHASEENCODING_PLAN peplan,
const KS_MTF_DIRECTION  mtf_direction,
const int  etl,
const int  center 
)

<-— center
^ *
| *
ky *

                     Time ->                        
9082  {
9083 
9085 
9086  int midshots, idx;
9087  KS_VIEW* views =(KS_VIEW*) malloc(kacq->num_coords * sizeof(KS_VIEW));
9088 
9089  int midcoords = 0;
9090  int readout, train;
9091  int is3d = 0;
9092 
9093  KS_PEPLAN_SHOT_DISTRIBUTION mid_shots;
9094  KS_PEPLAN_SHOT_DISTRIBUTION outer_shots;
9095  /* Figure out center by sorting by radius */
9096  KS_KCOORD* K = kacq->coords;
9097  int center_line = mtf_direction == MTF_Z ? kacq->matrix_size[ZGRAD]
9098  : kacq->matrix_size[YGRAD];
9099 
9100  /* Create views and figure out if this is a 3D peplan */
9101  for (idx = 0; idx < kacq->num_coords; idx++) {
9102  is3d |= (K[0].z != K[idx].z);
9103  views[idx].coord = &(K[idx]);
9104  views[idx].train = KS_NOTSET;
9105  }
9106 
9107  if (is3d == 0 && mtf_direction == MTF_Z) {
9108  free(views);
9109  return KS_THROW("Requested MTF along Z ordering with 2D coordinates");
9110  }
9111 
9112  int found_solution = 0;
9113  for (midshots = 1; midshots < unsplit_shots.shots; ) {
9114  midcoords = midshots * unsplit_shots.encodes_per_shot;
9115  mid_shots = ks_peplan_distribute_shots(etl, midcoords, center);
9116  int mid_readout_end = mid_shots.readout_start + mid_shots.encodes_per_shot;
9117 
9118  /* Reset encodes */
9119  for (idx = 0; idx < kacq->num_coords; idx++) {
9120  views[idx].encode = -1000;
9121  }
9122 
9123  /* Sort views z->y or y->z */
9124  if (mtf_direction == MTF_Z) {
9125  qsort(views, kacq->num_coords, sizeof(KS_VIEW), &ks_comp_kview_z_y);
9126  } else {
9127  qsort(views, kacq->num_coords, sizeof(KS_VIEW), &ks_comp_kview_y_z);
9128  }
9129 
9130  /* Assign encodes to midcoord views (center -> out) */
9131  idx = 0;
9132  for (readout = mid_readout_end-1; readout >= mid_shots.readout_start; readout--) {
9133  for (train = 0; train < midshots; train++) {
9134  int block = train < mid_shots.first_blocks ? 0 : 1;
9135  if (block == 1 && readout == (mid_readout_end -1)) {
9136  continue;
9137  }
9138 
9139  /* Only assign midcoords */
9140  if (idx < midcoords) {
9141  views[idx].encode = readout;
9142  }
9143  idx++;
9144  }
9145  }
9146 
9147  if (idx != midcoords) {
9148  ks_error("Something is wrong with the midcoords");
9149  }
9150 
9151  /* Sort midcoords encode->z or encode->y */
9152  if (mtf_direction == MTF_Z) {
9153  qsort(views, midcoords, sizeof(KS_VIEW), &ks_comp_kview_e_zy);
9154  } else {
9155  qsort(views, midcoords, sizeof(KS_VIEW), &ks_comp_kview_e_yz);
9156  }
9157 
9158  /* Figure out the center encode */
9159  int current_center_encode, center_distance_late, center_distance_early, e;
9160  for (e = 0; e < mid_shots.encodes_per_shot; e++) {
9161  if (mtf_direction == MTF_Z) {
9162  center_distance_late = views[(e+1) * midshots - 1].coord->z - center_line;
9163  center_distance_early = views[e * midshots].coord->z - center_line;
9164  } else {
9165  center_distance_late = views[(e+1) * midshots - 1].coord->y - center_line;
9166  center_distance_early = views[e * midshots].coord->y - center_line;
9167  }
9168  if (center_distance_early <= 0 && center_distance_late >= 0) { /* distance 0 is inside the encode section */
9169  current_center_encode = e;
9170  found_solution = 1;
9171  break;
9172  }
9173  }
9174  if (current_center_encode == center) { /* Found it */
9175  break;
9176  } else {
9177  /* Incorrect center encode -> increase midshots and try again */
9178  midshots++;
9179  continue;
9180  }
9181  } /* for midshots */
9182 
9183  if (!found_solution) {
9184  free(views);
9185  return KS_THROW("Could not split shots to get center = %d", center);
9186  }
9187  /* Split encodes between remaining shots */
9188  const int rest_num_coords = kacq->num_coords - midcoords;
9189  outer_shots = ks_peplan_distribute_shots(etl, rest_num_coords, center);
9190 
9191  STATUS status = ks_phaseencoding_alloc(peplan, etl, outer_shots.shots + mid_shots.shots);
9192  KS_RAISE(status);
9193 
9194  const int outer_readout_end = outer_shots.readout_start + outer_shots.encodes_per_shot;
9195  /* Loop through the block (which is shifted to the desired encode) and assign the entries */
9196  for (readout = outer_shots.readout_start; readout < outer_readout_end; readout++) {
9197  for (train = 0; train < outer_shots.shots; train++) {
9198  int block = train < outer_shots.first_blocks ? 0 : 1;
9199  if (block == 1 && readout == (outer_readout_end -1)) {
9200  continue;
9201  }
9202  if (idx >= kacq->num_coords) {
9203  free(views);
9204  return KS_THROW("Trying to assign more coordinates than what is available");
9205  }
9206  views[idx++].encode = readout;
9207  }
9208  }
9209 
9210  /* Sort outer coordinates (either encode->z or encode->y) */
9211  if (mtf_direction == MTF_Z) {
9212  qsort(views + midcoords, rest_num_coords, sizeof(KS_VIEW), &ks_comp_kview_e_zy);
9213  } else {
9214  qsort(views + midcoords, rest_num_coords, sizeof(KS_VIEW), &ks_comp_kview_e_yz);
9215  }
9216 
9217  KS_RAISE(ks_peplan_assign_shots(peplan, views, midcoords, &mid_shots, 0, kacq->matrix_size[YGRAD]/2, kacq->matrix_size[ZGRAD]/2, is3d));
9218  KS_RAISE(ks_peplan_assign_shots(peplan, views + midcoords, rest_num_coords, &outer_shots, midshots, kacq->matrix_size[YGRAD]/2, kacq->matrix_size[ZGRAD]/2, is3d));
9219 
9220  free(views);
9221  return SUCCESS;
9222 }
int shots
Definition: KSFoundation.h:2166
STATUS ks_peplan_assign_shots(KS_PHASEENCODING_PLAN *peplan, const KS_VIEW *const views, const int num_views, const KS_PEPLAN_SHOT_DISTRIBUTION *const shot_dist, const int first_shot_number, const int y_offset, const int z_offset, const int is3D)
Definition: KSFoundation_host.c:9041
STATUS ks_phaseencoding_alloc(KS_PHASEENCODING_PLAN *phaseenc_plan_ptr, int etl, int num_shots) WARN_UNUSED_RESULT
Reallocate memory for KS_PHASEENCODING_PLAN entries
Definition: KSFoundation_common.c:746
int ks_comp_kview_e_zy(const void *a, const void *b)
Definition: KSFoundation_host.c:8492
#define KS_NOTSET
Definition: KSFoundation.h:115
KS_KCOORD * coords
Definition: KSFoundation.h:2228
STATUS ks_error(const char *format,...) __attribute__((format(printf
Common error message function for HOST and TGT
int readout_start
Definition: KSFoundation.h:2169
int matrix_size[3]
Definition: KSFoundation.h:2230
int ks_comp_kview_e_yz(const void *a, const void *b)
Definition: KSFoundation_host.c:8509
int encodes_per_shot
Definition: KSFoundation.h:2168
KS_PEPLAN_SHOT_DISTRIBUTION ks_peplan_distribute_shots(const int etl, const int num_coords, const int center)
ADDTITLEHERE
Definition: KSFoundation_host.c:8955
KS_KCOORD * coord
Definition: KSFoundation.h:2215
int first_blocks
Definition: KSFoundation.h:2170
int z
Definition: KSFoundation.h:2202
ADDTITLEHERE
Definition: KSFoundation.h:2165
int ks_comp_kview_z_y(const void *a, const void *b)
Definition: KSFoundation_host.c:8398
#define KS_RAISE(status)
Definition: KSFoundation.h:190
int ks_comp_kview_y_z(const void *a, const void *b)
ADDTITLEHERE
Definition: KSFoundation_host.c:8378
ADDTITLEHERE
Definition: KSFoundation.h:2214
int encode
Definition: KSFoundation.h:2216
ADDTITLEHERE
Definition: KSFoundation.h:2200
int num_coords
Definition: KSFoundation.h:2229
#define KS_THROW(format,...)
Definition: KSFoundation.h:181
KS_KSPACE_ACQ kacq
Definition: ksepi_implementation.e:355
int y
Definition: KSFoundation.h:2201
int train
Definition: KSFoundation.h:2217
Definition: KSFoundation.h:2106

◆ ks_fse_calcecho()

STATUS ks_fse_calcecho ( double *  bestecho,
double *  optecho,
int *  nacqlines_to_kspacecenter,
const KS_PHASER *const  pe,
KS_PF_EARLYLATE  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 or KS_PF_LATE (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
804  {
805  int numshots, i;
806  int nacqlines_fullside, nacqlines_pfside;
807  double numshots_double;
808 
809  if (etl < 1) {
810  return ks_error("%s: ETL (arg 5) must be at least 1", __FUNCTION__);
811  }
812  if (pe->numlinestoacq < 1) {
813  return ks_error("%s: field '.numlinestoacq' in 3rd arg must be at least 1", __FUNCTION__);
814  }
815  if (etl > pe->numlinestoacq) {
816  return ks_error("%s: ETL (arg 5) cannot exceed number of lines to acquire", __FUNCTION__);
817  }
818  if (esp <= 0) {
819  return ks_error("%s: ESP (arg 6) must be larger than 1", __FUNCTION__);
820  }
821  if (pe->nover < 0) {
822  return ks_error("%s: Only top half of k-space supported for pFourier", __FUNCTION__);
823  }
824 
825  /* Number of shots */
826  numshots = CEIL_DIV(pe->numlinestoacq, etl);
827  numshots_double = (double) pe->numlinestoacq / (double) etl;
828 
829  /* index 'i' will stop incrementing as we pass the center of k-space (i=0 is top row of k-space).
830  Note that 'i' will be much less that res/2 (after break) even for full Fourier when
831  we have R > 1 and acslines */
832  for (i = 0; i < pe->numlinestoacq - 1; i++) {
833  if (((pe->res - 1) / 2.0 - pe->linetoacq[i]) < 0)
834  break;
835  }
836 
837  nacqlines_fullside = i;
838  nacqlines_pfside = pe->numlinestoacq - i; /* which is equal or almost equal to nacqlines_fullside for full Fourier */
839 
840  if (pf_direction == KS_PF_EARLY) {
841  *nacqlines_to_kspacecenter = nacqlines_pfside;
842  } else {
843  *nacqlines_to_kspacecenter = nacqlines_fullside;
844  }
845 
846 
847  if (etl % 2) {
848  /* etl is odd. The optimal echo will straddle around the center of k-space */
849 
850  if (optecho != NULL)
851  *optecho = ceil((((double) *nacqlines_to_kspacecenter + numshots_double / 2.0) / numshots_double) - 1e-5); /* integer value since ETL is odd */
852 
853  if (bestecho != NULL) {
854  *bestecho = floor((double) TE / (double) esp + 0.5); /* integer */
855  if (*bestecho < 1)
856  *bestecho = 1;
857  if (*bestecho > etl)
858  *bestecho = etl;
859  }
860 
861  } else {
862  /* etl is even. The optimal two echoes will be placed above and below k-space center, respectively */
863 
864  if (optecho != NULL)
865  *optecho = ceil((double) *nacqlines_to_kspacecenter / numshots_double) + 0.5; /* half-integer value since ETL is even */
866 
867  if (bestecho != NULL) {
868  if (etl == 2) {
869  /* special case for ETL = 2 */
870  if (TE < 1.25 * esp)
871  *bestecho = 1; /* 1st echo straddling around k-space center */
872  else if (TE < 1.75 * esp)
873  *bestecho = 1.5; /* 1st/end echo below/above k-space center */
874  else
875  *bestecho = 2; /* 2nd echo straddling around k-space center */
876  } else {
877  *bestecho = floor((double) TE / (double) esp) + 0.5; /* half-integer */
878  if (*bestecho <= 1.5)
879  *bestecho = 1; /* minTE, back to integer 'bestecho' for center-out encoding */
880  if (*bestecho >= (etl - 0.5))
881  *bestecho = etl; /* max TE, back to integer 'bestecho' for out-center encoding */
882  }
883  }
884 
885  }
886 
887  if (optecho != NULL) {
888  /* Partial Fourier MinTE case, regardless if ETL is even or odd */
889  if ((pe->nover > 0) && (*nacqlines_to_kspacecenter <= numshots / 2))
890  *optecho = 1;
891 
892  /* Partial Fourier MaxTE case, regardless if ETL is even or odd */
893  if ((pe->nover < 0) && (*nacqlines_to_kspacecenter >= (pe->linetoacq[i] - numshots / 2)))
894  *optecho = etl;
895  }
896 
897  return SUCCESS;
898 
899 } /* ks_fse_calcecho() */
int res
Definition: KSFoundation.h:1721
int numlinestoacq
Definition: KSFoundation.h:1727
STATUS ks_error(const char *format,...)
Definition: KSFoundation_common.c:78
int linetoacq[KS_MAX_PHASEDYN]
Definition: KSFoundation.h:1728
Definition: KSFoundation.h:386
int nover
Definition: KSFoundation.h:1722

◆ 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_PF_EARLYLATE  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 or KS_PF_LATE (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
999  {
1000 
1001  int numileaves = epitrain->blipphaser.R;
1002 
1003  if (epitrain->blipphaser.numlinestoacq < 2 && (numileaves != epitrain->blipphaser.res)) {
1004  return ks_error("%s: field '.numlinestoacq' of epitrain->blipphaser must be at least 2, not %d", __FUNCTION__, epitrain->blipphaser.numlinestoacq);
1005  }
1006 
1007  strncpy(phaseenc_plan_ptr->description, desc, KS_DESCRIPTION_LENGTH - 1);
1008  phaseenc_plan_ptr->description[KS_DESCRIPTION_LENGTH - 1] = 0;
1009 
1010  /* don't allow empty description or a description with leading space */
1011  if (phaseenc_plan_ptr->description == NULL || phaseenc_plan_ptr->description[0] == ' ') {
1012  return ks_error("%s: description of phase encoding plan (2nd arg) cannot be NULL or leading space", __FUNCTION__);
1013  }
1014 
1015  int readout, shot, interleaf, kyview, kzview, kzind;
1016  int etl, numfullsegments, numshotsforsegment, totalnumshots, i;
1017  int shift;
1018  STATUS status;
1019 
1020  /* Number of shots (2D) */
1021  if (numileavestoacq < 1 || numileavestoacq > numileaves || (numileaves % numileavestoacq != 0)) {
1022  return ks_error("%s: numileavestoacq (=%d) must be in range 1-blipphaser.R and an integer factor of blipphaser.R (=%d)", __FUNCTION__, numileavestoacq, numileaves);
1023  }
1024 
1025  /* Number of segments (3D) */
1026  if (epitrain->zphaser.res != KS_NOTSET && (numsegments < 1 || numsegments > epitrain->zphaser.numlinestoacq)) {
1027  return ks_error("%s: numsegments (=%d) must be in range 1-zphaser.numlinestoacq (=%d)", __FUNCTION__, numsegments, epitrain->zphaser.numlinestoacq);
1028  }
1029 
1030  /* Number of readouts per EPI train (etl) */
1031  etl = epitrain->blipphaser.numlinestoacq;
1032 
1033  /* Check number of kz segments and how many should be fully sampled (controlled by zphaser.nacslines) */
1034  if (epitrain->zphaser.res == KS_NOTSET) { /* 2D epi */
1035  numsegments = 1;
1036  numfullsegments = (numileavestoacq == numileaves);
1037  } else { /* 3D epi */
1038  if (epitrain->zphaser.R == 1) {
1039  numfullsegments = epitrain->zphaser.nacslines; /* special interpretation for zR=1 */
1040  } else { /* zR > 1 */
1041  if (epitrain->zphaser.nacslines > 0) {
1042  numfullsegments = 1; /* initialize */
1043  for (kzind = 1; kzind < numsegments; kzind++) {
1044  if (abs(epitrain->zphaser.linetoacq[kzind]-epitrain->zphaser.linetoacq[kzind-1])==1) {
1045  numfullsegments++;
1046  }
1047  }
1048  } else { /* no kz "acs lines" */
1049  numfullsegments = 0;
1050  }
1051  }
1052  }
1053 
1054  /* allocate KS_PHASEENCODING_PLAN table (all entries will be initialized to KS_NOTSET) */
1055  totalnumshots = numfullsegments * numileaves + (numsegments-numfullsegments) * numileavestoacq;
1056  status = ks_phaseencoding_alloc(phaseenc_plan_ptr, etl, totalnumshots);
1057  KS_RAISE(status);
1058 
1059  shot = 0; /* initialize */
1060  int ileaves[numileaves]; /* interleaf indices */
1061 
1062  for (kzind = 0; kzind < numsegments; kzind++) {
1063  kzview = (epitrain->zphaser.numlinestoacq > 0) ? epitrain->zphaser.linetoacq[kzind] : KS_NOTSET;
1064 
1065  if (kzview != KS_NOTSET && abs(kzview*2 - (epitrain->zphaser.res-1)) <= numfullsegments) {
1066  numshotsforsegment = numileaves;
1067  } else {
1068  numshotsforsegment = numileavestoacq;
1069  }
1070 
1071  if (sweep_order == KS_SWEEP_ORDER_TOP_DOWN) {
1072  for (i = 0; i < numshotsforsegment; i++) {
1073  ileaves[i] = i;
1074  }
1075  } else if (sweep_order == KS_SWEEP_ORDER_BOTTOM_UP) {
1076  for (i = 0; i < numshotsforsegment; i++) {
1077  ileaves[numshotsforsegment - i - 1] = i;
1078  }
1079  } else if (sweep_order == KS_SWEEP_ORDER_OUTSIDE_IN) {
1080  for (i = 0; i < numshotsforsegment; i++) {
1081  ileaves[i] = (i % 2) ? numshotsforsegment - 1 - i/2 : i/2;
1082  }
1083  } else if (sweep_order == KS_SWEEP_ORDER_CENTER_OUT) {
1084  for (i = 0; i < numshotsforsegment; i++) {
1085  ileaves[numshotsforsegment - i - 1] = (i % 2) ? numshotsforsegment - 1 - i/2 : i/2;
1086  }
1087  }
1088  for (i = 0; i < numshotsforsegment; i++) {
1089  interleaf = ileaves[i];
1090  for (readout = 0; readout < etl; readout++) {
1091  shift = interleaf * (numileaves / numshotsforsegment); /* interleaf shift */
1092  shift = (shift + (kzview/epitrain->zphaser.R) * caipi_delta) % numileaves; /* CAIPIRINHA shift */
1093  if ((epitrain->blipphaser.nover != 0) && (((pf_direction == KS_PF_LATE) && (blipsign == KS_EPI_POSBLIPS)) || ((pf_direction == KS_PF_EARLY) && (blipsign == KS_EPI_NEGBLIPS))) ) {
1094  /* shift kspace area to lower half */
1095  shift += epitrain->blipphaser.res - (epitrain->blipphaser.res/2 + epitrain->blipphaser.nover);
1096  }
1097  if (blipsign == KS_EPI_NEGBLIPS) {
1098  kyview = epitrain->blipphaser.linetoacq[readout] + shift; /* admittedly, .linetoacq[] has lost its role a bit for EPI with all shifting we do */
1099  } else if (blipsign == KS_EPI_POSBLIPS) {
1100  kyview = epitrain->blipphaser.linetoacq[(etl - 1) - readout] + shift; /* admittedly, .linetoacq[] has lost its role a bit for EPI with all shifting we do */
1101  } else if (blipsign == KS_EPI_NOBLIPS) {
1102  kyview = KS_NOTSET;
1103  kzview = KS_NOTSET;
1104  } else {
1105  return ks_error("%s: blipsign must be KS_EPI_POSBLIPS or KS_EPI_NEGBLIPS, not %d", __FUNCTION__, blipsign);
1106  }
1107  ks_phaseencoding_set(phaseenc_plan_ptr, readout, shot, kyview, kzview);
1108  } /* readout */
1109  shot++;
1110  } /* interleaf */
1111  } /* kzind */
1112 
1113  return SUCCESS;
1114 
1115 } /* ks_phaseencoding_generate_epi() */
void ks_phaseencoding_set(KS_PHASEENCODING_PLAN *phaseenc_plan_ptr, int encode, int shot, int ky, int kz)
Set [ky,kz] coordinate from KS_PHASEENCODING_PLAN for given encode and shot
Definition: KSFoundation_common.c:653
int R
Definition: KSFoundation.h:1723
int res
Definition: KSFoundation.h:1721
Definition: KSFoundation.h:388
int nacslines
Definition: KSFoundation.h:1724
Definition: KSFoundation.h:2083
#define KS_NOTSET
Definition: KSFoundation.h:115
KS_PHASER blipphaser
Definition: KSFoundation.h:1937
int numlinestoacq
Definition: KSFoundation.h:1727
STATUS ks_error(const char *format,...)
Definition: KSFoundation_common.c:78
KS_PHASER zphaser
Definition: KSFoundation.h:1938
int linetoacq[KS_MAX_PHASEDYN]
Definition: KSFoundation.h:1728
Definition: KSFoundation.h:386
#define KS_DESCRIPTION_LENGTH
Definition: KSFoundation.h:253
Definition: KSFoundation.h:2084
KS_DESCRIPTION description
Definition: KSFoundation.h:1791
Definition: KSFoundation.h:2081
Definition: KSFoundation.h:2082
Definition: KSFoundation.h:2330
#define KS_RAISE(status)
Definition: KSFoundation.h:190
int nover
Definition: KSFoundation.h:1722
Definition: KSFoundation.h:2330
Definition: KSFoundation.h:2330
STATUS ks_phaseencoding_alloc(KS_PHASEENCODING_PLAN *phaseenc_plan_ptr, int etl, int num_shots)
Reallocate memory for KS_PHASEENCODING_PLAN entries
Definition: KSFoundation_common.c:746

◆ 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
6295  {
6296 
6297  return ks_calc_sliceplan_interleaved(slice_plan, nslices, slperpass, 2);
6298 
6299 }
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:6304

◆ 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
6304  {
6305 
6306  if (!slice_plan) {
6307  return ks_error("%s: invalid input, slice_plan is NULL", __FUNCTION__);
6308  }
6309 
6310  if (nslices <= 0 || slperpass <= 0 || ninterleaves <= 0) {
6311  return ks_error("%s: invalid input (nslices: %d, slperpass: %d, ninterleaves: %d)",
6312  __FUNCTION__, nslices, slperpass, ninterleaves);
6313  }
6314 
6315  slice_plan->nslices = nslices;
6316  slice_plan->npasses = CEIL_DIV(nslices, slperpass);
6317  slice_plan->nslices_per_pass = slperpass;
6318 
6319  int nslicesthispass[slice_plan->npasses];
6320  int sllocthispass[slice_plan->npasses][slperpass];
6321  int i, p, s, k, t, interleaf;
6322  /*
6323  - slice_plan->npasses: a.k.a. number of acquisitions (standard GE CV is: `acqs`)
6324  - slperpass: maximum number of slices in a pass (some passes may have fewer slices)
6325  - nslicesthispass[slice_plan->npasses]: Array where each element indicates how many slices to acquired in this pass
6326  - sllocthispass[slice_plan->npasses][slperpass]: Spatial location index into the prescribed slice stack, i.e. all slices
6327  for current pass and pass_sliceindx (spatially sorted)
6328  */
6329 
6330  if (slice_plan->npasses == slice_plan->nslices) {
6331  /* Sequential scanning, opirmode = 1 (i.e. fill k-space fully for each slice, then change slice). We don't want
6332  to acquire them sequentially in space though to avoid cross talk. Therefore, interleaves are made over the slice stack. */
6333 
6334  p = 0;
6335  for (interleaf = 0; interleaf < ninterleaves; interleaf++) {
6336  /* interleaf: slice interleaves over stack (typically odd/even) */
6337  for (i = 0; i < CEIL_DIV(slice_plan->nslices, ninterleaves); i++) {
6338  k = interleaf + i * ninterleaves;
6339  slice_plan->acq_order[p].slloc = k; /* Spatial location index into the prescribed slice stack */
6340  slice_plan->acq_order[p].slpass = p; /* pass index */
6341  slice_plan->acq_order[p].sltime = 0; /* time index in current pass = 0 */
6342  p++;
6343  }
6344  }
6345 
6346  } else {
6347  /* Standard, interleaved scanning, opirmode = 0. Interleaves are made within passes. */
6348 
6349  for (p = 0; p < slice_plan->npasses; p++) {
6350  nslicesthispass[p] = 0;
6351  for (s = 0; s < slperpass; s++) {
6352  sllocthispass[p][s] = -1;
6353  if (s * slice_plan->npasses + p < nslices) {
6354  nslicesthispass[p]++;
6355  sllocthispass[p][s] = p + (s * slice_plan->npasses);
6356  }
6357  }
6358  }
6359 
6360  for (p = 0; p < slice_plan->npasses; p++) {
6361  t = 0; /* linearly increasing time index in current pass */
6362  for (interleaf = 0; interleaf < ninterleaves; interleaf++) {
6363  /* interleaf: slice interleaves *within* each pass (typically odd/even) */
6364  for (i = 0; i < CEIL_DIV(nslicesthispass[p], ninterleaves); i++) {
6365  k = interleaf + i * ninterleaves; /* pass_sliceindx */
6366  if (k < nslicesthispass[p]) {
6367  slice_plan->acq_order[sllocthispass[p][k]].slloc = sllocthispass[p][k]; /* Spatial location index into the prescribed slice stack */
6368  slice_plan->acq_order[sllocthispass[p][k]].slpass = p; /* pass index */
6369  slice_plan->acq_order[sllocthispass[p][k]].sltime = t++; /* time index in current pass. 0->nslicesthispass[p] */
6370  }
6371  }
6372  }
6373  }
6374 
6375  } /* sequential scanning or not */
6376 
6377 
6378  return SUCCESS;
6379 }
DATA_ACQ_ORDER acq_order[SLICE_FACTOR *DATA_ACQ_MAX]
Definition: KSFoundation.h:1321
STATUS ks_error(const char *format,...) __attribute__((format(printf
Common error message function for HOST and TGT
int nslices_per_pass
Definition: KSFoundation.h:1320
int npasses
Definition: KSFoundation.h:1319
int nslices
Definition: KSFoundation.h:1318

◆ 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
6384  {
6385  int slices_per_band, passes_per_band;
6386  int i;
6387  STATUS status;
6388 
6389  /* sanitize the input */
6390  multiband_factor = multiband_factor < 1 ? 1 : multiband_factor;
6391  nslices = nslices < 0 ? 1 : nslices;
6392  slperpass = slperpass < 0 ? 1 : slperpass;
6393 
6394  if (slperpass > nslices)
6395  slperpass = nslices;
6396 
6397  /* no SMS?, fall back to standard slice plan */
6398  if (multiband_factor == 1) {
6399  status = ks_calc_sliceplan(slice_plan, nslices, slperpass);
6400  return status;
6401  }
6402 
6403  /* a 'band' is a group of slices out of the prescribed slices (opslquant), where the #slices in the stack is equal to
6404  opslquant / multiband_factor. The stack does only cover 1/multiband_factor of the entire slice FOV (i.e. not interleaved).
6405  The exact number of slices in each stack is ceil(opslquant/multiband_factor), which may often lead to that more slices than
6406  prescribed need to be acquired for divisibility */
6407  slices_per_band = CEIL_DIV(nslices, multiband_factor);
6408 
6409  /* number of passes per TR for one slice stack */
6410  passes_per_band = CEIL_DIV(slices_per_band, slperpass);
6411 
6412  if (passes_per_band == 1) {
6413  /* we can fit all slices in the stack in 1 pass (acquisition), i.e. one TR. Do SMS-specific slice interleaving */
6414  ks_calc_slice_acquisition_order_smssingleacq(slice_plan->acq_order, slices_per_band);
6415  } else {
6416  /* need 2+ acqs to acquire the 'slices_per_band' slices. Do standard slice interleaving */
6417  ks_calc_sliceplan(slice_plan, slices_per_band, slperpass);
6418  }
6419 
6420  /* copy to stacks 2->multiband_factor */
6421  slice_plan->nslices = slices_per_band * multiband_factor;
6422  slice_plan->npasses = passes_per_band * multiband_factor;
6423 
6424  for (i = slices_per_band; i < slice_plan->nslices; i++) {
6425  int slice_in_stack = i % slices_per_band;
6426  int stack = i / slices_per_band;
6427  slice_plan->acq_order[i].slloc = i;
6428  slice_plan->acq_order[i].slpass = slice_plan->acq_order[slice_in_stack].slpass + stack * passes_per_band;
6429  slice_plan->acq_order[i].sltime = slice_plan->acq_order[slice_in_stack].sltime;
6430  }
6431 
6432  return SUCCESS;
6433 }
DATA_ACQ_ORDER acq_order[SLICE_FACTOR *DATA_ACQ_MAX]
Definition: KSFoundation.h:1321
int npasses
Definition: KSFoundation.h:1319
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:6295
int nslices
Definition: KSFoundation.h:1318
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:6533

◆ ks_sms_check_divisibility()

STATUS ks_sms_check_divisibility ( int *  inpass_interleaves,
const int  sms_factor,
const int  nslices_per_pass,
const int  nslices 
)

Checks that nslices_per_pass is an odd number when accelerating with SMS

If possible, the number of inpass_interleaves will be adjusted to fix the issue.

Parameters
[out]inpass_interleavesInterleave factor of slices in a pass
[in]sms_factorNumber of SMS slices
[in]nslices_per_passNumber of slices per pass (i.e. per TR)
[in]nslicesTotal number of slices
Return values
STATUSSUCCESS or FAILURE
6439  {
6440 
6441  if ((sms_factor > 1) && (!(nslices_per_pass % 2))) {
6442  int npasses = CEIL_DIV(nslices, nslices_per_pass);
6443  int adjusted_ileaves = 0;
6444  if (areSame((float)nslices/nslices_per_pass,(float)npasses)) {
6445  /* Check if adjusting the number of interleaves can help */
6446  int i;
6447  for (i = 4; i > 1; i--) {
6448  int y = CEIL_DIV(nslices_per_pass, i);
6449  float x = (float)(nslices_per_pass) / (float)(y*(i-1) + (y-1));
6450  if (areSame(x,1.0)) {
6451  *inpass_interleaves = i;
6452  adjusted_ileaves = 1;
6453  }
6454  }
6455  }
6456  if (!adjusted_ileaves) {
6457  int down = (nslices_per_pass - 1) * npasses * sms_factor;
6458  int up = (nslices_per_pass + 1) * npasses * sms_factor;
6459  return ks_error("#slices not compatible with SMS, try %d or %d.", down, up);
6460  } else {
6461  ks_dbg("%s: Adjusted inpass_interleaves to %d, for SMS divisibillity.", __FUNCTION__, *inpass_interleaves);
6462  }
6463  }
6464 
6465  return SUCCESS;
6466 }
#define areSame(a, b)
Definition: KSFoundation.h:144
STATUS ks_error(const char *format,...) __attribute__((format(printf
Common error message function for HOST and TGT
STATUS STATUS ks_dbg(const char *format,...) __attribute__((format(printf
Common debug message function for HOST and TGT

◆ 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)
6533  {
6534 
6535  return nslices % 2 ?
6538 }
int ks_calc_slice_acquisition_order_smssingleacq_impl(DATA_ACQ_ORDER *dacq, int nslices, int interleave)
Definition: KSFoundation_host.c:6493

◆ ks_calc_integrate_coords()

float ks_calc_integrate_coords ( float *  t,
float *  G,
int  num_coords 
)

Integrates between coords using a trapezoidal method.

Parameters
[in]tTime points (array with num_coords elements)
[in]GAmplitude (array with num_coords elements)
[in]num_coordsNumber of points in t and G
Return values
floatIntegral (in units of G*t)
6543  {
6544  int i;
6545  if (num_coords < 2) {
6546  return ks_error("%s: At least two coords must be provided", __FUNCTION__);
6547  }
6548 
6549  for (i = 1; i < num_coords; i++) {
6550  if (t[i] < t[i-1]) {
6551  return ks_error("%s: Coords must be in time order", __FUNCTION__);
6552  }
6553  }
6554  float sum = 0.0;
6555  for (i = 0; i < (num_coords -1); i++) {
6556  sum += (t[i+1] - t[i])/2 * (G[i] + G[i+1]);
6557  }
6558  return sum;
6559 }
STATUS ks_error(const char *format,...) __attribute__((format(printf
Common error message function for HOST and TGT

◆ 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

TODO: just works for single acquisitions

Parameters
[out]dacqData acquisition order array (local)
[in]nslicesTotal number of slices prescribed
Return values
intThe minimum time gap between adjacent slices
6471  {
6472  int min_gap = nslices;
6473 
6474  int i;
6475  for (i=1; i<nslices; i++) {
6476  int gap = abs(dacq[i-1].sltime - dacq[i].sltime);
6477  if (gap < min_gap) {
6478  min_gap = gap;
6479  }
6480  }
6481 
6482  int last_to_first = nslices - dacq[nslices-1].sltime;
6483  if (last_to_first < min_gap) {
6484  min_gap = last_to_first;
6485  }
6486 
6487  return min_gap;
6488 }

◆ 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
6639  {
6640  int i;
6641 
6642  fprintf(fp, "\nSlice Plan:\n");
6643  fprintf(fp, "=============================================\n");
6644  fprintf(fp, "Number of slices: %d\n", slice_plan.nslices);
6645  fprintf(fp, "Number of passes: %d\n", slice_plan.npasses);
6646  fprintf(fp, "Slices per pass: %d\n", slice_plan.nslices_per_pass);
6647  fprintf(fp, "\nslpass\tsltime\tslloc\n");
6648  fprintf(fp, "---------------------------------------------\n");
6649  for (i = 0; i < slice_plan.nslices; i++) { /* i = spatial location */
6650  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);
6651  }
6652  fprintf(fp, "=============================================\n\n");
6653 
6654 }
DATA_ACQ_ORDER acq_order[SLICE_FACTOR *DATA_ACQ_MAX]
Definition: KSFoundation.h:1321
int nslices_per_pass
Definition: KSFoundation.h:1320
int npasses
Definition: KSFoundation.h:1319
int nslices
Definition: KSFoundation.h:1318

◆ 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
6680  {
6681 
6682  int i;
6683  FILE *asciiWaveFile;
6684  asciiWaveFile = fopen(filename, "w");
6685 
6686  if (asciiWaveFile == NULL) {
6687  return;
6688  }
6689 
6690  for (i = 0; i < res; i++) {
6691  fprintf(asciiWaveFile, "%d %f\n", i, waveform[i]); fflush(asciiWaveFile);
6692  }
6693 
6694  fclose(asciiWaveFile); /* close ascii file */
6695 }

◆ ks_print_acqwaves()

STATUS ks_print_acqwaves ( KS_SEQ_COLLECTION seqcollection)

A generic function that searches for acquisition waves that require resampling

Parameters
[in]seqcollectionThe KS_SEQ_COLLECTION sequence collection
Returns
void This function writes out a file acq_waveforms.bin in /usr/g/mrraw/kstmp//embed/
6975  {
6976  int seq;
6977  int resampler_index = 0;
6978  STATUS status = SUCCESS;
6979 
6980  char filename[512];
6981 #ifdef PSD_HW /* on MR-scanner (host) */
6982  char outputdir_uid[512];
6983  char cmd[512];
6984  sprintf(outputdir_uid, "/usr/g/mrraw/kstmp/%010d/embed/", rhkacq_uid);
6985  sprintf(filename, "%s/acq_waveforms.bin", outputdir_uid);
6986  sprintf(cmd, "mkdir -p %s > /dev/null", outputdir_uid);
6987  system(cmd);
6988 #else /* e.g. WTools */
6989  sprintf(filename, "./acq_waveforms.bin");
6990 #endif
6991 
6992  FILE* fp = fopen(filename, "wb");
6993 
6994  for(seq = 0; seq < seqcollection->numseq; seq++) {
6995  KS_GRADRFCTRL * gradrf = &seqcollection->seqctrlptr[seq]->gradrf;
6996  int acq;
6997  for(acq = 0; acq < gradrf->numacq; acq++) {
6998  KS_READ * read = gradrf->readptr[acq];
7000  switch (read->resampler.ndims) {
7001  case 3:
7002  {
7003  if (read->resampler.zwave) {
7004  header.zgrad.dwell = read->resampler.zwave->duration / read->resampler.zwave->res;
7005  header.zgrad.npts = read->duration / header.zgrad.dwell;
7006  header.zgrad.target_res = read->resampler.target_res;
7007  }
7008  }
7009  case 2:
7010  {
7011  if (read->resampler.ywave) {
7012  header.ygrad.dwell = read->resampler.ywave->duration / read->resampler.ywave->res;
7013  header.ygrad.npts = read->duration / header.ygrad.dwell;
7014  header.ygrad.target_res = read->resampler.target_res;
7015  }
7016  }
7017  case 1:
7018  {
7019  if (read->resampler.xwave) {
7020  header.xgrad.dwell = read->resampler.xwave->duration / read->resampler.xwave->res;
7021  header.xgrad.npts = read->duration / header.xgrad.dwell;
7022  header.xgrad.target_res = read->resampler.target_res;
7023  }
7024 
7025  header.acq_bw = read->filt.bw;
7026  header.acq_npts = read->filt.outputs;
7027  header.acq_tsp = read->filt.tsp;
7028 
7029  int state;
7030  for (state = 0; state < read->resampler.xwave->base.nstates; state++) {
7031  read->resampler.index[state] = resampler_index++;
7032  fwrite(&header, 1, sizeof(KSCOMMON_RESAMPLER), fp);
7033  if(header.xgrad.npts) {
7034  fwrite(&read->resampler.xwave->p_waveformstates[state][read->resampler.wave_st_idx], header.xgrad.npts, sizeof(float), fp);
7035  }
7036  if(header.ygrad.npts) {
7037  fwrite(&read->resampler.ywave->p_waveformstates[state][read->resampler.wave_st_idx], header.ygrad.npts, sizeof(float), fp);
7038  }
7039  if(header.zgrad.npts) {
7040  fwrite(&read->resampler.zwave->p_waveformstates[state][read->resampler.wave_st_idx], header.zgrad.npts, sizeof(float), fp);
7041  }
7042  }
7043  break;
7044  }
7045  case 0:
7046  break;
7047  default:
7048  return ks_error("%s - acq_resampling up to 3 dims supported (read.ndims = %d)", __FUNCTION__, read->resampler.ndims);
7049  }
7050 
7051  } /*gradrf*/
7052 
7053  } /*seqcollection*/
7054  fflush(fp);
7055  fclose(fp);
7056 
7057  return status;
7058 }
uint16_t acq_npts
Definition: KSFoundation.h:1639
int wave_st_idx
Definition: KSFoundation.h:779
int index[KS_READCONTROL_MAXSTATE]
Definition: KSFoundation.h:780
KS_WAVE * zwave
Definition: KSFoundation.h:777
Definition: KSFoundation.h:1637
KS_READ_RESAMPLER resampler
Definition: KSFoundation.h:847
STATUS ks_error(const char *format,...) __attribute__((format(printf
Common error message function for HOST and TGT
int target_res
Definition: KSFoundation.h:781
kscommon_wave_params zgrad
Definition: KSFoundation.h:1643
float acq_bw
Definition: KSFoundation.h:1640
KS_SEQ_CONTROL * seqctrlptr[KS_MAXUNIQUE_SEQUENCES]
Definition: KSFoundation.h:1268
uint16_t target_res
Definition: KSFoundation.h:1629
KS_GRADRFCTRL gradrf
Definition: KSFoundation.h:1236
int rhkacq_uid
uint16_t npts
Definition: KSFoundation.h:1628
typedef struct that is a part of the KS_SEQ_CONTROL typedef struct, used internally to collect gradie...
Definition: KSFoundation.h:1054
uint16_t acq_tsp
Definition: KSFoundation.h:1638
KS_BASE base
Definition: KSFoundation.h:744
Core sequence object that handles a data acquisition window
Definition: KSFoundation.h:838
int duration
Definition: KSFoundation.h:841
KS_WAVE * ywave
Definition: KSFoundation.h:776
kscommon_wave_params xgrad
Definition: KSFoundation.h:1641
kscommon_wave_params ygrad
Definition: KSFoundation.h:1642
int nstates
Definition: KSFoundation.h:495
#define KSCOMMON_INIT_RESAMPLER
Definition: KSFoundation.h:1645
FILTER_INFO filt
Definition: KSFoundation.h:843
float * p_waveformstates[KS_WAVE_MAXNSTATES]
Definition: KSFoundation.h:749
KS_WAVE * xwave
Definition: KSFoundation.h:775
KS_SEQ_COLLECTION seqcollection
Definition: ksepi.e:82
uint16_t dwell
Definition: KSFoundation.h:1627
KS_READ * readptr[KS_MAXUNIQUE_READ]
Definition: KSFoundation.h:1061
int res
Definition: KSFoundation.h:746
int ndims
Definition: KSFoundation.h:778
int duration
Definition: KSFoundation.h:747
int numacq
Definition: KSFoundation.h:1062
int numseq
Definition: KSFoundation.h:1264

◆ ks_print_readwave()

void ks_print_readwave ( const KS_READWAVE *const  readwave,
const char *  filename 
)

Writes a KS_READWAVE to disk with the specified filename.

Only the waveform played out while acquiring data is saved. If the full waveform is desired, useks_print_wave on readwave.grads[].

Parameters
[in]readwavePointer to KS_READWAVE to be printed
[in]filenameString with the output file name
Returns
void
6722  {
6723  FILE *fp;
6724  int i = 0;
6725  fp = fopen(filename, "w");
6726 
6727  if (fp == NULL) {
6728  return;
6729  }
6730 
6731  for (i = (readwave->acqdelay / GRAD_UPDATE_TIME);
6732  i < (readwave->acqdelay + readwave->acq.duration) / GRAD_UPDATE_TIME;
6733  i++) {
6734  fprintf(fp, "%f, ", readwave->grad.waveform[i]);
6735  }
6736  fflush(fp);
6737  fclose(fp); /* close ascii file */
6738 }
KS_WAVE grad
Definition: KSFoundation.h:1604
int duration
Definition: KSFoundation.h:841
int acqdelay
Definition: KSFoundation.h:1608
KS_READ acq
Definition: KSFoundation.h:1606
KS_WAVEFORM waveform
Definition: KSFoundation.h:748

◆ ks_print_wave_part()

void ks_print_wave_part ( const KS_WAVE *const  wave,
const char *  filename,
int  startidx,
int  stopidx 
)

ADDTITLEHERE

6700  {
6701  FILE *fp = fopen(filename, "ab");
6702  if (fp == NULL) {
6703  return;
6704  }
6705  if (startidx < 0 || startidx >= wave->res) {
6706  ks_error("%s: startidx (%d) is not in [0, %d]", __FUNCTION__, startidx, wave->res-1);
6707  return;
6708  }
6709  int stopidx = startidx + numel;
6710  if (stopidx < startidx || stopidx >= wave->res) {
6711  ks_error("%s: stopidx (%d) is not in [%d, %d]", __FUNCTION__, stopidx, startidx, wave->res-1);
6712  return;
6713  }
6714  fwrite(&wave->waveform[startidx], sizeof(float), numel, fp);
6715  fflush(fp);
6716  fclose(fp);
6717 }
STATUS ks_error(const char *format,...) __attribute__((format(printf
Common error message function for HOST and TGT
int res
Definition: KSFoundation.h:746
KS_WAVEFORM waveform
Definition: KSFoundation.h:748

◆ ks_print_wave()

void ks_print_wave ( const KS_WAVE *const  wave,
const char *  filename 
)

ADDTITLEHERE

6743  {
6744  FILE *fp;
6745  int i = 0;
6746  fp = fopen(filename, "w");
6747 
6748  if (fp == NULL) {
6749  return;
6750  }
6751 
6752  for (i = 0; i < wave->res; i++) {
6753  fprintf(fp, "%f, ", wave->waveform[i]);
6754  }
6755  fflush(fp);
6756  fclose(fp); /* close ascii file */
6757 }
int res
Definition: KSFoundation.h:746
KS_WAVEFORM waveform
Definition: KSFoundation.h:748

◆ 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
6762  {
6763  if (a.description == NULL || a.duration == 0)
6764  return;
6765  fprintf(fp, "%s.duration: %d\n", a.description, a.duration);
6766  fprintf(fp, "%s.rbw: %g\n", a.description, a.rbw);
6767  fprintf(fp, "%s.filt.decimation: %g\n", a.description, a.filt.decimation);
6768  fprintf(fp, "%s.filt.tdaq: %d\n", a.description, a.filt.tdaq);
6769  fprintf(fp, "%s.filt.bw: %g\n", a.description , a.filt.bw);
6770  fprintf(fp, "%s.filt.tsp: %g\n", a.description, a.filt.tsp);
6771  fprintf(fp, "%s.filt.outputs: %d\n", a.description, a.filt.outputs);
6772  fprintf(fp, "%s.filt.prefills: %d\n", a.description, a.filt.prefills);
6773  fprintf(fp, "%s.filt.taps: %d\n", a.description, a.filt.taps);
6774  fflush(fp);
6775 }

◆ 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
6780  {
6781  if (t.description == NULL || t.duration == 0)
6782  return;
6783  fprintf(fp, "%s.amp: %g\n", t.description, t.amp);
6784  fprintf(fp, "%s.ramptime: %d\n", t.description, t.ramptime);
6785  fprintf(fp, "%s.plateautime: %d\n", t.description, t.plateautime);
6786  fprintf(fp, "%s.duration: %d\n", t.description , t.duration);
6787  fprintf(fp, "%s.area: %g\n", t.description, t.area);
6788  fflush(fp);
6789 }

◆ 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
6794  {
6795  if (r.grad.description == NULL || r.grad.duration == 0)
6796  return;
6797  fprintf(fp, "\nKS_READTRAP (%s):\n", r.grad.description);
6798  fprintf(fp, "fov: %g\n", r.fov);
6799  fprintf(fp, "res: %d\n", r.res);
6800  fprintf(fp, "rampsampling: %d\n", r.rampsampling);
6801  fprintf(fp, "nover: %d\n", r.nover);
6802  fprintf(fp, "acqdelay: %d\n", r.acqdelay);
6803  fprintf(fp, "area2center: %g\n", r.area2center);
6804  fprintf(fp, "time2center: %d\n", r.time2center);
6805  fflush(fp);
6806  ks_print_read(r.acq, fp);
6807  ks_print_trap(r.grad, fp);
6808  if (r.omega.duration > 0) {
6809  fprintf(fp, "omega: {");
6810  int i = 0;
6811  for (i = 0; i < r.omega.res; i++) {
6812  fprintf(fp, "%f, ", r.omega.waveform[i]);
6813  }
6814  fprintf(fp, "}");
6815  fflush(fp);
6816  }
6817 }
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:6780
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:6762

◆ 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
6822  {
6823  if (p.grad.description == NULL || p.grad.duration == 0)
6824  return;
6825  fprintf(fp, "\nKS_PHASER (%s):\n", p.grad.description);
6826  fprintf(fp, "fov: %g\n", p.fov);
6827  fprintf(fp, "res: %d\n", p.res);
6828  fprintf(fp, "nover: %d\n", p.nover);
6829  fprintf(fp, "R: %d\n", p.R);
6830  fprintf(fp, "nacslines: %d\n", p.nacslines);
6831  fprintf(fp, "areaoffset: %g\n", p.areaoffset);
6832  fprintf(fp, "numlinestoacq: %d\n", p.numlinestoacq);
6833  fflush(fp);
6834  ks_print_trap(p.grad, fp);
6835 }
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:6780

◆ 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
6860  {
6861  fprintf(fp, "\nKS_EPI (%s):\n", s.read.grad.description);
6862  fprintf(fp, "etl: %d\n", s.etl);
6863  fprintf(fp, "read_spacing: %.2f [ms]\n", s.read_spacing / 1000.0);
6864  fprintf(fp, "duration: %.2f [ms]\n", s.duration / 1000.0);
6865  fprintf(fp, "time2center: %.2f [ms]\n", s.time2center / 1000.0);
6866  ks_print_readtrap(s.read, fp);
6867  ks_print_trap(s.readphaser, fp);
6868  ks_print_trap(s.blip, fp);
6869  ks_print_phaser(s.blipphaser, fp);
6870 } /* 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:6780
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:6794
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:6822

◆ 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
6904  {
6905  if (r.rfpulse.activity == 0) {
6906  return;
6907  }
6908 
6909  fprintf(fp, "\nKS_RF (%s):\n", r.rfwave.description);
6910  fprintf(fp, "%s\n", r.designinfo);
6911  switch (r.role) {
6912  case KS_RF_ROLE_NOTSET: fprintf(fp, "role: KS_RF_ROLE_NOTSET\n"); break;
6913  case KS_RF_ROLE_EXC: fprintf(fp, "role: KS_RF_ROLE_EXC\n"); break;
6914  case KS_RF_ROLE_REF: fprintf(fp, "role: KS_RF_ROLE_REF\n"); break;
6915  case KS_RF_ROLE_CHEMSAT: fprintf(fp, "role: KS_RF_ROLE_CHEMSAT\n"); break;
6916  case KS_RF_ROLE_SPSAT: fprintf(fp, "role: KS_RF_ROLE_SPSAT\n"); break;
6917  case KS_RF_ROLE_INV: fprintf(fp, "role: KS_RF_ROLE_INV\n"); break;
6918  }
6919  fprintf(fp, "rfwave.res: %d\n", r.rfwave.res);
6920  fprintf(fp, "rfwave.duration: %d\n", r.rfwave.duration);
6921  fprintf(fp, "amp: %g\n", r.amp);
6922  fprintf(fp, "flip: %g\n", r.flip);
6923  fprintf(fp, "bw: %g\n", r.bw);
6924  fprintf(fp, "cf_offset: %g\n", r.cf_offset);
6925  fprintf(fp, "start2iso: %d\n", r.start2iso);
6926  fprintf(fp, "iso2end: %d\n", r.iso2end);
6927 
6928  ks_print_rfpulse(r.rfpulse, fp);
6929 
6930  if (r.rfwave.res == 0)
6931  fprintf(fp, " rfwave: OFF\n");
6932  else
6933  fprintf(fp, " rfwave: Waveform assigned\n");
6934 
6935  if (r.thetawave.res == 0)
6936  fprintf(fp, " thetawave: OFF\n");
6937  else
6938  fprintf(fp, " thetawave: Waveform assigned\n");
6939 
6940  fflush(fp);
6941 
6942 } /* ks_print_rf */
Definition: KSFoundation.h:2340
Definition: KSFoundation.h:2340
Definition: KSFoundation.h:2340
Definition: KSFoundation.h:2340
Definition: KSFoundation.h:2340
void ks_print_rfpulse(RF_PULSE rfpulse, FILE *fp)
Writes out the contents of an RF_PULSE struct for debugging
Definition: KSFoundation_host.c:6875
Definition: KSFoundation.h:2340

◆ 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
6875  {
6876  fprintf(fp, " rfpulse.pw: %d\n", *(rfpulse.pw));
6877  fprintf(fp, " rfpulse.amp: %g\n", *(rfpulse.amp));
6878  fprintf(fp, " rfpulse.abswidth: %g\n", rfpulse.abswidth);
6879  fprintf(fp, " rfpulse.effwidth: %g\n", rfpulse.effwidth);
6880  fprintf(fp, " rfpulse.area: %g\n", rfpulse.area);
6881  fprintf(fp, " rfpulse.dtycyc: %g\n", rfpulse.dtycyc);
6882  fprintf(fp, " rfpulse.maxpw: %g\n", rfpulse.maxpw);
6883  fprintf(fp, " rfpulse.num: %d\n", (int)rfpulse.num);
6884  fprintf(fp, " rfpulse.max_b1: %g\n", rfpulse.max_b1);
6885  fprintf(fp, " rfpulse.max_int_b1_sq: %g\n", rfpulse.max_int_b1_sq);
6886  fprintf(fp, " rfpulse.max_rms_b1: %g\n", rfpulse.max_rms_b1);
6887  fprintf(fp, " rfpulse.nom_fa: %g\n", rfpulse.nom_fa);
6888  fprintf(fp, " rfpulse.act_fa: %g\n", *(rfpulse.act_fa));
6889  fprintf(fp, " rfpulse.nom_pw: %g\n", rfpulse.nom_pw);
6890  fprintf(fp, " rfpulse.nom_bw: %g\n", rfpulse.nom_bw);
6891  fprintf(fp, " rfpulse.activity: %d\n", rfpulse.activity);
6892  fprintf(fp, " rfpulse.isodelay: %d\n", rfpulse.isodelay);
6893  fprintf(fp, " rfpulse.scale: %g\n", rfpulse.scale);
6894  fprintf(fp, " rfpulse.res: %d\n", *(rfpulse.res));
6895  fprintf(fp, " rfpulse.extgradfile: %d\n", rfpulse.extgradfile);
6896 
6897  fflush(fp);
6898 
6899 }
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
6945  {
6946 
6947  if (r.rf.rfpulse.activity == 0 || r.rf.rfwave.description == NULL || r.rf.rfwave.duration == 0) {
6948  return;
6949  }
6950 
6951  fprintf(fp, "\nKS_SELRF(%s):\n", r.rf.rfwave.description);
6952 
6953  fprintf(fp, "slthick: %g\n", r.slthick);
6954  if (r.rf.role == KS_RF_ROLE_REF)
6955  fprintf(fp, "crusher_dephasing: %g\n", r.crusher_dephasing);
6956  fprintf(fp, "bridge_crushers: %d\n", r.bridge_crushers);
6957  fprintf(fp, "pregrad_area_offset: %g\n", r.pregrad_area_offset);
6958  fprintf(fp, "postgrad_area_offset: %g\n", r.postgrad_area_offset);
6959  ks_print_trap(r.pregrad, fp);
6960  ks_print_trap(r.grad, fp);
6961  ks_print_trap(r.postgrad, fp);
6962  if (r.gradwave.res > 0) {
6963  fprintf(fp, " gradwave: Waveform assigned\n");
6964  }
6965  fprintf(fp, "grad2rf_start: %d\n", r.grad2rf_start);
6966  fprintf(fp, "rf2grad_end: %d\n", r.rf2grad_end);
6967 
6968  ks_print_rf(r.rf, fp);
6969 
6970  fflush(fp);
6971 
6972 } /* 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:6780
Definition: KSFoundation.h:2340
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:6904

◆ ks_print_readwaves()

void ks_print_readwaves ( KS_ECHOTRAIN *const  echotrain,
const char *  suffix,
int  rhkacq_uid 
)

ADDTITLEHERE

ADDDESCHERE

Parameters
echotrainADDTEXTHERE
suffixADDTEXTHERE
[in]rhkacq_uidADDTEXTHERE
Returns
void
7063  {
7064  if (echotrain->numwaves == 0) {
7065  return;
7066  }
7067 
7068  char filename[512];
7069 #ifdef PSD_HW /* on MR-scanner (host) */
7070  char outputdir_uid[512];
7071  char cmd[512];
7072  sprintf(outputdir_uid, "/usr/g/mrraw/kstmp/%010d/embed/", rhkacq_uid);
7073  sprintf(filename, "%s/readout_waveforms%s.bin", outputdir_uid, suffix);
7074  sprintf(cmd, "mkdir -p %s > /dev/null", outputdir_uid);
7075  system(cmd);
7076 #else /* e.g. WTools */
7077  sprintf(filename, "./readout_waveforms%s.bin", suffix);
7078 #endif
7079 
7080  FILE* fp = fopen(filename, "wb");
7081  int8_t wave_index;
7082 
7083  for (wave_index = 0; wave_index < echotrain->numwaves; wave_index++) {
7084  /* int object_index = echotrain->controls[wave_index].pg.object_index; */
7085  KS_READWAVE* const rw = &echotrain->readwaves[wave_index];
7086  const int16_t numplaced =ks_numplaced(&rw->grad.base);
7087  if (numplaced == 0) {
7088  ks_error("%s: %s has not been pg'd. Not included in %s", __FUNCTION__, rw->grad.description, filename);
7089  continue;
7090  }
7091  const int8_t acq_dwell = rw->acq.filt.tsp;
7092  const int8_t grad_dwell = rw->grad.duration / rw->grad.res;
7093  const uint16_t grad_samples_during_acq = rw->acq.duration / grad_dwell;
7094  const int8_t nstates = rw->grad.base.nstates;
7095  fwrite(&wave_index, sizeof(wave_index), 1, fp);
7096  fwrite(&nstates, sizeof(nstates), 1, fp);
7097  fwrite(&numplaced, sizeof(numplaced), 1, fp);
7098  fwrite(&grad_samples_during_acq, sizeof(grad_samples_during_acq), 1, fp);
7099  fwrite(&acq_dwell, sizeof(acq_dwell), 1, fp);
7100  fwrite(&grad_dwell, sizeof(grad_dwell), 1, fp);
7101  if (rw->grad.base.nstates > 1) {
7102  int state = 0;
7103  for (; state < rw->grad.base.nstates; state++) {
7104  fwrite(&(rw->grad.p_waveformstates[state][rw->acqdelay / grad_dwell]), sizeof(float), grad_samples_during_acq, fp);
7105  }
7106  } else {
7107  fwrite(&rw->grad.waveform[rw->acqdelay / grad_dwell], sizeof(float), grad_samples_during_acq, fp);
7108  }
7109  }
7110  fflush(fp);
7111  fclose(fp);
7112 } /* ks_print_readwaves */
KS_READWAVE readwaves[KS_ECHOTRAIN_MAX_WAVES]
Definition: KSFoundation.h:2017
STATUS ks_error(const char *format,...) __attribute__((format(printf
Common error message function for HOST and TGT
KS_WAVE grad
Definition: KSFoundation.h:1604
int numwaves
Definition: KSFoundation.h:2019
int rhkacq_uid
KS_DESCRIPTION description
Definition: KSFoundation.h:745
int ks_numplaced(KS_BASE *base)
ADDTITLEHERE
Definition: KSFoundation_common.c:4838
KS_BASE base
Definition: KSFoundation.h:744
int duration
Definition: KSFoundation.h:841
Composite sequence object for data readout during a wave (1-D resampling)
Definition: KSFoundation.h:1603
int nstates
Definition: KSFoundation.h:495
FILTER_INFO filt
Definition: KSFoundation.h:843
float * p_waveformstates[KS_WAVE_MAXNSTATES]
Definition: KSFoundation.h:749
int acqdelay
Definition: KSFoundation.h:1608
KS_READ acq
Definition: KSFoundation.h:1606
int res
Definition: KSFoundation.h:746
KS_WAVEFORM waveform
Definition: KSFoundation.h:748
int duration
Definition: KSFoundation.h:747

◆ 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
6840  {
6841  int i;
6842 
6843  for (i = 0; i < gradrfctrl.numrf; i++) {
6844  fprintf(fp, "RF[%d] '%s': %d times\n", i, gradrfctrl.rfptr[i]->rfwave.description, gradrfctrl.rfptr[i]->rfwave.base.ninst);
6845  }
6846  for (i = 0; i < gradrfctrl.numtrap; i++) {
6847  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]);
6848  }
6849  for (i = 0; i < gradrfctrl.numwave; i++) {
6850  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]);
6851  }
6852 
6853 }
KS_RF * rfptr[KS_MAXUNIQUE_RF]
Definition: KSFoundation.h:1055
int numtrap
Definition: KSFoundation.h:1058
int ninst
Definition: KSFoundation.h:494
int numrf
Definition: KSFoundation.h:1056
int gradnum[3]
Definition: KSFoundation.h:750
KS_WAVE * waveptr[KS_MAXUNIQUE_WAVE]
Definition: KSFoundation.h:1059
KS_DESCRIPTION description
Definition: KSFoundation.h:745
KS_WAVE rfwave
Definition: KSFoundation.h:1037
KS_BASE base
Definition: KSFoundation.h:744
KS_TRAP * trapptr[KS_MAXUNIQUE_TRAP]
Definition: KSFoundation.h:1057
KS_DESCRIPTION description
Definition: KSFoundation.h:668
int numwave
Definition: KSFoundation.h:1060
int gradnum[3]
Definition: KSFoundation.h:674

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

ADDTITLEHERE

ADDDESCHERE

Parameters
fnameADDTEXTHERE
Return values
STATUSADDTEXTHERE
203  {
204  FILE *file;
205  if ((file = fopen(fname, "r"))) {
206  fclose(file);
207  return SUCCESS;
208  }
209  return FAILURE;
210 }

◆ ks_syslimits_hasICEhardware()

int ks_syslimits_hasICEhardware ( )

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

Return values
int1: ICE 0: MGD
215  {
216  int isice = FALSE;
217 #if EPIC_RELEASE >= 27
218  isice = cfssctype;
219 #endif
220 
221  return isice;
222 
223 }
int cfssctype

◆ ks_dbg_reset()

void ks_dbg_reset ( )

Clear debug file content

140  {
141  FILE *fp;
142 
143 /* if on MR scanner (HOST and TGT/IPG, HW=hardware=MRscanner) */
144 #ifdef PSD_HW
145  fp = fopen("/usr/g/mrraw/ks_debug.txt", "w");
146 #else
147  fp = fopen("./ks_debug.txt", "w");
148 #endif
149 
150 fclose(fp);
151 
152 }

◆ 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
235  {
236  return (FMin(3, loggrd.tx_xyz, loggrd.ty_xyz, loggrd.tz_xyz));
237 }
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
242  {
243  return (FMin(6, loggrd.tx_xy, loggrd.tx_xz, loggrd.ty_xy, loggrd.ty_yz, loggrd.tz_xz, loggrd.tz_yz));
244 }
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
249  {
250  return (FMin(3, loggrd.tx, loggrd.ty, loggrd.tz));
251 }
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)
257  {
258  return (FMin(3, loggrd.xfs, loggrd.yfs, loggrd.zfs));
259 }
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
282  {
283  /* ramptime at full gradient scale */
284  return IMax(3, loggrd.xrt, loggrd.yrt, loggrd.zrt);
285 }
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
305  {
306  extern float ks_srfact, ks_qfact;
307  if (ks_srfact / ks_qfact < 1/sqrt(3)){
308  return(ks_syslimits_slewrate_phys());
309  }
310 #if EPIC_RELEASE >= 27
311  return (ks_syslimits_ampmax(loggrd) / ((float)ks_syslimits_ramptimemax(loggrd) * loggrd.scale_3axis_risetime));
312 #else
314 #endif
315 }
float ks_syslimits_slewrate_phys()
ADDTITLEHERE
Definition: KSFoundation_common.c:298
float ks_qfact
Definition: GERequired.e:271
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:235
int ks_syslimits_ramptimemax(LOG_GRAD loggrd)
Returns the minimum ramp time to get from zero to full gradient scale
Definition: KSFoundation_common.c:282
float ks_srfact
Definition: GERequired.e:270

◆ 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
320  {
321  extern float ks_srfact, ks_qfact;
322  if (ks_srfact / ks_qfact < 1/sqrt(3)){
323  return(ks_syslimits_slewrate_phys());
324  }
325 #if EPIC_RELEASE >= 27
326  return (ks_syslimits_ampmax2(loggrd) / ((float)ks_syslimits_ramptimemax(loggrd) * loggrd.scale_2axis_risetime));
327 #else
329 #endif
330 }
float ks_syslimits_slewrate_phys()
ADDTITLEHERE
Definition: KSFoundation_common.c:298
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:242
float ks_qfact
Definition: GERequired.e:271
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:282
float ks_srfact
Definition: GERequired.e:270

◆ 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
335  {
336  extern float ks_srfact, ks_qfact;
337  if (ks_srfact / ks_qfact < 1/sqrt(3)){
338  return(ks_syslimits_slewrate_phys());
339  }
340 #if EPIC_RELEASE >= 27
341  return (ks_syslimits_ampmax1(loggrd) / ((float)ks_syslimits_ramptimemax(loggrd) * loggrd.scale_1axis_risetime));
342 #else
344 #endif
345 
346 }
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:249
float ks_syslimits_slewrate_phys()
ADDTITLEHERE
Definition: KSFoundation_common.c:298
float ks_qfact
Definition: GERequired.e:271
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:282
float ks_srfact
Definition: GERequired.e:270

◆ 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)
352  {
354 }
LOG_GRAD loggrd
float ks_syslimits_ampmax1p(LOG_GRAD loggrd)
Returns the maximum gradient amplitude on the physical gradient axes
Definition: KSFoundation_common.c:257
int ks_syslimits_ramptimemax(LOG_GRAD loggrd)
Returns the minimum ramp time to get from zero to full gradient scale
Definition: KSFoundation_common.c:282

◆ 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
264  {
265  switch (board) {
266  case XGRAD:
267  return loggrd.tx;
268  case YGRAD:
269  return loggrd.ty;
270  case ZGRAD:
271  return loggrd.tz;
272  case OMEGA:
273  return 5e6;
274  default:
275  return 1.0;
276  }
277 }
LOG_GRAD loggrd

◆ ks_syslimits_ampmax_phys()

float ks_syslimits_ampmax_phys ( )

ADDTITLEHERE

228  {
229  return FMin(3, phygrd.xfs, phygrd.yfs, phygrd.zfs);
230 }
PHYS_GRAD phygrd

◆ ks_syslimits_ramptimemax_phys()

int ks_syslimits_ramptimemax_phys ( )

ADDTITLEHERE

290  {
291  /* ramptime at full gradient scale */
292  return IMax(3, phygrd.xrt, phygrd.yrt, phygrd.zrt);
293 }
PHYS_GRAD phygrd

◆ ks_syslimits_slewrate_phys()

float ks_syslimits_slewrate_phys ( )

ADDTITLEHERE

298  {
300 }
int ks_syslimits_ramptimemax_phys()
ADDTITLEHERE
Definition: KSFoundation_common.c:290
float ks_syslimits_ampmax_phys()
ADDTITLEHERE
Definition: KSFoundation_common.c:228

◆ ks_cycles_to_iphase()

unsigned short ks_cycles_to_iphase ( double  cycles)

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]cyclesperiods
Return values
unsignediphase to use on THETA board on hardwarewrapper of ks_cycles_to_iphase for degrees
367  {
368  /* Actual period for u14 */
369  static const unsigned short FS_2PI_u14 = (1<<14);
370  /* Want the fractional part */
371  cycles -= (long)cycles;
372  /* Force positive */
373  cycles++;
374  /* Scale to u14 domain then shift two bits to the
375  left for compatibility with FS_2PI [u16] period. */
376  return (unsigned short)(cycles * (double)FS_2PI_u14 + 0.5) % FS_2PI_u14 << 2;
377 } /* ks_cycles_to_iphase */

◆ ks_degrees_to_iphase()

unsigned short ks_degrees_to_iphase ( double  degrees)

wrapper of ks_cycles_to_iphase for radians

379  {
380  return ks_cycles_to_iphase(degrees / 360.0);
381 } /* wrapper for ks_cycles_to_iphase */
unsigned short ks_cycles_to_iphase(double cycles)
Returns the integer phase (internal use)
Definition: KSFoundation_common.c:367

◆ ks_radians_to_iphase()

unsigned short ks_radians_to_iphase ( double  radians)
383  {
384  return ks_cycles_to_iphase(radians / (2.0 * PI));
385 } /* wrapper for ks_cycles_to_iphase */
unsigned short ks_cycles_to_iphase(double cycles)
Returns the integer phase (internal use)
Definition: KSFoundation_common.c:367

◆ 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]
448  {
449 
450  if (slthick > 0 && rfbw > 0)
451  return (rfbw / (GAM * slthick / 10.0));
452  else
453  return 0.0; /* return zero amp if requested slice thickness is <= 0 */
454 }

◆ ks_wave_compute_params()

void ks_wave_compute_params ( KS_WAVE *const  wave)

Fills the members of the KS_WAVE sequence object

max_amp, min_amp, abs_max_amp, abs_max_slew, area

Parameters
[in]waveKS_WAVE object
Return values
void
1123  {
1124  /* pre-compute fields: max_amp, min_amp, abs_max_amp, abs_max_slew, area */
1125  wave->abs_max_slew = 0.0;
1126  float val = wave->waveform[0];
1127  wave->area = val;
1128  wave->min_amp = val;
1129  wave->max_amp = val;
1130 
1131  int i;
1132  for(i = 1; i < wave->res; i++) {
1133  val = wave->waveform[i];
1134  wave->area += val;
1135  if (val < wave->min_amp) {
1136  wave->min_amp = val;
1137  }
1138  if (val > wave->max_amp) {
1139  wave->max_amp = val;
1140  }
1141  float slew = fabs(val - wave->waveform[i-1]);
1142  if (slew > wave->abs_max_slew) {
1143  wave->abs_max_slew = slew;
1144  }
1145  }
1146  float dwell = (float)wave->duration / (float)wave->res;
1147  wave->abs_max_slew /= dwell;
1148  wave->area *= dwell;
1149  /* maximum amplitude in wave (in its own units, like e.g. [G/cm] or [degrees]) */
1150  wave->abs_max_amp = (fabs(wave->min_amp) > fabs(wave->max_amp)) ? fabs(wave->min_amp) : fabs(wave->max_amp);
1151 }
float abs_max_slew
Definition: KSFoundation.h:760
float min_amp
Definition: KSFoundation.h:758
float area
Definition: KSFoundation.h:757
float abs_max_amp
Definition: KSFoundation.h:761
float max_amp
Definition: KSFoundation.h:759
int res
Definition: KSFoundation.h:746
KS_WAVEFORM waveform
Definition: KSFoundation.h:748
int duration
Definition: KSFoundation.h:747

◆ 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
1156  {
1157 
1158  if (wave == NULL) {
1159  ks_error("ks_wave_res: wave is NULL");
1160  return KS_NOTSET;
1161  }
1162  if (wave->description == NULL || wave->description[0] == ' ') {
1163  ks_error("ks_wave_res: wave description not initialized or has leading space");
1164  return KS_NOTSET;
1165  }
1166  if (wave->res < 2 || wave->res > KS_MAXWAVELEN) {
1167  ks_error("ks_wave_res(%s): 'res' (%d) is out of valid range [2, %d]", wave->description, wave->res, KS_MAXWAVELEN);
1168  return KS_NOTSET;
1169  }
1170  if (wave->res % 2) {
1171  ks_error("ks_wave_res(%s): 'res' (%d) must be even", wave->description, wave->res);
1172  return KS_NOTSET;
1173  }
1174 
1175  return wave->res;
1176 }
#define KS_NOTSET
Definition: KSFoundation.h:115
STATUS ks_error(const char *format,...)
Definition: KSFoundation_common.c:78
KS_DESCRIPTION description
Definition: KSFoundation.h:745
int res
Definition: KSFoundation.h:746
#define KS_MAXWAVELEN
Definition: KSFoundation.h:254

◆ 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
1253  {
1254  float maxv = -1.0e38;
1255  int i;
1256 
1257  if (waveform == NULL) {
1258  ks_error("ks_waveform_max: wave is NULL");
1259  return maxv;
1260  }
1261  if (res <= 0) {
1262  return maxv;
1263  }
1264 
1265  for (i = 0; i < res; i++) {
1266  if (waveform[i] > maxv)
1267  maxv = waveform[i];
1268  }
1269 
1270  return maxv;
1271 }
STATUS ks_error(const char *format,...)
Definition: KSFoundation_common.c:78

◆ 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
1276  {
1277  return ks_waveform_max(wave->waveform, wave->res);
1278 }
float ks_waveform_max(const KS_WAVEFORM waveform, int res)
Returns the maximum value in a KS_WAVEFORM
Definition: KSFoundation_common.c:1253
int res
Definition: KSFoundation.h:746
KS_WAVEFORM waveform
Definition: KSFoundation.h:748

◆ 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
1283  {
1284  float minv = 1.0e38;
1285  int i;
1286 
1287  if (waveform == NULL) {
1288  ks_error("ks_waveform_min: wave is NULL");
1289  return minv;
1290  }
1291  if (res <= 0) {
1292  return minv;
1293  }
1294 
1295  for (i = 0; i < res; i++) {
1296  if (waveform[i] < minv)
1297  minv = waveform[i];
1298  }
1299 
1300  return minv;
1301 }
STATUS ks_error(const char *format,...)
Definition: KSFoundation_common.c:78

◆ 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
1306  {
1307  return ks_waveform_min(wave->waveform, wave->res);
1308 }
int res
Definition: KSFoundation.h:746
float ks_waveform_min(const KS_WAVEFORM waveform, int res)
Returns the minimum value in a KS_WAVEFORM
Definition: KSFoundation_common.c:1283
KS_WAVEFORM waveform
Definition: KSFoundation.h:748

◆ 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
1336  {
1337  float maxv = -1.0e38;
1338  int i;
1339 
1340  if (waveform == NULL) {
1341  ks_error("ks_waveform_absmax: wave is NULL");
1342  return maxv;
1343  }
1344  if (res <= 0) {
1345  return maxv;
1346  }
1347 
1348  for (i = 0; i < res; i++) {
1349  if (fabs(waveform[i]) > maxv)
1350  maxv = fabs(waveform[i]);
1351  }
1352 
1353  return maxv;
1354 }
STATUS ks_error(const char *format,...)
Definition: KSFoundation_common.c:78

◆ ks_waveform_maxslew()

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

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
[out]indexIndex in waveform where max slewrate is. Can be NULL if you are not interested.
Return values
absmaxvalMaximum absolute value
1366  {
1367  float maxslew = -1.0e38;
1368  int i;
1369 
1370  if (waveform == NULL) {
1371  ks_error("ks_waveform_absmax: wave is NULL");
1372  return maxslew;
1373  }
1374  if (res <= 0) {
1375  return maxslew;
1376  }
1377  float tmp = 0;
1378  for (i = 0; i < res-1; i++) {
1379  tmp = fabs(waveform[i] - waveform[i+1]);
1380  if (tmp > maxslew) {
1381  maxslew = tmp;
1382  if (index != NULL) {
1383  *index = i;
1384  }
1385  }
1386  }
1387  maxslew = maxslew*res/duration;
1388  return maxslew;
1389 }
STATUS ks_error(const char *format,...)
Definition: KSFoundation_common.c:78

◆ 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
1394  {
1395  return ks_waveform_maxslew(wave->waveform, wave->res, wave->duration, NULL);
1396 }
float ks_waveform_maxslew(const KS_WAVEFORM waveform, int res, int duration, int *index)
Returns the maximum absolute value in a KS_WAVEFORM
Definition: KSFoundation_common.c:1366
int res
Definition: KSFoundation.h:746
KS_WAVEFORM waveform
Definition: KSFoundation.h:748
int duration
Definition: KSFoundation.h:747

◆ 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
1313  {
1314  short maxv = 0;
1315  int i;
1316 
1317  if (waveform == NULL) {
1318  ks_error("%s: wave is NULL", __FUNCTION__);
1319  return maxv;
1320  }
1321  if (res <= 0) {
1322  return maxv;
1323  }
1324 
1325  for (i = 0; i < res; i++) {
1326  if (abs(waveform[i]) > maxv)
1327  maxv = abs(waveform[i]);
1328  }
1329 
1330  return maxv;
1331 }
STATUS ks_error(const char *format,...)
Definition: KSFoundation_common.c:78

◆ 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
1359  {
1360  return ks_waveform_absmax(wave->waveform, wave->res);
1361 }
float ks_waveform_absmax(const KS_WAVEFORM waveform, int res)
Returns the maximum absolute value in a KS_WAVEFORM
Definition: KSFoundation_common.c:1336
int res
Definition: KSFoundation.h:746
KS_WAVEFORM waveform
Definition: KSFoundation.h:748

◆ ks_wave_time2area()

int ks_wave_time2area ( const KS_WAVE wave,
float  area_in 
)

Calculates the time in [us] that it takes a wave to achieve a certain area

Parameters
[in]wavePointer to KS_WAVE
[in]area_infloat [(G/cm) * us]
Return values
time[us]
1401  {
1402  float dwell = wave->duration / wave->res;
1403  int time_dwells = 0;
1404  float area = 0.0f;
1405  for (time_dwells = 0; time_dwells < wave->res; time_dwells++) {
1406  area += wave->waveform[time_dwells] * dwell;
1407  if (fabs(area) > area_in) { /* area_in should be positive */
1408  break;
1409  }
1410  }
1411  if (time_dwells == wave->res) {
1412  if (areSame(area_in, area)) {
1413  return wave->duration;
1414  }
1415  return KS_NOTSET;
1416  }
1417  /* Now step back and find the fractional part */
1418  area -= wave->waveform[time_dwells] * dwell;
1419  float f = fabs((area_in - fabs(area)) / (wave->waveform[time_dwells] * dwell));
1420  return (int)(((float)(time_dwells) + f) * dwell + 0.5f);
1421 }
#define areSame(a, b)
Definition: KSFoundation.h:144
#define KS_NOTSET
Definition: KSFoundation.h:115
int res
Definition: KSFoundation.h:746
KS_WAVEFORM waveform
Definition: KSFoundation.h:748
int duration
Definition: KSFoundation.h:747

◆ 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]
1426  {
1427  int i;
1428  double area = 0;
1429  int start_indx = CEIL_DIV(start, dwelltime);
1430  int end_indx = end / dwelltime;
1431 
1432  if (waveform == NULL) {
1433  ks_error("%s: wave is NULL", __FUNCTION__);
1434  }
1435  if (start_indx < 0) {
1436  ks_error("%s: start must be >= 0", __FUNCTION__);
1437  }
1438  if (end_indx > KS_MAXWAVELEN) {
1439  ks_error("%s: end cannot exceed %d", __FUNCTION__, KS_MAXWAVELEN);
1440  }
1441 
1442  for (i = start_indx; i < end_indx; i++) {
1443  area += waveform[i];
1444  }
1445  area *= dwelltime;
1446 
1447  /* add potential partial dwell time due to CEIL_DIV, when start [us] is not divisible by dwelltime */
1448  if ((start_indx * dwelltime) > start && start_indx > 0) {
1449  area += waveform[start_indx - 1] * (((double) start_indx * dwelltime) - (double) start);
1450  }
1451 
1452  /* add potential partial dwell time due to (floor) integer division, when end [us] is not divisible by dwelltime */
1453  if ((end_indx * dwelltime) < end && (end_indx < (KS_MAXWAVELEN - 1))) {
1454  area += waveform[end_indx + 1] * ((double) end - ((double) end_indx * dwelltime));
1455  }
1456 
1457  return ((float) area);
1458 }
STATUS ks_error(const char *format,...)
Definition: KSFoundation_common.c:78
#define KS_MAXWAVELEN
Definition: KSFoundation.h:254

◆ ks_iwave_area()

long ks_iwave_area ( const KS_IWAVE  iwave,
int  start,
int  end,
int  dwelltime 
)

Returns the area of an interger waveform (not in use), used for exact areas

Parameters
[in]iwaveKS_IWAVE (short 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_IWAVE over the specified interval in aribtrary units
1463  {
1464  int i;
1465  long area = 0;
1466  int start_indx = CEIL_DIV(start, dwelltime);
1467  int end_indx = end / dwelltime;
1468 
1469  if (iwave == NULL) {
1470  ks_error("%s: wave is NULL", __FUNCTION__);
1471  }
1472  if (start_indx < 0) {
1473  ks_error("%s: start must be >= 0", __FUNCTION__);
1474  }
1475  if (end_indx > KS_MAXWAVELEN) {
1476  ks_error("%s: end cannot exceed %d", __FUNCTION__, KS_MAXWAVELEN);
1477  }
1478 
1479  for (i = start_indx; i < end_indx; i++) {
1480  area += iwave[i];
1481  }
1482  area *= dwelltime;
1483 
1484  /* add potential partial dwell time due to CEIL_DIV, when start [us] is not divisible by dwelltime */
1485  if ((start_indx * dwelltime) > start && start_indx > 0) {
1486  area += iwave[start_indx - 1] * ((start_indx * dwelltime) - start);
1487  }
1488 
1489  /* add potential partial dwell time due to (floor) integer division, when end [us] is not divisible by dwelltime */
1490  if ((end_indx * dwelltime) < end && (end_indx < (KS_MAXWAVELEN - 1))) {
1491  area += iwave[end_indx + 1] * (end - (end_indx * dwelltime));
1492  }
1493 
1494  return area;
1495 }
STATUS ks_error(const char *format,...)
Definition: KSFoundation_common.c:78
#define KS_MAXWAVELEN
Definition: KSFoundation.h:254

◆ 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]
1500  {
1501  int dwelltime = wave->duration / wave->res;
1502  return ks_waveform_area(wave->waveform, start, end, dwelltime);
1503 }
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:1426
int res
Definition: KSFoundation.h:746
KS_WAVEFORM waveform
Definition: KSFoundation.h:748
int duration
Definition: KSFoundation.h:747

◆ ks_wave_full_area()

float ks_wave_full_area ( const KS_WAVE wave)

Returns the area of a KS_WAVE object over its entire duration

Parameters
[in]waveKS_WAVE object
Return values
areaThe area of the KS_WAVE object in [(G/cm) * us]
1508  {
1509  return ks_wave_area(wave, 0, wave->duration);
1510 }
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]
Definition: KSFoundation_common.c:1500
int duration
Definition: KSFoundation.h:747

◆ 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
1515  {
1516  int i;
1517  double sum = 0;
1518 
1519  if (waveform == NULL) {
1520  ks_error("ks_waveform_sum: wave is NULL");
1521  }
1522  if (res <= 0) {
1523  ks_error("ks_waveform_sum: res must be positive");
1524  }
1525 
1526  for (i = 0; i < res; i++) {
1527  sum += waveform[i];
1528  }
1529 
1530  return (float)sum;
1531 }
STATUS ks_error(const char *format,...)
Definition: KSFoundation_common.c:78

◆ 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
1536  {
1537  return ks_waveform_sum(wave->waveform, wave->res);
1538 }
float ks_waveform_sum(const KS_WAVEFORM waveform, int res)
Returns the sum of a KS_WAVEFORM
Definition: KSFoundation_common.c:1515
int res
Definition: KSFoundation.h:746
KS_WAVEFORM waveform
Definition: KSFoundation.h:748

◆ 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
1543  {
1544  int i;
1545  double sum = 0;
1546 
1547  if (waveform == NULL) {
1548  ks_error("ks_waveform_norm: wave is NULL");
1549  }
1550  if (res <= 0) {
1551  ks_error("ks_waveform_norm: res must be positive");
1552  }
1553 
1554  for (i = 0; i < res; i++) {
1555  sum += pow(fabs(waveform[i]), 2.0);
1556  }
1557 
1558  return (float)sqrt(sum);
1559 }
STATUS ks_error(const char *format,...)
Definition: KSFoundation_common.c:78

◆ 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
1564  {
1565  return ks_waveform_norm(wave->waveform, wave->res);
1566 }
float ks_waveform_norm(const KS_WAVEFORM waveform, int res)
Returns the 2-norm of a KS_WAVEFORM
Definition: KSFoundation_common.c:1543
int res
Definition: KSFoundation.h:746
KS_WAVEFORM waveform
Definition: KSFoundation.h:748

◆ 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
1571  {
1572  int i, j;
1573 
1574  for (j = 0; j < res; j++) {
1575 
1576  double sum = 0;
1577  for (i = 0; i < j; i++) {
1578  sum += waveform[i];
1579  }
1580 
1581  cumsumwaveform[j] = (float)sum;
1582  }
1583 }

◆ 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
1588  {
1589  int res;
1590  res = (cumsumwave->res > wave->res) ? wave->res : cumsumwave->res; /* smallest of the two */
1591  ks_waveform_cumsum(cumsumwave->waveform, wave->waveform, res);
1592 }
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:1571
int res
Definition: KSFoundation.h:746
KS_WAVEFORM waveform
Definition: KSFoundation.h:748

◆ 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
1597  {
1598  int i;
1599 
1600  for (i = 0; i < res; i++) {
1601  waveform_mod[i] *= waveform[i];
1602  }
1603 }

◆ 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
1608  {
1609  int res;
1610  res = (wave_mod->res > wave->res) ? wave->res : wave_mod->res; /* smallest of the two */
1611  ks_waveform_multiply(wave_mod->waveform, wave->waveform, res);
1612 
1613  ks_wave_compute_params(wave_mod);
1614 
1615 }
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:1597
void ks_wave_compute_params(KS_WAVE *const wave)
Fills the members of the KS_WAVE sequence object
Definition: KSFoundation_common.c:1123
int res
Definition: KSFoundation.h:746
KS_WAVEFORM waveform
Definition: KSFoundation.h:748

◆ 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
1620  {
1621  int i;
1622 
1623  for (i = 0; i < res; i++) {
1624  waveform_mod[i] += waveform[i];
1625  }
1626 }

◆ ks_eval_readwave_negatestate()

void ks_eval_readwave_negatestate ( KS_READWAVE readwave,
int  state 
)

#### Modify an existing state to its negative (flipped polarity readout)

Parameters
[in,out]readwaveA pointer to KS_READWAVE you would like to negate
[in]stateThe wavestate to modify
1686  {
1687  if (state == 0) {
1688  KS_WAVE* wave = &readwave->grad;
1689  wave->max_amp *= -1;
1690  wave->min_amp *= -1;
1691  float min_amp = wave->max_amp;
1692  wave->max_amp = wave->min_amp;
1693  wave->min_amp = min_amp;
1694  wave->area *= -1;
1695  }
1696 
1697  ks_waveform_multiplyval(readwave->grad_states[state], -1, readwave->grad.res);
1698  ks_waveform_multiplyval(readwave->omega_states[state], -1, readwave->omega.res);
1699 }
KS_WAVEFORM omega_states[KS_READCONTROL_MAXSTATE]
Definition: KSFoundation.h:1619
Core sequence object making arbitrary waveforms on any board (using float data format)
Definition: KSFoundation.h:743
float min_amp
Definition: KSFoundation.h:758
KS_WAVE omega
Definition: KSFoundation.h:1605
KS_WAVE grad
Definition: KSFoundation.h:1604
float area
Definition: KSFoundation.h:757
float max_amp
Definition: KSFoundation.h:759
KS_WAVEFORM grad_states[KS_READCONTROL_MAXSTATE]
Definition: KSFoundation.h:1618
void ks_waveform_multiplyval(KS_WAVEFORM waveform, float val, int res)
In-place scalar multiplication of a KS_WAVEFORM
Definition: KSFoundation_common.c:1642
int res
Definition: KSFoundation.h:746

◆ 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
1631  {
1632  int res;
1633  res = (wave_mod->res > wave->res) ? wave->res : wave_mod->res; /* smallest of the two */
1634  ks_waveform_add(wave_mod->waveform, wave->waveform, res);
1635 
1636  ks_wave_compute_params(wave_mod);
1637 }
void ks_wave_compute_params(KS_WAVE *const wave)
Fills the members of the KS_WAVE sequence object
Definition: KSFoundation_common.c:1123
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:1620
int res
Definition: KSFoundation.h:746
KS_WAVEFORM waveform
Definition: KSFoundation.h:748

◆ 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
1642  {
1643  int i;
1644 
1645  for (i = 0; i < res; i++) {
1646  waveform[i] *= val;
1647  }
1648 }

◆ 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
1653  {
1654  ks_waveform_multiplyval(wave->waveform, val, wave->res);
1655  /* max_amp, min_amp, abs_max_amp, abs_max_slew, area */
1656  wave->max_amp *= val;
1657  wave->min_amp *= val;
1658  if (val < 0.0) { /* swap min/max for negative val */
1659  float min_amp = wave->max_amp;
1660  wave->max_amp = wave->min_amp;
1661  wave->min_amp = min_amp;
1662  }
1663  wave->abs_max_amp *= fabs(val);
1664  wave->abs_max_slew *= fabs(val);
1665  wave->area *= val;
1666 }
float abs_max_slew
Definition: KSFoundation.h:760
float min_amp
Definition: KSFoundation.h:758
float area
Definition: KSFoundation.h:757
float abs_max_amp
Definition: KSFoundation.h:761
float max_amp
Definition: KSFoundation.h:759
void ks_waveform_multiplyval(KS_WAVEFORM waveform, float val, int res)
In-place scalar multiplication of a KS_WAVEFORM
Definition: KSFoundation_common.c:1642
int res
Definition: KSFoundation.h:746
KS_WAVEFORM waveform
Definition: KSFoundation.h:748

◆ 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
1671  {
1672  int i;
1673 
1674  for (i = 0; i < res; i++) {
1675  waveform[i] += val;
1676  }
1677 }

◆ 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
1682  {
1683  ks_waveform_addval(wave->waveform, val, wave->res);
1684  /* max_amp, min_amp, abs_max_amp, abs_max_slew, area */
1685  wave->max_amp += val;
1686  wave->min_amp += val;
1687  if(-wave->min_amp > wave->max_amp) {
1688  wave->abs_max_amp = -wave->min_amp;
1689  } else {
1690  wave->abs_max_amp = wave->max_amp;
1691  }
1692  wave->area += (val * wave->duration);
1693 }
void ks_waveform_addval(KS_WAVEFORM waveform, float val, int res)
In-place scalar addition of a KS_WAVEFORM
Definition: KSFoundation_common.c:1671
float min_amp
Definition: KSFoundation.h:758
float area
Definition: KSFoundation.h:757
float abs_max_amp
Definition: KSFoundation.h:761
float max_amp
Definition: KSFoundation.h:759
int res
Definition: KSFoundation.h:746
KS_WAVEFORM waveform
Definition: KSFoundation.h:748
int duration
Definition: KSFoundation.h:747

◆ ks_waveform2iwave()

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

(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_cycles_to_iphase(). 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
[in]maxmax value of the waveform
[in]fs_factorhow much of the dynamic range to use 1 = FULL
Return values
STATUSSUCCESS or FAILURE
1698  {
1699  int i;
1700  if (board != THETA) {
1701  /* 16383 - FS signed 15bit value (2^15) / 2 - 1 */
1702  const float FS = (float)(max_pg_wamp>>1) * fs_factor / max;
1703  for (i = 0; i < res; i++) {
1704  iwave[i] = ((short) (FS * waveform[i]) << 1);
1705  }
1706  } else {
1707  for (i = 0; i < res; i++) {
1708  /* Theta board used a 14-bit representation */
1709  iwave[i] = ((short) ks_degrees_to_iphase(waveform[i]));
1710  }
1711  }
1712 
1713  /* set end-of-waveform flag */
1714  iwave[res - 1] |= WEOS_BIT;
1715 
1716  return SUCCESS;
1717 
1718 } /* ks_wave2iwave() */
unsigned short ks_degrees_to_iphase(double degrees)
Definition: KSFoundation_common.c:379

◆ 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_cycles_to_iphase(). 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
1721  {
1722  return ks_waveform2iwave(iwave, wave->waveform, wave->res, board, wave->abs_max_amp, wave->fs_factor);
1723 }
float fs_factor
Definition: KSFoundation.h:756
STATUS ks_waveform2iwave(KS_IWAVE iwave, const KS_WAVEFORM waveform, int res, int board, float max, float fs_factor)
(Internal use) Conversion of a KS_WAVEFORM to a short int array for use on hardware
Definition: KSFoundation_common.c:1698
float abs_max_amp
Definition: KSFoundation.h:761
int res
Definition: KSFoundation.h:746
KS_WAVEFORM waveform
Definition: KSFoundation.h:748

◆ 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
1738  {
1739 
1740  if (!strcmp(suffix, "xtr")) {
1741  return echo->assoc_pulse;
1742  } else if (!strcmp(suffix, "rba")) {
1743  return echo->assoc_pulse->assoc_pulse;
1744  } else {
1745  return echo;
1746  }
1747 
1748 }

◆ 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
1802  {
1803  float boardmax;
1804  char boardc[8] = "xyz---o";
1805 
1806  boardmax = ks_syslimits_gradtarget(loggrd, loc.board); /* returns 1.0 for OMEGA board */
1807 
1808  /* checking for NULL pointers */
1809  if (trap == NULL) {
1810  return ks_error("ks_pg_trap: First argument is NULL");
1811  }
1812  /* duration of 0 should not throw an error as this is a sign of that it has not been set up
1813  or has manually been disabled this way */
1814  if (trap->duration == 0) {
1815  return SUCCESS;
1816  }
1817 
1818  if (trap->description == NULL || trap->description[0] == 0 || trap->description[0] == ' ') {
1819  return ks_error("ks_pg_trap: Invalid description ('%s')", trap->description);
1820  }
1821 
1822 #ifdef IPG
1823  /* TGT: Don't place anything on hardware if ctrl->duration = 0.
1824  This is in concert with:
1825  - not performing createseq() in KS_SEQLENGTH if seqctrl.duration = 0
1826  - not playing sequence modules in scan() using ks_scan_playsequence() if seqctrl.duration = 0
1827  */
1828  if (ctrl->duration == 0) {
1829  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);
1830  }
1831 #endif
1832 
1833  /* input validation */
1834  if ((trap->ramptime % 4) || (trap->plateautime % 4) || (trap->duration % 4)) {
1835  return ks_error("ks_pg_trap(%s): All timing parameters must be divisible by 4", trap->description);
1836  }
1837 
1838  if ((trap->ramptime < 4) || (trap->plateautime < 4) || (trap->duration < 4)) {
1839  return ks_error("ks_pg_trap(%s): All timing parameters must be at least 4 us", trap->description);
1840  }
1841 
1842  if (isNaN(loc.ampscale)) {
1843  return ks_error("ks_pg_trap(%s): loc.ampscale NaN", trap->description);
1844  }
1845 
1846  if (loc.pos < GRAD_UPDATE_TIME || loc.pos > 4e6) {
1847  return ks_error("ks_pg_trap(%s): Invalid position %d, it must be between 4us and 4s", trap->description, loc.pos);
1848  }
1849 
1850  if (loc.board == OMEGA && !areSame(loc.ampscale,1.0)) {
1851  return ks_error("ks_pg_trap(%s): ampscale must be +1.0 for OMEGA", trap->description);
1852  }
1853 
1854  if (loc.ampscale < -1.0 || loc.ampscale > 1.0) {
1855  return ks_error("ks_pg_trap(%s): Invalid ampscale %g, it must be between -1.0 and 1.0", trap->description, loc.ampscale);
1856  }
1857 
1858  if (loc.board != XGRAD && loc.board != YGRAD && loc.board != ZGRAD && loc.board != OMEGA) {
1859  return 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);
1860  }
1861 
1862  /* compute the minimum fs_factor */
1863  float min_fs_factor = fabs(trap->amp) / boardmax;
1864  /* change fs_factor to min if needed */
1865  if (trap->fs_factor < min_fs_factor) {
1866  ks_dbg("%s: (%s) trap->fs_factor too low, modified from %1.2f to: %1.2f", __FUNCTION__, trap->description, trap->fs_factor, min_fs_factor);
1867  trap->fs_factor = min_fs_factor;
1868  }
1869 
1870  if (trap->fs_factor > 1.0f) {
1871  ks_error("ks_pg_trap(%s): Corner case where an exact representation of the trap shape is not possible amp %g [g/cm] with a ramp period of %i us", trap->description, trap->amp, trap->ramptime);
1872  }
1873 
1874  if (fabs(trap->amp * loc.ampscale) > boardmax) {
1875  return 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]);
1876  }
1877 
1878  loc.pos = RUP_GRD(loc.pos);
1879 
1880 #ifdef IPG
1881  {
1882 
1883  int i;
1884 
1885  char tmpstr[KS_DESCRIPTION_LENGTH + 7];
1886  tmpstr[KS_DESCRIPTION_LENGTH + 7 - 1] = 0;
1887 
1888  if (trap->base.ngenerated >= trap->base.ninst) {
1889  return 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);
1890  }
1891 
1892  if (!ctrl->gradrf.is_cleared_on_tgt) /* Reset gradrfctrl on tgt to remove dead pointers copied from host. */
1893  {
1895  }
1896 
1897  const int res = trap->ramptime / GRAD_UPDATE_TIME;
1898  short * attack = (short *) AllocNode(res * sizeof(short));
1899  short * decay = (short *) AllocNode(res * sizeof(short));
1900 
1902 
1903  ks_pg_trap_make_symramps(trap, min_fs_factor, attack, decay);
1904 
1905  }
1906  /* now set the plateau once all adjustments of fs_factor are over */
1907  short plateau = (short)(trap->fs_factor * max_pg_wamp);
1908 
1909  /* TODO: consider freeing memory if ngenerated is zero -- EA */
1910 
1911  if (trap->wfpulse == NULL) {
1912  WF_PULSE wfInit = INITPULSE;
1913 
1914  trap->wfpulse = (WF_PULSE **) AllocNode(8 * sizeof(WF_PULSE *)); /* X, Y, Z, (OMEGA = 6). Not applicable to other boards */
1915 
1916  for (i = XGRAD; i <= OMEGA; i++) {
1917 
1918  if (i == XGRAD || i == YGRAD || i == ZGRAD || i == OMEGA) {
1919 
1920  trap->wfpulse[i] = (WF_PULSE *) AllocNode(3 * sizeof(WF_PULSE)); /* attack, plateau, decay */
1921 
1922  /* init pulse structure */
1923  trap->wfpulse[i][G_ATTACK] = wfInit;
1924  trap->wfpulse[i][G_PLATEAU] = wfInit;
1925  trap->wfpulse[i][G_DECAY] = wfInit;
1926 
1927  /* Name for WF_PULSEs */
1928  sprintf(tmpstr, "%s.%ca", trap->description, boardc[i]); pulsename(&(trap->wfpulse[i][G_ATTACK]), tmpstr);
1929  sprintf(tmpstr, "%s.%c", trap->description, boardc[i]); pulsename(&(trap->wfpulse[i][G_PLATEAU]), tmpstr);
1930  sprintf(tmpstr, "%s.%cd", trap->description, boardc[i]); pulsename(&(trap->wfpulse[i][G_DECAY]), tmpstr);
1931 
1933  createreserve(&trap->wfpulse[i][G_ATTACK], (WF_PROCESSOR) i, res);
1934  trap->wfpulse[i][G_ATTACK].type = TYPEXTERNAL;
1935  movewaveimm(attack, &trap->wfpulse[i][G_ATTACK], (int) 0, res, TOHARDWARE);
1936 
1937  createreserve(&trap->wfpulse[i][G_DECAY], (WF_PROCESSOR) i, res);
1938  trap->wfpulse[i][G_DECAY].type = TYPEXTERNAL;
1939  movewaveimm(decay, &trap->wfpulse[i][G_DECAY], (int) 0, res, TOHARDWARE);
1940  } else {
1941  createramp(&(trap->wfpulse[i][G_ATTACK]), (WF_PROCESSOR) i, trap->ramptime, 0, plateau, res, 1.0);
1942  createramp(&(trap->wfpulse[i][G_DECAY]), (WF_PROCESSOR) i, trap->ramptime, plateau, 0, res, 1.0);
1943  }
1944  createconst(&(trap->wfpulse[i][G_PLATEAU]), (WF_PROCESSOR) i, trap->plateautime, plateau);
1945 
1946  linkpulses(3, &(trap->wfpulse[i][G_PLATEAU]), &(trap->wfpulse[i][G_ATTACK]), &(trap->wfpulse[i][G_DECAY]));
1947 
1948  } else {
1949 
1950  trap->wfpulse[i] = NULL;
1951 
1952  }
1953 
1954  } /* per board */
1955  FreeNode(attack);
1956  FreeNode(decay);
1957 
1958  } /* wfpulse == NULL */
1959 
1960 
1961  /* allocate the array of WFINSTANCE's if needed */
1962  if (!trap->wfi) {
1963  trap->wfi = (KS_WFINSTANCE *) AllocNode(trap->base.ninst * sizeof(KS_WFINSTANCE));
1964  }
1965 
1966  /* Set instruction amplitudes at the proper position and board */
1967  {
1968  const int corrected_pos = loc.pos + (loc.board == OMEGA)*psd_rf_wait;
1969  /* creating new instructions */
1970  createinstr(&(trap->wfpulse[loc.board][G_ATTACK]),
1971  corrected_pos,
1972  trap->ramptime,
1973  (int)(trap->amp * loc.ampscale * MAX_PG_IAMP / (trap->fs_factor * boardmax)));
1974  createinstr(&(trap->wfpulse[loc.board][G_PLATEAU]),
1975  corrected_pos + trap->ramptime,
1976  trap->plateautime,
1977  (int)(trap->amp * loc.ampscale * MAX_PG_IAMP / (trap->fs_factor * boardmax)));
1978  createinstr(&(trap->wfpulse[loc.board][G_DECAY]),
1979  corrected_pos + trap->ramptime + trap->plateautime,
1980  trap->ramptime,
1981  (int)(trap->amp * loc.ampscale * MAX_PG_IAMP / (trap->fs_factor * boardmax)));
1982  }
1983 
1984  /* initializing the corresponding KS_WFINSTANCE */
1985  trap->wfi[trap->base.ngenerated].boardinstance = trap->wfpulse[loc.board][G_PLATEAU].ninsts - 1; /* zero based */
1986  trap->wfi[trap->base.ngenerated].wf = &(trap->wfpulse[loc.board][G_PLATEAU]);
1987  trap->wfi[trap->base.ngenerated].loc = loc;
1988 
1989  trap->base.ngenerated++;
1990 
1991  /* sort after the last instance is generated */
1992  if (trap->base.ngenerated == trap->base.ninst) {
1993  ks_sort_wfi_by_timeboard(trap->wfi, trap->base.ninst);
1994  }
1995  }
1996 #else /* TGT(IPG) or HOST */
1997  {
1998  /* HOST */
1999  /* update instance counter on host only. This counter is used to allocate the right amount of WFINSTANCEs on IPG */
2000  trap->base.ninst++;
2001 
2002  KS_SEQLOC *locs = (KS_SEQLOC *) realloc(trap->locs, sizeof(KS_SEQLOC)*trap->base.ninst);
2003  if (!locs) {
2004  return KS_THROW("Realloc failed");
2005  }
2006  trap->locs = locs;
2007  trap->locs[trap->base.ninst-1] = loc;
2008 
2009 
2010  ks_sort_loc_by_timeboard(trap->locs, trap->base.ninst);
2011 
2012  /* for trap, we also need to keep track of #instances per X, Y, Z for gradient heating calculations.
2013  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 */
2014  if (loc.board == XGRAD || loc.board == YGRAD || loc.board == ZGRAD)
2015  trap->gradnum[loc.board]++;
2016 
2017  }
2018 
2019 #endif /* IPG */
2020  /* register this KS_TRAP for later heating calculations */
2021  {
2022  STATUS status = ks_eval_addtraptogradrfctrl(&ctrl->gradrf, trap);
2023  KS_RAISE(status);
2024  }
2025  return SUCCESS;
2026 
2027 } /* 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:4656
void ks_sort_wfi_by_timeboard(KS_WFINSTANCE *a, int nitems)
Sort WF_INSTANCEs in time, then board
Definition: KSFoundation_common.c:4120
int plateautime
Definition: KSFoundation.h:672
int boardinstance
Definition: KSFoundation.h:484
#define areSame(a, b)
Definition: KSFoundation.h:144
KS_BASE base
Definition: KSFoundation.h:667
KS_DISCRETE_REPRESENTATION discrete_representation
Definition: KSFoundation.h:1233
int pos
Definition: KSFoundation.h:463
(Internal use) Structure being a part of various sequence objects to sort them in time across boards...
Definition: KSFoundation.h:483
int ninst
Definition: KSFoundation.h:494
KS_SEQLOC * locs
Definition: KSFoundation.h:678
STATUS ks_error(const char *format,...)
Definition: KSFoundation_common.c:78
STATUS ks_dbg(const char *format,...)
Definition: KSFoundation_common.c:157
int board
Definition: KSFoundation.h:462
int duration
Definition: KSFoundation.h:1227
float ks_syslimits_gradtarget(LOG_GRAD loggrd, int board)
Returns the maximum target amplitude for a board (internal use)
Definition: KSFoundation_common.c:264
float ampscale
Definition: KSFoundation.h:464
Definition: KSFoundation.h:2310
KS_GRADRFCTRL gradrf
Definition: KSFoundation.h:1236
KS_WFINSTANCE * wfi
Definition: KSFoundation.h:680
int isNaN(float a)
Definition: KSFoundation_common.c:58
#define KS_DESCRIPTION_LENGTH
Definition: KSFoundation.h:253
KS_SEQLOC loc
Definition: KSFoundation.h:486
float fs_factor
Definition: KSFoundation.h:677
LOG_GRAD loggrd
int is_cleared_on_tgt
Definition: KSFoundation.h:1065
#define KS_RAISE(status)
Definition: KSFoundation.h:190
float amp
Definition: KSFoundation.h:669
typedef struct used as argument to ks_pg_*** functions to control where and when to place a sequence ...
Definition: KSFoundation.h:461
WF_PULSE * wf
Definition: KSFoundation.h:485
KS_DESCRIPTION description
Definition: KSFoundation.h:668
void ks_tgt_reset_gradrfctrl(KS_GRADRFCTRL *gradrfctrl)
Definition: KSFoundation_tgt.c:1797
KS_DESCRIPTION description
Definition: KSFoundation.h:1234
Definition: KSFoundation.h:419
int ngenerated
Definition: KSFoundation.h:496
void ks_pg_trap_make_symramps(KS_TRAP *trap, float min_fs_factor, KS_IWAVE attack, KS_IWAVE decay)
Definition: KSFoundation_common.c:1753
Definition: KSFoundation.h:2310
WF_PULSE ** wfpulse
Definition: KSFoundation.h:679
int duration
Definition: KSFoundation.h:673
int psd_rf_wait
#define KS_THROW(format,...)
Definition: KSFoundation.h:181
int gradnum[3]
Definition: KSFoundation.h:674
Definition: KSFoundation.h:2310
int ramptime
Definition: KSFoundation.h:671
void ks_sort_loc_by_timeboard(KS_SEQLOC *a, int nitems)
Sort KS_SEQLOCs in time, then board
Definition: KSFoundation_common.c:4128

◆ 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
2051  {
2052 #ifdef IPG
2053  int acqwin_start;
2054  int placementindx;
2055  long dab_pos;
2056  char tmpstr[KS_DESCRIPTION_LENGTH + 7];
2057 #endif
2058  STATUS status;
2059 
2060  /* 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 */
2061  if (read->duration == 0) {
2062  return SUCCESS;
2063  }
2064 
2065 #ifdef IPG
2066  {
2067  tmpstr[KS_DESCRIPTION_LENGTH + 7 - 1] = 0;
2068 
2069  if (read->base.ngenerated >= read->base.ninst) {
2070  return 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);
2071  }
2072 
2073  if (!ctrl->gradrf.is_cleared_on_tgt) { /* Reset gradrfctrl on tgt to remove dead pointers copied from host. */
2075  }
2076 
2077  if (read->echo == NULL) {
2078  /* this relies on that we have run the sequence in cveval() (i.e. on host) before to get proper grad.ninst */
2079  read->echo = (WF_PULSE *) AllocNode(read->base.ninst * sizeof(WF_PULSE));
2080  }
2081 
2082  placementindx = read->base.ngenerated; /* not sorted in time, but in placement order */
2083 
2084  /* the ADC start time is the bin center so we need to shift by half a tsp */
2085  /* tsp is always a multiple of 2, so div by two is exact here for int representation */
2086  acqwin_start = pos + psd_grd_wait + (int)(read->filt.tsp / 2.0f);
2087  /* in symmetric mode the waveforms are shifted half a GRAD_UPDATE_TIME earlier */
2089  acqwin_start -= GRAD_UPDATE_TIME / 2;
2090  }
2091 
2092  if (placementindx == 0 || read->filt.tdaq > 200) {
2093  dab_pos = DEFAULTPOS; /* results in 200 us before RBA (DEFAULTPOS is defined as 0. See: /ESE_.../psd/include/private/pulsegen.h) */
2094  } else {
2095  /* For small matrix sizes, the acq window is so short that the DAB packet
2096  for current lobe may collide with the RBA belonging to the previous lobe.
2097  We are not allowed to decrease the DAB-RBA distance below 200us. Instead,
2098  let's put it right after the end of the previous XTR pulse.
2099  With the default XTR-RBA offset of 143us, and since the RBA-RBA distance
2100  is equal to read->grad.duration, we will get a DAB-RBA offset of
2101  acqwin_duration + 143us - 17us (XTR duration)
2102  */
2103  dab_pos = pbegallssp(ks_pg_echossp(&read->echo[placementindx - 1], "xtr"), 0) + 32 /* LONG_DAB_length = 31 */;
2104  if (dab_pos + 200 > acqwin_start)
2105  dab_pos = DEFAULTPOS; /* fallback to default pos. Perhaps the readouts were not placed out in time order in the PSD ? */
2106  }
2107 
2108  /* name the ACQ pulse */
2109  sprintf(tmpstr, "%s%03d", read->description, placementindx); pulsename(&(read->echo[placementindx]), tmpstr);
2110 
2111  if (read->filt.fslot == KS_NOTSET) {
2112  /* add 'return' before ks_error once we've got an answer from GE regarding acqq and setrfltrs() */
2113  ks_error("ks_pg_read @tgt(%s): WARNING no filter slot assigned. First run setfilter(&read.filt)", read->description);
2114  }
2115 
2116  /* place the ACQ pulse */
2117  acqq_longdab(&(read->echo[placementindx]),
2118  (long) acqwin_start,
2119  (long) dab_pos, /* DAB */
2120  (long) DEFAULTPOS, /* XTR */
2121  read->filt.fslot /* filter slot */);
2122 
2123  read->base.ngenerated++;
2124 
2125  /* sort after the last instance is generated */
2126  if (read->base.ngenerated == read->base.ninst) {
2127  ks_sort_wfp_by_time(read->echo, read->base.ninst);
2128  }
2129 
2130  /* Associate this echo with a global filter slot number
2131  This number is generated by the GE function setfilter() in e.g. predownload(), before this function is called.
2132  For main sequences, there is also the wrapper function GEReq_predownload_setfilter() */
2133  if (read->filt.fslot >= 0 && read->filt.fslot <= MAX_FILTER_SLOT) {
2134  setrfltrs((int) read->filt.fslot, &(read->echo[placementindx]));
2135  }
2136 
2137  }
2138 #else /* IPG */
2139  {
2140  read->base.ninst++; /* update acq counter on host */
2141 
2142  int *locs = (int*) realloc(read->pos, sizeof(int)*read->base.ninst);
2143  if (!locs) {
2144  return KS_THROW("Realloc failed");
2145  }
2146  read->pos = locs;
2147  read->pos[read->base.ninst-1] = pos;
2148  }
2149 #endif /* IPG */
2150 
2151  status = ks_eval_addreadtogradrfctrl(&ctrl->gradrf, read);
2152  KS_RAISE(status);
2153  return SUCCESS;
2154 }
WF_PULSE * echo
Definition: KSFoundation.h:846
KS_DISCRETE_REPRESENTATION discrete_representation
Definition: KSFoundation.h:1233
void ks_sort_wfp_by_time(WF_PULSE *a, int nitems)
Sort WF_PULSEs in time
Definition: KSFoundation_common.c:4143
#define KS_NOTSET
Definition: KSFoundation.h:115
int ninst
Definition: KSFoundation.h:494
STATUS ks_error(const char *format,...)
Definition: KSFoundation_common.c:78
int * pos
Definition: KSFoundation.h:844
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:4799
KS_BASE base
Definition: KSFoundation.h:839
KS_GRADRFCTRL gradrf
Definition: KSFoundation.h:1236
KS_DESCRIPTION description
Definition: KSFoundation.h:840
#define KS_DESCRIPTION_LENGTH
Definition: KSFoundation.h:253
int is_cleared_on_tgt
Definition: KSFoundation.h:1065
int duration
Definition: KSFoundation.h:841
#define KS_RAISE(status)
Definition: KSFoundation.h:190
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:1738
STATUS acqq_longdab(WF_PULSE_ADDR pulse, LONG pos_ref, LONG dab_ref, LONG xtr_ref, LONG fslot_value)
Definition: KSFoundation_tgt.c:1840
FILTER_INFO filt
Definition: KSFoundation.h:843
int psd_grd_wait
void ks_tgt_reset_gradrfctrl(KS_GRADRFCTRL *gradrfctrl)
Definition: KSFoundation_tgt.c:1797
Definition: KSFoundation.h:419
int ngenerated
Definition: KSFoundation.h:496
#define KS_THROW(format,...)
Definition: KSFoundation.h:181

◆ 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
2031  {
2032 
2033 
2034  /* loc.ampscale != 1.0 is not allowed */
2035  if (!areSame(loc.ampscale,1.0)) {
2036  return ks_error("ks_pg_phaser(%s): field 'ampscale' in 2nd arg must be 1.0", phaser->grad.description);
2037  }
2038 
2039  /* loc.board must be one of XGRAD, YGRAD, ZGRAD */
2040  if (loc.board != XGRAD && loc.board != YGRAD && loc.board != ZGRAD) {
2041  return ks_error("ks_pg_phaser(%s): field 'board' in 2nd arg must be XGRAD, YGRAD, or ZGRAD", phaser->grad.description);
2042  }
2043 
2044  return ks_pg_trap(&phaser->grad, loc, ctrl);
2045 
2046 }
#define areSame(a, b)
Definition: KSFoundation.h:144
KS_TRAP grad
Definition: KSFoundation.h:1719
STATUS ks_error(const char *format,...)
Definition: KSFoundation_common.c:78
int board
Definition: KSFoundation.h:462
float ampscale
Definition: KSFoundation.h:464
KS_DESCRIPTION description
Definition: KSFoundation.h:668
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:1802

◆ 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
2161  {
2162 
2163  STATUS status;
2164  KS_SEQLOC omegaloc;
2165 
2166  /* checking for NULL pointers */
2167  if (readtrap == NULL) {
2168  return ks_error("%s: First argument is NULL", __FUNCTION__);
2169  }
2170  /* duration of 0 should not throw an error as this is a sign of that it has not been set up
2171  or has manually been disabled this way */
2172  if (readtrap->grad.duration == 0) {
2173  return SUCCESS;
2174  }
2175  if (readtrap->grad.description == NULL || readtrap->grad.description[0] == 0 || readtrap->grad.description[0] == ' ') {
2176  return ks_error("%s: Invalid description ('%s')", __FUNCTION__, readtrap->grad.description);
2177  }
2178 
2179 #ifdef IPG
2180  /* TGT (IPG): Don't place anything on hardware if ctrl->duration = 0.
2181  This is in concert with:
2182  - not performing createseq() in KS_SEQLENGTH if seqctrl.duration = 0
2183  - not playing sequence modules in scan() using ks_scan_playsequence() if seqctrl.duration = 0
2184  */
2185  if (ctrl->duration == 0) {
2186  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);
2187  }
2188 #endif
2189 
2190  if (!areSame(loc.ampscale, 1.0) && !areSame(loc.ampscale, -1.0) && !areSame(loc.ampscale, 0.0)) {
2191  return ks_error("%s(%s): .ampscale %g must be exactly +1, 0 or -1 for readout gradients", __FUNCTION__, readtrap->grad.description, loc.ampscale);
2192  }
2193 
2194  /*** acq ***/
2195  status = ks_pg_read(&readtrap->acq, loc.pos + readtrap->acqdelay, ctrl);
2196  KS_RAISE(status);
2197 
2198  /*** trapezoid ***/
2199  status = ks_pg_trap(&readtrap->grad, loc, ctrl);
2200  KS_RAISE(status);
2201 
2202  /* also place the omega wave form for rampsampled cases */
2203  if (readtrap->omega.duration > 0) {
2204  /* omega trapezoid matching the readout lobe to allow for FOV offsets in freq dir. with ramp sampling */
2205  omegaloc.board = OMEGA;
2206  omegaloc.pos = loc.pos + readtrap->acqdelay;
2207  omegaloc.ampscale = 1.0; /* just unity value for conformance. It does not matter during scanning, since ks_scan_omegahz() ignores this value */
2208  status = ks_pg_wave(&readtrap->omega, omegaloc, ctrl);
2209  KS_RAISE(status);
2210 
2211 #ifdef IPG
2212  short * iwave = (short *) AllocNode(readtrap->omega.res * sizeof(short));
2213  movewaveimm(iwave, &readtrap->omega.wfpulse[OMEGA][0], 0, readtrap->omega.res, FROMHARDWARE);
2214 
2215  /* phase is accumulated at 10 MHz */
2216  int k;
2217  readtrap->omega_iwave_phase_at_echo = 0;
2218  for(k = 0; k < (readtrap->time2center - readtrap->acqdelay); k++) {
2219  readtrap->omega_iwave_phase_at_echo += 10 * iwave[k / GRAD_UPDATE_TIME];
2220  }
2221  FreeNode(iwave);
2222 #endif
2223 
2224  }
2225 
2226  return SUCCESS;
2227 
2228 
2229 } /* 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:2051
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:2289
#define areSame(a, b)
Definition: KSFoundation.h:144
int pos
Definition: KSFoundation.h:463
KS_WAVE omega
Definition: KSFoundation.h:1562
STATUS ks_error(const char *format,...)
Definition: KSFoundation_common.c:78
int board
Definition: KSFoundation.h:462
int duration
Definition: KSFoundation.h:1227
KS_TRAP grad
Definition: KSFoundation.h:1561
float ampscale
Definition: KSFoundation.h:464
int omega_iwave_phase_at_echo
Definition: KSFoundation.h:1564
WF_PULSE ** wfpulse
Definition: KSFoundation.h:753
#define KS_RAISE(status)
Definition: KSFoundation.h:190
typedef struct used as argument to ks_pg_*** functions to control where and when to place a sequence ...
Definition: KSFoundation.h:461
KS_DESCRIPTION description
Definition: KSFoundation.h:668
KS_DESCRIPTION description
Definition: KSFoundation.h:1234
KS_READ acq
Definition: KSFoundation.h:1549
int duration
Definition: KSFoundation.h:673
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:1802
int time2center
Definition: KSFoundation.h:1560
int res
Definition: KSFoundation.h:746
int acqdelay
Definition: KSFoundation.h:1555
int duration
Definition: KSFoundation.h:747

◆ 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

pg_wave.svg
graphical description

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

This function adds an instruction and a waveform (state 0) in the sequencer memory. If you intend to use states this function must always be called first, before you try: ks_pg_addwaveformstate. For more infor see ks_pg_addwaveformstate

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
2289  {
2290 
2291  STATUS status;
2292  (void)status;
2293 
2294  float boardmax;
2295  char boardc[8] = "xyzrrto"; /* x,y,z,rho1,rho2,theta,omega */
2296 
2297  boardmax = ks_syslimits_gradtarget(loggrd, loc.board); /* returns 1.0 for non-gradients boards */
2298 
2299  float min_fs_factor = wave->abs_max_amp / boardmax;
2300  if (wave->fs_factor < min_fs_factor) {
2301  wave->fs_factor = min_fs_factor;
2302  }
2303 
2304  /* checking for NULL pointers */
2305  if (wave == NULL) {
2306  return ks_error("ks_pg_wave: First argument is NULL");
2307  }
2308  /* duration of 0 should not throw an error as this is a sign of that it has not been set up
2309  or has manually been disabled this way */
2310  if (wave->duration == 0) {
2311  return SUCCESS;
2312  }
2313  if (wave->description == NULL || wave->description[0] == 0 || wave->description[0] == ' ') {
2314  return ks_error("ks_pg_wave: Invalid description ('%s')", wave->description);
2315  }
2316 
2317 #ifndef IPG
2318  /* HOST: eval the wave to ensure its members have been computed correctly */
2319  int isGrad = (loc.board == XGRAD || loc.board == YGRAD || loc.board == ZGRAD);
2320  ks_wave_compute_params(wave);
2321  /* we use config variables because loggrd or physgrd may be derated for silent scanning purposes selrf waves are timing sensitive */
2322  extern float cfxfs, cfyfs, cfzfs;
2323  extern int cfrmp2xfs, cfrmp2yfs, cfrmp2zfs;
2324  float slew_limit = sqrt(3.0) * FMin(3, cfxfs, cfyfs, cfzfs)/IMax(3,cfrmp2xfs,cfrmp2yfs,cfrmp2zfs);
2325  /* multiply by 1.00001 to account for errors cause by floating point calcs */
2326  if ((isGrad) && (fabs(wave->abs_max_slew * loc.ampscale) > (1.00001 * slew_limit))) {
2327  int _i;
2328  ks_waveform_maxslew(wave->waveform, wave->res, wave->duration, &_i);
2329  KS_THROW("wave(%s) exceeds the least conservative gradient slew rate limit at index %d. This will not scan. %f > %f", wave->description, _i, fabs(wave->abs_max_slew * loc.ampscale), (1.00001 * slew_limit));
2330 #ifdef PSD_HW
2331  return FAILURE; /* TODO: why not return the error on the host? */
2332 #endif
2333  }
2334 #endif
2335 
2336 #ifdef IPG
2337  /* TGT: Don't place anything on hardware if ctrl->duration = 0.
2338  This is in concert with:
2339  - not performing createseq() in KS_SEQLENGTH if seqctrl.duration = 0
2340  - not playing sequence modules in scan() using ks_scan_playsequence() if seqctrl.duration = 0
2341  */
2342  if (ctrl->duration == 0) {
2343  return KS_THROW("%s: Refusing to place wave since seqctrl.duration = 0 (indicating `%s` not to be played in scan())", wave->description, ctrl->description);
2344  }
2345 
2346  if (!ctrl->gradrf.is_cleared_on_tgt) /* Reset gradrfctrl on tgt to remove dead pointers copied from host. */
2347  {
2349  }
2350 
2351 #endif
2352 
2353  /* input validation */
2354  if (wave->duration % 4) {
2355  return ks_error("ks_pg_wave(%s): field 'duration' (%d) must be divisible by 4", wave->description, wave->duration);
2356  }
2357  if (wave->res == 0) {
2358  return ks_error("ks_pg_wave(%s): field 'res' cannot be 0", wave->description);
2359  }
2360  if (wave->duration % wave->res) {
2361  return ks_error("ks_pg_wave(%s): field 'duration' (%d) must be divisible by 'res' %d", wave->description, wave->duration, wave->res);
2362  }
2363  if (loc.board == XGRAD || loc.board == YGRAD || loc.board == ZGRAD) {
2364  if ((wave->duration / wave->res) % GRAD_UPDATE_TIME) {
2365  return ks_error("ks_pg_wave(%s): field 'duration' (%d) must a multiple of %d times 'res' (%d) for gradient boards", wave->description, wave->duration, GRAD_UPDATE_TIME, wave->res);
2366  }
2367  if (wave->gradwave_units == KS_GRADWAVE_ABSOLUTE && !areSame(loc.ampscale,1.0)) {
2368  ks_error("ks_pg_wave(%s): ampscale must be +1.0 for gradwave with absolute units", wave->description); /* TODO: missing return */
2369  }
2370  } else {
2371  if ((wave->duration / wave->res) % RF_UPDATE_TIME) {
2372  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);
2373  }
2374  }
2375 
2376  if (isNaN(loc.ampscale)) {
2377  return ks_error("ks_pg_wave(%s): loc.ampscale NaN", wave->description);
2378  }
2379  if (loc.pos < GRAD_UPDATE_TIME || loc.pos > 4e6) {
2380  return ks_error("ks_pg_wave(%s): Invalid position %d, it must be between 4us and 4s", wave->description, loc.pos);
2381  }
2382  if (loc.board == OMEGA && !areSame(loc.ampscale,1.0)) {
2383  return ks_error("ks_pg_wave(%s): ampscale must be +1.0 for OMEGA", wave->description);
2384  }
2385  if (loc.board == THETA && !areSame(loc.ampscale,1.0) && !areSame(loc.ampscale,-1.0)) {
2386  return ks_error("ks_pg_wave(%s): ampscale must be +1.0 or -1.0 for THETA", wave->description);
2387  }
2388  if (loc.ampscale < -1.0 || loc.ampscale > 1.0) {
2389  return ks_error("ks_pg_wave(%s): Invalid ampscale %g, it must be between -1.0 and 1.0", wave->description, loc.ampscale);
2390  }
2391  if (fabs(wave->abs_max_amp * loc.ampscale) > boardmax && loc.board != THETA && loc.board != OMEGA) {
2392  return ks_error("ks_pg_wave(%s): wave amplitude (%.2f) exceeds maximum (%g) for board %c", wave->description, wave->abs_max_amp * loc.ampscale, boardmax, boardc[loc.board]);
2393  }
2394 
2395  if (loc.board != OMEGA) {
2396  loc.pos = RUP_GRD(loc.pos);
2397  }
2398 
2399 #ifdef HOST_TGT
2400 
2401  /* HOST */
2402  /* update instance counter on host only. This counter is used to allocate the right amount of WFINSTANCEs on IPG */
2403  wave->base.ninst++;
2404 
2405  KS_SEQLOC *locs = (KS_SEQLOC*) realloc(wave->locs, sizeof(KS_SEQLOC)*wave->base.ninst);
2406  if (!locs) {
2407  return KS_THROW("Realloc failed");
2408  }
2409  wave->locs = locs;
2410  wave->locs[wave->base.ninst-1] = loc;
2411 
2412  ks_sort_loc_by_timeboard(wave->locs, wave->base.ninst);
2413 
2414  /* To check if pg has been called */
2415  wave->base.nstates = 1; /* this function will create state0 */
2416  wave->p_waveformstates[0] = wave->waveform;
2417  /* for wave, we also need to keep track of #instances per X, Y, Z for gradient heating calculations.
2418  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 */
2419  if (loc.board == XGRAD || loc.board == YGRAD || loc.board == ZGRAD) {
2420  wave->gradnum[loc.board]++;
2421  }
2422 
2423 #else
2424 
2425  {
2426 
2427  short i;
2428  int isrf;
2429  char tmpstr[KS_DESCRIPTION_LENGTH + 14];
2430 
2431  if (wave->base.ngenerated >= wave->base.ninst) {
2432  return 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);
2433  }
2434 
2435 
2436  /* first call on TGT, allocate WF_PULSE * array where each element corresponds to one board */
2437  if (wave->wfpulse == NULL) {
2438  wave->wfpulse = (WF_PULSE **) AllocNode(7 * sizeof(WF_PULSE *)); /* WF pointers for all boards (XGRAD->OMEGA) */
2439  } /* wfpulse == NULL */
2440 
2441 
2442  /* first call on TGT using this loc.board, make waveform memory */
2443  if (wave->wfpulse[loc.board] == NULL) {
2444  WF_PULSE wfInit = INITPULSE;
2445  wave->wfpulse[loc.board] = (WF_PULSE *) AllocNode(wave->base.nstates * sizeof(WF_PULSE)); /* allocate memory for nstates */
2446  wave->wave_ptrs = (LONG *) AllocNode(wave->base.nstates * sizeof(LONG));
2447  /* give the pulse for each state a unqiue name */
2448  for (i = 0; i < wave->base.nstates; i++) {
2449  wave->wfpulse[loc.board][i] = wfInit;
2450  sprintf(tmpstr, "%s.state%d.%c", wave->description, i, boardc[loc.board]);
2451  pulsename(&(wave->wfpulse[loc.board][i]), tmpstr);
2452  wave->wfpulse[loc.board][i].type = TYPEXTERNAL;
2453  }
2454 
2455  /* wave -> short int wave for hardware use. All waveforms but THETA are autoscaled to +/- max_pg_wamp (32766)*/
2456  short * iwave = (short *) AllocNode(wave->res * sizeof(short));
2457  status = ks_wave2iwave(iwave, wave, loc.board);
2458  KS_RAISE(status);
2459 
2460  /* STATE 0 is the primary waveform; we reserve the memory for this pulse here*/
2461  createreserve(&wave->wfpulse[loc.board][0], (WF_PROCESSOR) loc.board, wave->res);
2462  movewaveimm(iwave, &wave->wfpulse[loc.board][0], (int) 0, wave->res, TOHARDWARE);
2463  getwave(&wave->wave_ptrs[0], &wave->wfpulse[loc.board][0]);
2464  FreeNode(iwave);
2465  } /* wfpulse[loc.board] == NULL */
2466 
2467  int iamp;
2468 
2469  /* create instruction at the current position */
2470  isrf = loc.board == RHO || loc.board == RHO2 || loc.board == THETA || loc.board == OMEGA;
2471 
2472  switch (loc.board) {
2473  case RHO:
2474  case RHO2:
2475  {
2476  wave->fs_factor = 1.0f;
2477  iamp = (int)((wave->abs_max_amp * loc.ampscale / boardmax) * MAX_PG_IAMP);
2478  break;
2479  }
2480  case XGRAD:
2481  case YGRAD:
2482  case ZGRAD:
2483  {
2484  iamp = (int)((wave->abs_max_amp * loc.ampscale / (wave->fs_factor * boardmax)) * MAX_PG_IAMP);
2485  break;
2486  }
2487  case OMEGA:
2488  {
2489  /* The TARDIS_FREQ_RES = 10e6 / (1 << 24) is the effect of one bit. This is the overflow period of the 24-bit register
2490  when incremented by 1 at 10 MHz = 0.586 Hz.
2491  The product of iwave and iamp is only 16 bit wide and is therefore shift left 8 bits before being added to the accumulator,
2492  this effectively mulitplies this value by 256 - so we need to shift 8 to the right here to be in the correct units [Hz] */
2493  iamp = (int)((wave->abs_max_amp * loc.ampscale / (wave->fs_factor * TARDIS_FREQ_RES))) >> 8;
2494  break;
2495  }
2496  case THETA:
2497  {
2498  iamp = (int)(loc.ampscale * MAX_PG_IAMP);
2499  break;
2500  }
2501  default:
2502  KS_RAISE(ks_error("Wave scaling is currently not implemented for board: %i", loc.board));
2503  }
2504 
2505  createinstr(&wave->wfpulse[loc.board][0],
2506  (long) (loc.pos + (psd_rf_wait * isrf)),
2507  (long) wave->duration,
2508  (long) iamp);
2509 
2510  /* WF instance bookkeeping */
2511  if (wave->wfi == NULL) {
2512  wave->wfi = (KS_WFINSTANCE *) AllocNode(wave->base.ninst * sizeof(KS_WFINSTANCE));
2513  }
2514  wave->wfi[wave->base.ngenerated].boardinstance = wave->wfpulse[loc.board][0].ninsts - 1;
2515  wave->wfi[wave->base.ngenerated].wf = wave->wfpulse[loc.board];
2516  wave->wfi[wave->base.ngenerated].loc = loc;
2517 
2518  wave->base.ngenerated++;
2519 
2520  wave->p_waveformstates[0] = wave->waveform;
2521 
2522  /* sort after the last instance is generated */
2523  if (wave->base.ngenerated == wave->base.ninst) {
2524  ks_sort_wfi_by_timeboard(wave->wfi, wave->base.ninst);
2525  }
2526 
2527  }
2528 
2529 #endif /* IPG or HOST */
2530 
2531  /* register this KS_WAVE for later heating calculations */
2532  {
2533  STATUS status = ks_eval_addwavetogradrfctrl(&ctrl->gradrf, wave);
2534  KS_RAISE(status);
2535  }
2536  wave->current_state = (int *) AllocNode(wave->base.ngenerated * sizeof(int*));
2537 
2538  return SUCCESS;
2539 
2540 } /* ks_pg_wave */
Definition: KSFoundation.h:2359
void ks_sort_wfi_by_timeboard(KS_WFINSTANCE *a, int nitems)
Sort WF_INSTANCEs in time, then board
Definition: KSFoundation_common.c:4120
float fs_factor
Definition: KSFoundation.h:756
int boardinstance
Definition: KSFoundation.h:484
float abs_max_slew
Definition: KSFoundation.h:760
#define areSame(a, b)
Definition: KSFoundation.h:144
KS_SEQLOC * locs
Definition: KSFoundation.h:752
int pos
Definition: KSFoundation.h:463
(Internal use) Structure being a part of various sequence objects to sort them in time across boards...
Definition: KSFoundation.h:483
int ninst
Definition: KSFoundation.h:494
int gradwave_units
Definition: KSFoundation.h:751
STATUS ks_error(const char *format,...)
Definition: KSFoundation_common.c:78
int board
Definition: KSFoundation.h:462
int duration
Definition: KSFoundation.h:1227
float ks_syslimits_gradtarget(LOG_GRAD loggrd, int board)
Returns the maximum target amplitude for a board (internal use)
Definition: KSFoundation_common.c:264
float ampscale
Definition: KSFoundation.h:464
KS_GRADRFCTRL gradrf
Definition: KSFoundation.h:1236
int isNaN(float a)
Definition: KSFoundation_common.c:58
KS_WFINSTANCE * wfi
Definition: KSFoundation.h:754
#define KS_DESCRIPTION_LENGTH
Definition: KSFoundation.h:253
int gradnum[3]
Definition: KSFoundation.h:750
float abs_max_amp
Definition: KSFoundation.h:761
void ks_wave_compute_params(KS_WAVE *const wave)
Fills the members of the KS_WAVE sequence object
Definition: KSFoundation_common.c:1123
KS_SEQLOC loc
Definition: KSFoundation.h:486
KS_DESCRIPTION description
Definition: KSFoundation.h:745
LOG_GRAD loggrd
LONG * wave_ptrs
Definition: KSFoundation.h:763
int is_cleared_on_tgt
Definition: KSFoundation.h:1065
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:4735
WF_PULSE ** wfpulse
Definition: KSFoundation.h:753
KS_BASE base
Definition: KSFoundation.h:744
#define KS_RAISE(status)
Definition: KSFoundation.h:190
float ks_waveform_maxslew(const KS_WAVEFORM waveform, int res, int duration, int *index)
Returns the maximum absolute value in a KS_WAVEFORM
Definition: KSFoundation_common.c:1366
typedef struct used as argument to ks_pg_*** functions to control where and when to place a sequence ...
Definition: KSFoundation.h:461
WF_PULSE * wf
Definition: KSFoundation.h:485
int nstates
Definition: KSFoundation.h:495
int * current_state
Definition: KSFoundation.h:762
float * p_waveformstates[KS_WAVE_MAXNSTATES]
Definition: KSFoundation.h:749
void ks_tgt_reset_gradrfctrl(KS_GRADRFCTRL *gradrfctrl)
Definition: KSFoundation_tgt.c:1797
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:1721
KS_DESCRIPTION description
Definition: KSFoundation.h:1234
int ngenerated
Definition: KSFoundation.h:496
int psd_rf_wait
#define KS_THROW(format,...)
Definition: KSFoundation.h:181
int res
Definition: KSFoundation.h:746
KS_WAVEFORM waveform
Definition: KSFoundation.h:748
int duration
Definition: KSFoundation.h:747
void ks_sort_loc_by_timeboard(KS_SEQLOC *a, int nitems)
Sort KS_SEQLOCs in time, then board
Definition: KSFoundation_common.c:4128

◆ ks_pg_addwaveformstate()

STATUS ks_pg_addwaveformstate ( KS_WAVE wave,
KS_WAVEFORM  waveform,
int  state_index 
)

Allocates an additional waveformstate in sequencer memory

pg_addwaveformstate.svg
graphical description

This function reserves additional space on the sequencer waveform memory and assignes the state=state_index to the waveform. During the scan it is then possible to make any instance of theat KS_WAVE object point to an alternative state using the fuction ks_scan_setwavestate, effectively changing the waveform shape in real time. This approach is much more effecient and safer than ks_scan_wave2hardware because we can ensure the waveform states used pass gradient and SAR limits. A drawback is that sequencer memory is not unlimited, so we have limited the number of states to #define KS_WAVE_MAXNSTATES 16 (in future we will increase this). If you require more waveform shapes than that, then you would have to use the more complicated ks_scan_wave2hardware.

ks_pg_wave needs to be called on the KS_WAVE object before you can use this function. It is also important to note that ks_pg_wave reserves state0, so don't overwrite 0 if you intend to use it. This function can be used on any KS_WAVE object. eg. if you would like to add multiple RF envelopes to a KS_SELRF compound object you can add additional states to the KS_WAVE members (like &myselrf->rf.wave) once you have called the pg_selrf function.

Parameters
[in,out]wavePointer to a KS_WAVE sequence object
[in]waveformA KS_WAVEFORM representing a new state/shape
[in]state_index
2544  {
2545 
2546  STATUS status;
2547  (void)status;
2548 
2549 #ifdef HOST_TGT
2550  const float waveform_abs_max = ks_waveform_absmax(waveform, wave->res);
2551  if(wave->base.nstates == 0) {
2552  return ks_error("%s [%s]: cannot add state before ks_wave_pg is called.", __FUNCTION__, wave->description);
2553  }
2554 
2555  if (state_index == wave->base.nstates) {
2556  wave->base.nstates = state_index + 1;
2557  }
2558 
2559  if(wave->max_amp < waveform_abs_max) {
2560  return ks_error("%s [%s]: can't add a waveform state with a larger amplitude.", __FUNCTION__, wave->description);
2561  }
2562 
2563  int idx_slew_max;
2564  float slew_max = ks_waveform_maxslew(waveform, wave->res, wave->duration, &idx_slew_max);
2565  /* times > 1 in case of float rounding */
2566  extern float cfxfs, cfyfs, cfzfs;
2567  extern int cfrmp2xfs, cfrmp2yfs, cfrmp2zfs;
2568  float slew_limit = sqrt(3.0) * FMin(3, cfxfs, cfyfs, cfzfs)/IMax(3,cfrmp2xfs,cfrmp2yfs,cfrmp2zfs)* 1.00001;
2569  int i = 0;
2570  for (; i < wave->base.ninst; i++) {
2571  KS_SEQLOC loc = wave->locs[i];
2572  int isGrad = (loc.board == XGRAD || loc.board == YGRAD || loc.board == ZGRAD);
2573  if ((isGrad) && (slew_max > slew_limit)) {
2574  /* TODO: no return? */
2575 #ifdef PSD_HW
2576  return KS_THROW("[%s]: state %i exceeds the least conservative gradient slew rate limit at index %d. %f > %f",
2577  wave->description, state_index, idx_slew_max, slew_max, slew_limit);
2578 #else
2579  KS_THROW("[%s]: state %i exceeds the least conservative gradient slew rate limit at index %d. This will not scan %f > %f",
2580  wave->description, state_index, idx_slew_max, slew_max, slew_limit);
2581 #endif
2582  }
2583  if (loc.board == RHO) {
2584  if (waveform_abs_max > 1.0f) {
2585  return KS_THROW("[%s]: RHO waves must be normalised (%1.3f > 1)", wave->description, waveform_abs_max);
2586  }
2587  }
2588  }
2589 #endif
2590 
2591  if (state_index < KS_WAVE_MAXNSTATES) {
2592  wave->p_waveformstates[state_index] = waveform;
2593  } else {
2594  return ks_error("%s [%s]: you have exceeded the maximum number of wavestates = %i", __FUNCTION__, wave->description, KS_WAVE_MAXNSTATES);
2595  }
2596 
2597  /*TODO: test slew rate for this board if(wave->abs_max_slew < ks_waveform_maxslew(waveform, wave->res, wave->duration)) {
2598  return ks_error("%s [%s]: can't add a waveform state with a higher max slew rate", __FUNCTION__, wave->description);
2599  }*/
2600 
2601 #ifndef HOST_TGT
2602  if(state_index > wave->base.nstates)
2603  return ks_error("ks_pg_addwavestate: state_index too large");
2604  /* wave -> short int wave for hardware use. All waveforms but THETA are autoscaled to +/- max_pg_wamp (32766)*/
2605  WF_PULSE * wfpulse;
2606  int board;
2607  short * iwave = (short *) AllocNode(wave->res * sizeof(short));
2608  int skippedboards = 0;
2609  for(board = 0; board < 7; board++) {
2610  wfpulse = wave->wfpulse[board];
2611  if (!wfpulse) {
2612  skippedboards++;
2613  continue;
2614  }
2615 
2616  float max_amp;
2617  if (board == RHO) {
2618  /* RHO waves are scaled after predownload */
2619  max_amp = 1.0;
2620  } else {
2621  max_amp = wave->abs_max_amp;
2622  }
2623  /* Here we adjust the iwave amplitude to achieve the correct final amplitude iamp*iwave*/
2624  status = ks_waveform2iwave(iwave,
2625  waveform,
2626  wave->res,
2627  board,
2628  max_amp,
2629  wave->fs_factor);
2630  KS_RAISE(status);
2631 
2632  createreserve(&wfpulse[state_index], (WF_PROCESSOR) board, wave->res);
2633  movewaveimm(iwave, &wfpulse[state_index], (int) 0, wave->res, TOHARDWARE);
2634  getwave(&wave->wave_ptrs[state_index], &wave->wfpulse[board][state_index]);
2635  }
2636  FreeNode(iwave);
2637 
2638  if (skippedboards == 7) {
2639  return ks_error("%s [%s] -> state %d: no WF_PULSES have been allocated, something is very wrong!", __FUNCTION__, wave->description, state_index);
2640  }
2641 
2642 #endif
2643 
2644  return SUCCESS;
2645 }
float fs_factor
Definition: KSFoundation.h:756
KS_SEQLOC * locs
Definition: KSFoundation.h:752
int ninst
Definition: KSFoundation.h:494
STATUS ks_error(const char *format,...)
Definition: KSFoundation_common.c:78
STATUS ks_waveform2iwave(KS_IWAVE iwave, const KS_WAVEFORM waveform, int res, int board, float max, float fs_factor)
(Internal use) Conversion of a KS_WAVEFORM to a short int array for use on hardware
Definition: KSFoundation_common.c:1698
int board
Definition: KSFoundation.h:462
float abs_max_amp
Definition: KSFoundation.h:761
KS_DESCRIPTION description
Definition: KSFoundation.h:745
LONG * wave_ptrs
Definition: KSFoundation.h:763
WF_PULSE ** wfpulse
Definition: KSFoundation.h:753
KS_BASE base
Definition: KSFoundation.h:744
float max_amp
Definition: KSFoundation.h:759
#define KS_RAISE(status)
Definition: KSFoundation.h:190
float ks_waveform_maxslew(const KS_WAVEFORM waveform, int res, int duration, int *index)
Returns the maximum absolute value in a KS_WAVEFORM
Definition: KSFoundation_common.c:1366
typedef struct used as argument to ks_pg_*** functions to control where and when to place a sequence ...
Definition: KSFoundation.h:461
#define KS_WAVE_MAXNSTATES
Definition: KSFoundation.h:293
float ks_waveform_absmax(const KS_WAVEFORM waveform, int res)
Returns the maximum absolute value in a KS_WAVEFORM
Definition: KSFoundation_common.c:1336
int nstates
Definition: KSFoundation.h:495
float * p_waveformstates[KS_WAVE_MAXNSTATES]
Definition: KSFoundation.h:749
#define KS_THROW(format,...)
Definition: KSFoundation.h:181
int res
Definition: KSFoundation.h:746
int duration
Definition: KSFoundation.h:747

◆ 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, see ks_scan_setwavestate, ks_scan_wave2hardware if you want to modiy waveforms during the scan.

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

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
2652  {
2653 
2654  STATUS status;
2655 
2656  /* input validation */
2657  if (rf == NULL) {
2658  return ks_error("%s: First argument is NULL", __FUNCTION__);
2659  }
2660 
2661  /* duration of 0 should not throw an error as this is a sign of that it has not been set up
2662  or has manually been disabled this way */
2663  if (rf->rfwave.duration == 0) {
2664  return SUCCESS;
2665  }
2666  if (rf->rfwave.description == NULL || rf->rfwave.description[0] == 0 || rf->rfwave.description[0] == ' ') {
2667  return ks_error("ks_pg_rf: Invalid description ('%s')", rf->rfwave.description);
2668  }
2669 
2670 #ifdef IPG
2671  /* TGT: Don't place anything on hardware if ctrl->duration = 0.
2672  This is in concert with:
2673  - not performing createseq() in KS_SEQLENGTH if seqctrl.duration = 0
2674  - not playing sequence modules in scan() using ks_scan_playsequence() if seqctrl.duration = 0
2675  */
2676  if (ctrl->duration == 0) {
2677  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);
2678  }
2679  /* TGT: make sure (the RF pulses in) the sequence module were a part of the seqcollection when GEReq_eval_rfscaling() was called */
2680  if (ctrl->rfscalingdone == FALSE && ctrl->duration > 0) {
2681  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);
2682  }
2683 
2684  if (!ctrl->gradrf.is_cleared_on_tgt) /* Reset gradrfctrl on tgt to remove dead pointers copied from host. */
2685  {
2687  }
2688 
2689 #endif
2690 
2691  loc.pos = RUP_RF(loc.pos);
2692 
2693  RFEnvelopeWaveformGeneratorCheck(rf->rfwave.description, (WF_PROCESSOR) ks_rhoboard);
2694 
2695  if (rf->omegawave.res) {
2696  if (rf->omegawave.duration != rf->rfwave.duration) {
2697  return ks_error("ks_pg_rf (%s): The duration of the omega waveform must be equal to the RF duration", rf->rfwave.description);
2698  }
2699  }
2700  if (rf->thetawave.res) {
2701  if (rf->thetawave.duration != rf->rfwave.duration) {
2702  return ks_error("ks_pg_rf (%s): The duration of the theta waveform must be equal to the RF duration", rf->rfwave.description);
2703  }
2704  }
2705 
2706  /* Set ctrl->momentstart if this is an excitation pulse */
2707  if (rf->role == KS_RF_ROLE_EXC) {
2708  ctrl->momentstart = RUP_GRD(loc.pos + rf->start2iso); /* RUP_GRD: make sure it is on a 4us raster too for gradient timing calcs */
2709  }
2710 
2711  /* 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()).
2712  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
2713  ks_gnuplot_seqmodule(). This would cause the RF pulse to show up twice in the plot / pdf / image.
2714  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 */
2715  {
2716  STATUS status = ks_eval_addrftogradrfctrl(&ctrl->gradrf, rf);
2717  KS_RAISE(status);
2718  }
2719 
2720 #ifdef HOST_TGT
2721 
2722  /* update rfpulse.num counter on host only. We can use this ninst variable as info
2723  for RF heating calculations */
2724  rf->rfpulse.num++;
2725 
2726  /* now that we have shown that we are really going to use this pulse, set its
2727  activity flag to SCAN, APS2, MPS2 */
2728  rf->rfpulse.activity = PSD_APS2_ON + PSD_MPS2_ON + PSD_SCAN_ON;
2729 
2730 #else
2731 
2732  if (areSame(rf->rfpulse.num, 0)) {
2733  return ks_error("ks_pg_rf @tgt(%s): pulsegen was never run on HOST (rf->rfpulse.num = 0)", rf->rfwave.description);
2734  }
2735 
2736  if (rf->rfpulse.activity == 0) {
2737  return ks_error("ks_pg_rf @tgt(%s): RF pulse with zero activity", rf->rfwave.description);
2738  }
2739 
2740  /* The values in rf->rfwave.waveform[] are relative (unit-less). The actual amplitude of the RF wave
2741  is only controlled by rf->amp (which in turn is set by GE's setScale() and indirectly by peakB1()).
2742  Prepare RF waveform with correct amplitude on TGT side that can be used with ks_pg_wave() to give the right
2743  RF amplitude, after we have done RF scaling on HOST and hence have a final value for rf->amp.
2744  The units of this RF waveform, before and after calling ks_wave_multiplywal(), are relative (not [G])
2745  due to GE's RF scaling process involving e.g. xmtaddScan.
2746  Look in GERequired.e:GEReq_eval_gradrflimits() for more info on how peakB1() and setScale() are used. */
2747 
2748  ks_wave_multiplyval(&rf->rfwave, rf->amp / ks_wave_absmax(&rf->rfwave));
2749 
2750 #endif
2751 
2752 
2753 
2754  /* place the RF wave */
2755  loc.board = RHO;
2756  status = ks_pg_wave(&rf->rfwave, loc, ctrl);
2757  KS_RAISE(status);
2758 
2759 #ifdef IPG
2760  /* add SSP RF bits that handles the frequency control and blanking/unblanking the receiver */
2761  addrfbits(&rf->rfwave.wfpulse[RHO][0], 0, loc.pos + psd_rf_wait, rf->rfwave.duration);
2762  /* set RF frequency offset */
2763  setfrequency((int)(rf->cf_offset / TARDIS_FREQ_RES), &rf->rfwave.wfpulse[RHO][0], rf->rfwave.base.ngenerated - 1);
2764 #endif
2765 
2766  /* set ampscale to 1. If you want to scale the omega or theata board, do it seperatly */
2767  loc.ampscale = 1;
2768 
2769  /* place the OMEGA wave */
2770  loc.board = OMEGA;
2771  if (rf->omegawave.res) {
2772  status = ks_pg_wave(&rf->omegawave, loc, ctrl);
2773  KS_RAISE(status);
2774 
2775 #ifdef IPG
2776  short * iwave = (short *) AllocNode(rf->omegawave.res * sizeof(short));
2777  movewaveimm(iwave, &rf->omegawave.wfpulse[OMEGA][0], 0, rf->omegawave.res, FROMHARDWARE);
2778 
2779  /* phase is accumulated at 10 MHz */
2780  int k;
2781  rf->omega_iwave_phase_at_iso = 0;
2782  for(k = 0; k < rf->start2iso; k++) {
2783  rf->omega_iwave_phase_at_iso += 10 * iwave[k / GRAD_UPDATE_TIME];
2784  }
2785  FreeNode(iwave);
2786 #endif
2787  }
2788 
2789 #ifdef IPG
2790  if (rf->omegawave.res) {
2791  /* add SSP RF bits that handles the frequency control and blanking/unblanking the receiver */
2792  addrfbits(&rf->omegawave.wfpulse[OMEGA][0], 0, loc.pos + psd_rf_wait, rf->omegawave.duration);
2793  }
2794 #endif
2795 
2796  /* place the THETA wave */
2797  loc.board = THETA;
2798  if (rf->thetawave.res) {
2799  status = ks_pg_wave(&rf->thetawave, loc, ctrl);
2800  KS_RAISE(status);
2801  }
2802 
2803 #ifdef IPG
2804  if (rf->thetawave.res) {
2805  /* add SSP RF bits that handles the frequency control and blanking/unblanking the receiver */
2806  addrfbits(&rf->thetawave.wfpulse[THETA][0], 0, loc.pos + psd_rf_wait, rf->thetawave.duration);
2807  }
2808 #endif
2809 
2810  return SUCCESS;
2811 
2812 } /* 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:2289
float cf_offset
Definition: KSFoundation.h:1030
int start2iso
Definition: KSFoundation.h:1032
#define areSame(a, b)
Definition: KSFoundation.h:144
float ks_wave_absmax(const KS_WAVE *wave)
Returns the maximum absolute value in a KS_WAVE sequence object
Definition: KSFoundation_common.c:1359
int pos
Definition: KSFoundation.h:463
int role
Definition: KSFoundation.h:1027
int ks_rhoboard
Definition: KSFoundation_common.c:42
int omega_iwave_phase_at_iso
Definition: KSFoundation.h:1040
RF_PULSE rfpulse
Definition: KSFoundation.h:1036
int rfscalingdone
Definition: KSFoundation.h:1231
int momentstart
Definition: KSFoundation.h:1228
STATUS ks_error(const char *format,...)
Definition: KSFoundation_common.c:78
int board
Definition: KSFoundation.h:462
int duration
Definition: KSFoundation.h:1227
Definition: KSFoundation.h:2340
float ampscale
Definition: KSFoundation.h:464
KS_GRADRFCTRL gradrf
Definition: KSFoundation.h:1236
KS_WAVE thetawave
Definition: KSFoundation.h:1039
KS_DESCRIPTION description
Definition: KSFoundation.h:745
int is_cleared_on_tgt
Definition: KSFoundation.h:1065
WF_PULSE ** wfpulse
Definition: KSFoundation.h:753
KS_WAVE rfwave
Definition: KSFoundation.h:1037
KS_BASE base
Definition: KSFoundation.h:744
float amp
Definition: KSFoundation.h:1031
#define KS_RAISE(status)
Definition: KSFoundation.h:190
void ks_tgt_reset_gradrfctrl(KS_GRADRFCTRL *gradrfctrl)
Definition: KSFoundation_tgt.c:1797
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:4771
KS_WAVE omegawave
Definition: KSFoundation.h:1038
KS_DESCRIPTION description
Definition: KSFoundation.h:1234
int ngenerated
Definition: KSFoundation.h:496
void ks_wave_multiplyval(KS_WAVE *wave, float val)
In-place scalar multiplication of a KS_WAVE
Definition: KSFoundation_common.c:1653
int psd_rf_wait
int res
Definition: KSFoundation.h:746
int duration
Definition: KSFoundation.h:747

◆ 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, see ks_scan_setwavestate, ks_scan_wave2hardware if you want to modiy waveforms during the scan.

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
2817  {
2818 
2819  STATUS status;
2820  KS_SEQLOC rfloc;
2821  KS_SEQLOC gradloc;
2822 
2823  if (loc.board != XGRAD && loc.board != YGRAD && loc.board != ZGRAD) {
2824  return ks_error("%s(%s): loc.board (2nd arg) must be a gradient board", __FUNCTION__, selrf->rf.rfwave.description);
2825  }
2826 
2827  gradloc = loc; gradloc.ampscale = 1.0;
2828  rfloc = loc; rfloc.board = RHO;
2829 
2830  /* RF starts after the preselgrad (if present) and ramp of slicesel (if present) */
2831  rfloc.pos += selrf->grad2rf_start;
2832 
2833  /************* generate the RF (incl. optional THETA & OMEGA) *************/
2834 
2835  status = ks_pg_rf(&selrf->rf, rfloc, ctrl);
2836  KS_RAISE(status);
2837 
2838  /************************ place gradients *********************************/
2839 
2840  gradloc.ampscale = (selrf->rf.role != KS_RF_ROLE_EXC) ? loc.ampscale2 : 1.0;
2841  /* Pre-slice sel gradient. Roles: e.g. left crusher or dephasing gradient in bSSFP */
2842  status = ks_pg_trap(&selrf->pregrad, gradloc, ctrl);
2843  KS_RAISE(status);
2844  gradloc.pos += selrf->pregrad.duration;
2845 
2846 
2847 
2848  /* slice select gradient */
2849  gradloc.ampscale = 1.0;
2850 
2851  if (selrf->gradwave.res) {
2852  /* use gradwave (not trapezoid) */
2853  status = ks_pg_wave(&selrf->gradwave, gradloc, ctrl);
2854  KS_RAISE(status);
2855 
2856  gradloc.pos += selrf->gradwave.duration;
2857 
2858  } else {
2859  /* use trapezoid (not gradwave) */
2860 
2861  if (selrf->grad.plateautime != selrf->rf.rfwave.duration) {
2862  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);
2863  } else {
2864  status = ks_pg_trap(&selrf->grad, gradloc, ctrl);
2865  KS_RAISE(status);
2866 
2867  gradloc.pos += selrf->grad.duration;
2868  }
2869 
2870  } /* slice select */
2871 
2872 
2873  /* post-slice sel gradient. Roles: e.g. right crusher or refocusing gradient */
2874  gradloc.ampscale = (selrf->rf.role != KS_RF_ROLE_EXC) ? loc.ampscale2 : 1.0;
2875  status = ks_pg_trap(&selrf->postgrad, gradloc, ctrl);
2876  KS_RAISE(status);
2877  gradloc.pos += selrf->postgrad.duration;
2878 
2879 
2880  return SUCCESS;
2881 
2882 } /* ks_pg_selrf */
float ampscale2
Definition: KSFoundation.h:465
int plateautime
Definition: KSFoundation.h:672
KS_TRAP grad
Definition: KSFoundation.h:1463
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:2289
int pos
Definition: KSFoundation.h:463
int role
Definition: KSFoundation.h:1027
KS_WAVE gradwave
Definition: KSFoundation.h:1465
STATUS ks_error(const char *format,...)
Definition: KSFoundation_common.c:78
int board
Definition: KSFoundation.h:462
Definition: KSFoundation.h:2340
float ampscale
Definition: KSFoundation.h:464
KS_TRAP pregrad
Definition: KSFoundation.h:1462
int grad2rf_start
Definition: KSFoundation.h:1466
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:2652
KS_RF rf
Definition: KSFoundation.h:1454
KS_DESCRIPTION description
Definition: KSFoundation.h:745
KS_WAVE rfwave
Definition: KSFoundation.h:1037
#define KS_RAISE(status)
Definition: KSFoundation.h:190
typedef struct used as argument to ks_pg_*** functions to control where and when to place a sequence ...
Definition: KSFoundation.h:461
KS_TRAP postgrad
Definition: KSFoundation.h:1464
int duration
Definition: KSFoundation.h:673
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:1802
int res
Definition: KSFoundation.h:746
int duration
Definition: KSFoundation.h:747

◆ 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
2889  {
2890  int i;
2891  const int first = loc.board == KS_ALL ? XGRAD : loc.board;
2892  const int last = loc.board == KS_ALL ? SSP : loc.board;
2893 
2894  /* checking for NULL pointers */
2895  if (wait == NULL) {
2896  return ks_error("ks_pg_wait: First argument is NULL");
2897  }
2898 
2899  /* duration of 0 should not throw an error as this is a sign of that it has not been set up
2900  or has manually been disabled this way */
2901  if (wait->max_duration == 0 || wait->pg_duration == 0) {
2902  return SUCCESS;
2903  }
2904 
2905  if (wait->description == NULL || wait->description[0] == 0 || wait->description[0] == ' ') {
2906  return ks_error("ks_pg_wait: Invalid description ('%s')", wait->description);
2907  }
2908 
2909  /* disallow EPI dual boards */
2910  if (loc.board >= KS_FREQX_PHASEY && loc.board <= KS_XYZ) {
2911  return ks_error("ks_pg_wait(%s): Invalid board selection: %d", wait->description, loc.board);
2912  }
2913 
2914  loc.pos = RUP_GRD(loc.pos);
2915 
2916 #ifdef IPG
2917  {
2918 
2919  /* TGT: Don't place anything on hardware if ctrl->duration = 0.
2920  This is in concert with:
2921  - not performing createseq() in KS_SEQLENGTH if seqctrl.duration = 0
2922  - not playing sequence modules in scan() using ks_scan_playsequence() if seqctrl.duration = 0
2923  */
2924  if (ctrl->duration == 0) {
2925  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);
2926  return SUCCESS;
2927  }
2928 
2929  if (wait->base.ngenerated >= wait->base.ninst) {
2930  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);
2931  }
2932 
2933  if (wait->wfpulse == NULL) {
2934  WF_PULSE wfInit = INITPULSE;
2935 
2936  const char boardc[9] = "xyzrrtos";
2937  char tmpstr[KS_DESCRIPTION_LENGTH + 4];
2938  tmpstr[KS_DESCRIPTION_LENGTH + 4 - 1] = 0;
2939 
2940  wait->wfpulse = (WF_PULSE *) AllocNode(8 * sizeof(WF_PULSE));
2941  for (i = XGRAD; i <= SSP; i++) {
2942  if (i != RHO2){
2943  /* init pulse structure */
2944  wait->wfpulse[i] = wfInit;
2945 
2946  /* Name for WF_PULSEs */
2947  sprintf(tmpstr, "%s.w%c", wait->description, boardc[i]); pulsename(&(wait->wfpulse[i]), tmpstr);
2948  createconst(&(wait->wfpulse[i]), (WF_PROCESSOR) i, wait->pg_duration, 0);
2949 
2950  } /* per board */
2951  }
2952 
2953  } /* wfpulse == NULL */
2954 
2955  if (!wait->wfi) {
2956  wait->wfi = (KS_WFINSTANCE *) AllocNode(wait->base.ninst * sizeof(KS_WAIT));
2957  }
2958 
2959  for (i = first; i <= last; ++i) {
2960  if (i == KS_RHO2) continue; /* skip RHO2 */
2961 
2962  loc.board = i;
2963  int isrf = loc.board == RHO || loc.board == RHO2 || loc.board == THETA || loc.board == OMEGA;
2964  int isssp = loc.board == SSP;
2965 
2966  createinstr(&(wait->wfpulse[i]), loc.pos + (isrf * psd_rf_wait) + (isssp * psd_grd_wait), wait->pg_duration, 0);
2967  wait->wfi[wait->base.ngenerated].boardinstance = wait->wfpulse[i].ninsts - 1; /* zero based */
2968  wait->wfi[wait->base.ngenerated].wf = &(wait->wfpulse[i]);
2969  wait->wfi[wait->base.ngenerated].loc = loc;
2970  wait->base.ngenerated++;
2971  }
2972 
2973  /* sort after the last instance is generated */
2974  if (wait->base.ngenerated == wait->base.ninst) {
2975  ks_sort_wfi_by_timeboard(wait->wfi, wait->base.ninst);
2976  }
2977  }
2978 #else
2979  {
2980  KS_SEQLOC *locs = (KS_SEQLOC*) realloc(wait->locs, sizeof(KS_SEQLOC)*(wait->base.ninst + last - first + 1));
2981  if (!locs) {
2982  return KS_THROW("Realloc failed");
2983  }
2984  wait->locs = locs;
2985 
2986  for (i = first; i <= last; ++i) {
2987  if (i == KS_RHO2) continue; /* skip RHO2 */
2988  loc.board = i;
2989  wait->locs[wait->base.ninst] = loc;
2990  wait->base.ninst++;
2991  ks_sort_loc_by_timeboard(wait->locs, wait->base.ninst);
2992  STATUS status = ks_eval_addwaittogradrfctrl(&ctrl->gradrf, wait);
2993  KS_RAISE(status);
2994  }
2995  }
2996 #endif
2997 
2998  return SUCCESS;
2999 
3000 } /* 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:4120
Definition: KSFoundation.h:2319
int boardinstance
Definition: KSFoundation.h:484
int pos
Definition: KSFoundation.h:463
Definition: KSFoundation.h:2315
(Internal use) Structure being a part of various sequence objects to sort them in time across boards...
Definition: KSFoundation.h:483
int ninst
Definition: KSFoundation.h:494
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:542
STATUS ks_error(const char *format,...)
Definition: KSFoundation_common.c:78
STATUS ks_dbg(const char *format,...)
Definition: KSFoundation_common.c:157
int board
Definition: KSFoundation.h:462
Definition: KSFoundation.h:2325
int duration
Definition: KSFoundation.h:1227
KS_DESCRIPTION description
Definition: KSFoundation.h:544
Definition: KSFoundation.h:2326
KS_GRADRFCTRL gradrf
Definition: KSFoundation.h:1236
#define KS_DESCRIPTION_LENGTH
Definition: KSFoundation.h:253
KS_SEQLOC loc
Definition: KSFoundation.h:486
KS_BASE base
Definition: KSFoundation.h:543
KS_SEQLOC * locs
Definition: KSFoundation.h:547
#define KS_RAISE(status)
Definition: KSFoundation.h:190
KS_WFINSTANCE * wfi
Definition: KSFoundation.h:549
typedef struct used as argument to ks_pg_*** functions to control where and when to place a sequence ...
Definition: KSFoundation.h:461
WF_PULSE * wf
Definition: KSFoundation.h:485
int pg_duration
Definition: KSFoundation.h:545
int max_duration
Definition: KSFoundation.h:546
int psd_grd_wait
WF_PULSE * wfpulse
Definition: KSFoundation.h:548
KS_DESCRIPTION description
Definition: KSFoundation.h:1234
int ngenerated
Definition: KSFoundation.h:496
STATUS ks_eval_addwaittogradrfctrl(KS_GRADRFCTRL *gradrfctrl, KS_WAIT *wait)
*Internal use*. Adds a wait pulse to the KS_GRADRFCTRL struct. Used for plotting
Definition: KSFoundation_common.c:4684
int psd_rf_wait
#define KS_THROW(format,...)
Definition: KSFoundation.h:181
void ks_sort_loc_by_timeboard(KS_SEQLOC *a, int nitems)
Sort KS_SEQLOCs in time, then board
Definition: KSFoundation_common.c:4128

◆ 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);
KS_RAISE(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);
KS_RAISE(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
3004  {
3005  STATUS status;
3006  KS_SEQLOC tmploc = KS_INIT_SEQLOC;
3007 
3008  if (! isirot->duration) {
3009  /* must have called ks_eval_isirot() first */
3010  return SUCCESS;
3011  }
3012 
3013  if (rotfun == NULL)
3014  return ks_error("%s: 4th arg (rotfun) cannot be NULL", __FUNCTION__);
3015 
3016 
3017  /* Set the ctrl_word to binary match the isi vector interupt number */
3018  /* /--------------------------------------------------------------\ */
3019  /* | interupt number | ctrl_word | */
3020  /* |--------------------------------------------------------------| */
3021  /* | 4 | PSD_ISI2_BIT | */
3022  /* | 5 | PSD_ISI0_BIT + PSD_ISI2_BIT | */
3023  /* | 6 | PSD_ISI1_BIT + PSD_ISI2_BIT | */
3024  /* | 7 | PSD_ISI0_BIT + PSD_ISI1_BIT + PSD_ISI2_BIT | */
3025  /* \--------------------------------------------------------------/ */
3026 
3027  tmploc.board = SSP;
3028  tmploc.pos = RUP_GRD(pos);
3029 
3030  status = ks_pg_wait(&isirot->waitfun, tmploc, ctrl);
3031  KS_RAISE(status);
3032 
3033  tmploc.pos += RUP_GRD(isirot->waitfun.max_duration);
3034 
3035  status = ks_pg_wait(&isirot->waitrot, tmploc, ctrl);
3036  KS_RAISE(status);
3037 
3038 
3039 #ifdef IPG
3040  {
3041  long ctrl_word = 0;
3042  int placementindx = isirot->waitfun.base.ngenerated - 1;
3043 
3044  /* SSP ISI number */
3045  getctrl(&ctrl_word, &isirot->waitfun.wfpulse[SSP], isirot->waitfun.wfpulse[SSP].ninsts - 1);
3046 
3047  if (isirot->isinumber == 4)
3048  ctrl_word = ctrl_word | PSD_ISI2_BIT;
3049  else if (isirot->isinumber == 5)
3050  ctrl_word = ctrl_word | PSD_ISI0_BIT | PSD_ISI2_BIT;
3051  else if (isirot->isinumber == 6)
3052  ctrl_word = ctrl_word | PSD_ISI1_BIT | PSD_ISI2_BIT;
3053  else if (isirot->isinumber == 7)
3054  ctrl_word = ctrl_word | PSD_ISI0_BIT | PSD_ISI1_BIT | PSD_ISI2_BIT;
3055 
3056  setctrl(ctrl_word, &isirot->waitfun.wfpulse[SSP], isirot->waitfun.wfpulse[SSP].ninsts - 1);
3057 
3058  /* SSP Rotation */
3059  getctrl(&ctrl_word, &isirot->waitrot.wfpulse[SSP], isirot->waitrot.wfpulse[SSP].ninsts - 1);
3060  ctrl_word |= PSD_MTX_UPDT;
3061  setctrl(ctrl_word, &isirot->waitrot.wfpulse[SSP], isirot->waitrot.wfpulse[SSP].ninsts - 1);
3062 
3063  /* Allocate memory for scan info array */
3064  if (isirot->scan_info == NULL) {
3065  isirot->scan_info = (SCAN_INFO *) AllocNode(isirot->numinstances * sizeof(SCAN_INFO));
3066  }
3067 
3068  /* Store the scan info for current ISI, where scan_info.oprot contains the rotation matrix */
3069  isirot->scan_info[placementindx] = scan_info;
3070 
3071  /* Reset num ISI and counter */
3072  isirot->counter = 0; /* make sure it is zero before scan starts */
3073 
3074  /* Connect ths isi number with the function pointer */
3075  isivector((short) isirot->isinumber, rotfun, (short) FALSE);
3076 
3077  }
3078 #else
3079 
3080  if (0) { /* make sure the WAIT pulses have been placed out in temporal order */
3081  int i;
3082  int lastpos = -1;
3083  WF_INSTR_HDR *instr = isirot->waitfun.wfpulse[SSP].inst_hdr_tail;
3084  for (i = 0; i < (isirot->waitfun.base.ninst - 1); i++) {
3085  if (lastpos > instr->start) {
3086  return ks_error("%s: Must place the ISI pulses in temporal order", __FUNCTION__);
3087  } else {
3088  lastpos = instr->start;
3089  instr = instr->next;
3090  }
3091  }
3092  }
3093 
3094  /* copy for convenience */
3095  isirot->numinstances = isirot->waitfun.base.ninst;
3096 
3097 #endif
3098 
3099  return SUCCESS;
3100 
3101 } /* ks_pg_isirot() */
int isinumber
Definition: KSFoundation.h:561
SCAN_INFO * scan_info
Definition: KSFoundation.h:560
int pos
Definition: KSFoundation.h:463
int ninst
Definition: KSFoundation.h:494
STATUS ks_error(const char *format,...)
Definition: KSFoundation_common.c:78
int board
Definition: KSFoundation.h:462
KS_WAIT waitfun
Definition: KSFoundation.h:558
KS_BASE base
Definition: KSFoundation.h:543
SCAN_INFO scan_info[]
int counter
Definition: KSFoundation.h:563
#define KS_RAISE(status)
Definition: KSFoundation.h:190
typedef struct used as argument to ks_pg_*** functions to control where and when to place a sequence ...
Definition: KSFoundation.h:461
int numinstances
Definition: KSFoundation.h:564
KS_WAIT waitrot
Definition: KSFoundation.h:559
int max_duration
Definition: KSFoundation.h:546
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:2889
#define KS_INIT_SEQLOC
Definition: KSFoundation.h:287
WF_PULSE * wfpulse
Definition: KSFoundation.h:548
int duration
Definition: KSFoundation.h:562
int ngenerated
Definition: KSFoundation.h:496

◆ 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
3106  {
3107 
3108  STATUS status;
3109  int readaxis, blipaxis, sliceaxis;
3110  KS_SEQLOC tmploc = KS_INIT_SEQLOC;
3111  int maxduration;
3112 
3113  if (!epi) { return ks_error("Ops"); }
3114 
3115  /* loc.ampscale may only be +1 or -1 and will control the read polarity.
3116  The blip polarity is controlled in run time with ks_scan_epi_shotcontrol() */
3117  if (!areSame(loc.ampscale, 1.0) && !areSame(loc.ampscale, -1.0)) {
3118  return ks_error("ks_pg_epi_dephasers: loc.ampscale must be +1 or -1 (controls read polarity)");
3119  }
3120 
3121  loc.pos = RDN_GRD(loc.pos);
3122 
3123  switch (loc.board) {
3124  case KS_FREQX_PHASEY:
3125  readaxis = XGRAD; blipaxis = YGRAD; sliceaxis = ZGRAD; break;
3126  case KS_FREQY_PHASEX:
3127  readaxis = YGRAD; blipaxis = XGRAD; sliceaxis = ZGRAD; break;
3128  case KS_FREQX_PHASEZ:
3129  readaxis = XGRAD; blipaxis = ZGRAD; sliceaxis = YGRAD; break;
3130  case KS_FREQZ_PHASEX:
3131  readaxis = ZGRAD; blipaxis = XGRAD; sliceaxis = YGRAD; break;
3132  case KS_FREQY_PHASEZ:
3133  readaxis = YGRAD; blipaxis = ZGRAD; sliceaxis = XGRAD; break;
3134  case KS_FREQZ_PHASEY:
3135  readaxis = ZGRAD; blipaxis = YGRAD; sliceaxis = XGRAD; break;
3136  default:
3137  return ks_error("ks_pg_epi_dephasers: loc.board is not valid: %d", loc.board);
3138  }
3139 
3140  /* allocate time corresponding to the longest of the three, but push the dephasers to the right below */
3141  maxduration = IMax(3, epi->readphaser.duration, epi->blipphaser.grad.duration, epi->zphaser.grad.duration);
3142 
3143  /* read phaser (same KS_TRAP for DE/REphasing => 2 instances per EPI) */
3144  if (epi->readphaser.duration > 0) {
3145  tmploc.board = readaxis;
3146  tmploc.ampscale = loc.ampscale;
3147  tmploc.pos = loc.pos + maxduration - epi->readphaser.duration;
3148  status = ks_pg_trap(&epi->readphaser, tmploc, ctrl);
3149  KS_RAISE(status);
3150  }
3151 
3152  /* blip phaser (same KS_PHASER for DE/REphasing => 2 instances per EPI) */
3153  if (epi->blipphaser.grad.duration > 0) {
3154  tmploc.board = blipaxis;
3155  tmploc.ampscale = 1.0;
3156  tmploc.pos = loc.pos + maxduration - epi->blipphaser.grad.duration;
3157  status = ks_pg_phaser(&epi->blipphaser, tmploc, ctrl);
3158  KS_RAISE(status);
3159  }
3160 
3161  /* balancing CAIPI-blip */
3162  if (epi->caipiblip.duration > 0) {
3163  tmploc.board = sliceaxis;
3164  tmploc.pos = loc.pos + maxduration - epi->caipiblip.duration;
3165  tmploc.ampscale = fabs(loc.ampscale);
3166  if (ks_pg_trap(&epi->caipiblip, tmploc, ctrl) == FAILURE)
3167  return FAILURE;
3168  }
3169 
3170  /* slice phaser (same KS_PHASER for DE/REphasing => 2 instances per EPI) */
3171  if (epi->zphaser.grad.duration > 0) {
3172  tmploc.board = sliceaxis;
3173  tmploc.ampscale = 1.0;
3174  tmploc.pos = loc.pos + maxduration - epi->zphaser.grad.duration;
3175  status = ks_pg_phaser(&epi->zphaser, tmploc, ctrl);
3176  KS_RAISE(status);
3177  }
3178 
3179  return SUCCESS;
3180 } /* 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:2031
Definition: KSFoundation.h:2319
#define areSame(a, b)
Definition: KSFoundation.h:144
KS_TRAP grad
Definition: KSFoundation.h:1719
int pos
Definition: KSFoundation.h:463
Definition: KSFoundation.h:2321
KS_PHASER blipphaser
Definition: KSFoundation.h:1937
STATUS ks_error(const char *format,...)
Definition: KSFoundation_common.c:78
int board
Definition: KSFoundation.h:462
KS_PHASER zphaser
Definition: KSFoundation.h:1938
Definition: KSFoundation.h:2324
float ampscale
Definition: KSFoundation.h:464
Definition: KSFoundation.h:2322
Definition: KSFoundation.h:2320
KS_TRAP caipiblip
Definition: KSFoundation.h:1936
#define KS_RAISE(status)
Definition: KSFoundation.h:190
typedef struct used as argument to ks_pg_*** functions to control where and when to place a sequence ...
Definition: KSFoundation.h:461
Definition: KSFoundation.h:2323
KS_TRAP readphaser
Definition: KSFoundation.h:1934
#define KS_INIT_SEQLOC
Definition: KSFoundation.h:287
int duration
Definition: KSFoundation.h:673
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:1802

◆ 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
3184  {
3185 
3186  STATUS status;
3187  int readaxis, blipaxis, sliceaxis;
3188  KS_SEQLOC tmploc = KS_INIT_SEQLOC;
3189 
3190  if (!epi) { return ks_error("Ops"); }
3191 
3192  /* loc.ampscale may only be +1 or -1 and will control the read polarity.
3193  The blip polarity is controlled in run time with ks_scan_epi_shotcontrol() */
3194  if (!areSame(loc.ampscale, 1.0) && !areSame(loc.ampscale, -1.0)) {
3195  return ks_error("ks_pg_epi_rephasers: loc.ampscale must be +1 or -1 (controls read polarity)");
3196  }
3197 
3198  loc.pos = RDN_GRD(loc.pos);
3199 
3200  switch (loc.board) {
3201  case KS_FREQX_PHASEY:
3202  readaxis = XGRAD; blipaxis = YGRAD; sliceaxis = ZGRAD; break;
3203  case KS_FREQY_PHASEX:
3204  readaxis = YGRAD; blipaxis = XGRAD; sliceaxis = ZGRAD; break;
3205  case KS_FREQX_PHASEZ:
3206  readaxis = XGRAD; blipaxis = ZGRAD; sliceaxis = YGRAD; break;
3207  case KS_FREQZ_PHASEX:
3208  readaxis = ZGRAD; blipaxis = XGRAD; sliceaxis = YGRAD; break;
3209  case KS_FREQY_PHASEZ:
3210  readaxis = YGRAD; blipaxis = ZGRAD; sliceaxis = XGRAD; break;
3211  case KS_FREQZ_PHASEY:
3212  readaxis = ZGRAD; blipaxis = YGRAD; sliceaxis = XGRAD; break;
3213  default:
3214  return ks_error("ks_pg_epi_rephasers: loc.board is not valid: %d", loc.board);
3215  }
3216 
3217  /* read phaser (same KS_TRAP for DE/REphasing => 2 instances per EPI) */
3218  if (epi->readphaser.duration > 0) {
3219  tmploc.board = readaxis;
3220  tmploc.pos = loc.pos;
3221  if ((epi->etl % 2 && !(epi->epi_readout_mode == KS_EPI_BIPOLAR)) || (epi->epi_readout_mode == KS_EPI_FLYBACK)) {
3222  /* same polarity of rephaser as dephaser if odd ETL and not epi->splitoddeven, or flyback */
3223  tmploc.ampscale = loc.ampscale;
3224  } else {
3225  tmploc.ampscale = -loc.ampscale;
3226  }
3227  status = ks_pg_trap(&epi->readphaser, tmploc, ctrl);
3228  KS_RAISE(status);
3229  }
3230 
3231  /* blip phaser (same KS_PHASER for DE/REphasing => 2 instances per EPI) */
3232  if (epi->blipphaser.grad.duration > 0) {
3233  tmploc.board = blipaxis;
3234  tmploc.pos = loc.pos;
3235  tmploc.ampscale = 1.0;
3236  status = ks_pg_phaser(&epi->blipphaser, tmploc, ctrl);
3237  KS_RAISE(status);
3238  }
3239 
3240  /* balancing CAIPI-blip */
3241  if (epi->caipiblip.duration > 0) {
3242  tmploc.board = sliceaxis;
3243  tmploc.pos = loc.pos;
3244  tmploc.ampscale = fabs(loc.ampscale);
3245  if (ks_pg_trap(&epi->caipiblip, tmploc, ctrl) == FAILURE)
3246  return FAILURE;
3247  }
3248 
3249  /* slice phaser (same KS_PHASER for DE/REphasing => 2 instances per EPI) */
3250  if (epi->zphaser.grad.duration > 0) {
3251  tmploc.board = sliceaxis;
3252  tmploc.pos = loc.pos;
3253  tmploc.ampscale = 1.0;
3254  status = ks_pg_phaser(&epi->zphaser, tmploc, ctrl);
3255  KS_RAISE(status);
3256  }
3257 
3258  return SUCCESS;
3259 } /* 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:2031
Definition: KSFoundation.h:2319
#define areSame(a, b)
Definition: KSFoundation.h:144
KS_TRAP grad
Definition: KSFoundation.h:1719
int pos
Definition: KSFoundation.h:463
Definition: KSFoundation.h:2321
KS_PHASER blipphaser
Definition: KSFoundation.h:1937
STATUS ks_error(const char *format,...)
Definition: KSFoundation_common.c:78
int board
Definition: KSFoundation.h:462
KS_PHASER zphaser
Definition: KSFoundation.h:1938
Definition: KSFoundation.h:2324
float ampscale
Definition: KSFoundation.h:464
Definition: KSFoundation.h:2322
int etl
Definition: KSFoundation.h:1941
Definition: KSFoundation.h:2320
int epi_readout_mode
Definition: KSFoundation.h:1939
KS_TRAP caipiblip
Definition: KSFoundation.h:1936
#define KS_RAISE(status)
Definition: KSFoundation.h:190
typedef struct used as argument to ks_pg_*** functions to control where and when to place a sequence ...
Definition: KSFoundation.h:461
Definition: KSFoundation.h:2323
KS_TRAP readphaser
Definition: KSFoundation.h:1934
Definition: KSFoundation.h:2329
#define KS_INIT_SEQLOC
Definition: KSFoundation.h:287
Definition: KSFoundation.h:2329
int duration
Definition: KSFoundation.h:673
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:1802

◆ 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
3263  {
3264 
3265  STATUS status;
3266  int readaxis, blipaxis, caipiaxis;
3267  KS_SEQLOC readloc = KS_INIT_SEQLOC;
3268  KS_SEQLOC bliploc = KS_INIT_SEQLOC;
3269  int i, s;
3270 
3271  if (!epi || !epi->read.grad.description) { return ks_error("Ops"); }
3272 
3273  /* loc.ampscale may only be +1 or -1 and will control the read polarity.</
3274  The blip polarity is controlled in run time with ks_scan_epi_shotcontrol() */
3275  if (!areSame(loc.ampscale, 1.0) && !areSame(loc.ampscale, -1.0)) {
3276  return ks_error("ks_pg_epi_echo: loc.ampscale must be +1 or -1 (controls read polarity)");
3277  }
3278 
3279  loc.pos = RUP_GRD(loc.pos);
3280 
3281  readloc.pos = loc.pos;
3282 
3283  switch (loc.board) {
3284  case KS_FREQX_PHASEY:
3285  readaxis = XGRAD; blipaxis = YGRAD; caipiaxis = ZGRAD; break;
3286  case KS_FREQY_PHASEX:
3287  readaxis = YGRAD; blipaxis = XGRAD; caipiaxis = ZGRAD; break;
3288  case KS_FREQX_PHASEZ:
3289  readaxis = XGRAD; blipaxis = ZGRAD; caipiaxis = YGRAD; break;
3290  case KS_FREQZ_PHASEX:
3291  readaxis = ZGRAD; blipaxis = XGRAD; caipiaxis = YGRAD; break;
3292  case KS_FREQY_PHASEZ:
3293  readaxis = YGRAD; blipaxis = ZGRAD; caipiaxis = XGRAD; break;
3294  case KS_FREQZ_PHASEY:
3295  readaxis = ZGRAD; blipaxis = YGRAD; caipiaxis = XGRAD; break;
3296  default:
3297  return ks_error("ks_pg_epi_echo: field 'board' in 2nd arg is not valid: %d", loc.board);
3298  }
3299 
3300  if (!areSame(fabs(loc.ampscale), 1.0)) {
3301  return ks_error("ks_pg_epi_echo: field 'ampscale' in 2nd arg must be +1 or -1 (controls read lobe polarity)");
3302  }
3303 
3304  /* read trapezoid */
3305  readloc.board = readaxis;
3306  for (i = 0; i < epi->etl; i++) {
3307  if (epi->epi_readout_mode == KS_EPI_FLYBACK){
3308  status = ks_pg_readtrap(&epi->read, readloc, ctrl);
3309  KS_RAISE(status);
3310 
3311  readloc.pos += RUP_GRD(epi->read.grad.duration);
3312 
3313  /* No flyback needed for the last echo */
3314  if (i < epi->etl - 1){
3315  readloc.ampscale *= -1;
3316  status = ks_pg_trap(&epi->read_flyback, readloc, ctrl);
3317  KS_RAISE(status);
3318 
3319  readloc.ampscale *= -1;
3320  readloc.pos += RUP_GRD(epi->read_flyback.duration + epi->read_spacing);
3321  }
3322 
3323  } else {
3324  int n_readouts_per_blip = (epi->epi_readout_mode == KS_EPI_SPLITODDEVEN ? 2 : 1);
3325  for (s = 0; s < n_readouts_per_blip; s++) { /* epi->splitoddeven can be 0 or 1 */
3326 
3327  if (epi->epi_readout_mode == KS_EPI_SPLITODDEVEN) {
3328  readloc.ampscale = (s % 2) ? -loc.ampscale : loc.ampscale; /* +1 or -1 */
3329  } else {
3330  readloc.ampscale = (i % 2) ? -loc.ampscale : loc.ampscale; /* +1 or -1 */
3331  }
3332  status = ks_pg_readtrap(&epi->read, readloc, ctrl);
3333  KS_RAISE(status);
3334 
3335  /* read_spacing is additional gap between lobes (normally 0) */
3336  readloc.pos += RUP_GRD(epi->read.grad.duration + epi->read_spacing);
3337 
3338  } /* for s */
3339  }
3340 
3341  /* phase encoding blip */
3342  if (i < epi->etl - 1) {
3343  /* etl-1, since no blip after the last read lobe */
3344  bliploc.board = blipaxis;
3345  bliploc.pos = RUP_GRD(readloc.pos - epi->blip.duration / 2 - epi->read_spacing / 2);
3346  bliploc.ampscale = fabs(loc.ampscale);
3347  status = ks_pg_trap(&epi->blip, bliploc, ctrl);
3348  KS_RAISE(status);
3349 
3350  bliploc.board = caipiaxis;
3351  bliploc.pos = RUP_GRD(readloc.pos - epi->caipiblip.duration / 2 + epi->read_spacing / 2);
3352  status = ks_pg_trap(&epi->caipiblip, bliploc, ctrl);
3353  KS_RAISE(status);
3354  }
3355 
3356  } /* etl */
3357 
3358 
3359  return SUCCESS;
3360 
3361 } /* ks_pg_epi_echo */
KS_TRAP blip
Definition: KSFoundation.h:1935
Definition: KSFoundation.h:2319
KS_TRAP read_flyback
Definition: KSFoundation.h:1933
#define areSame(a, b)
Definition: KSFoundation.h:144
int pos
Definition: KSFoundation.h:463
Definition: KSFoundation.h:2321
STATUS ks_error(const char *format,...)
Definition: KSFoundation_common.c:78
int board
Definition: KSFoundation.h:462
Definition: KSFoundation.h:2324
KS_TRAP grad
Definition: KSFoundation.h:1561
float ampscale
Definition: KSFoundation.h:464
Definition: KSFoundation.h:2322
int etl
Definition: KSFoundation.h:1941
KS_READTRAP read
Definition: KSFoundation.h:1932
Definition: KSFoundation.h:2329
Definition: KSFoundation.h:2320
int epi_readout_mode
Definition: KSFoundation.h:1939
KS_TRAP caipiblip
Definition: KSFoundation.h:1936
#define KS_RAISE(status)
Definition: KSFoundation.h:190
typedef struct used as argument to ks_pg_*** functions to control where and when to place a sequence ...
Definition: KSFoundation.h:461
KS_DESCRIPTION description
Definition: KSFoundation.h:668
Definition: KSFoundation.h:2323
#define KS_INIT_SEQLOC
Definition: KSFoundation.h:287
int read_spacing
Definition: KSFoundation.h:1942
Definition: KSFoundation.h:2329
int duration
Definition: KSFoundation.h:673
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:2161
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:1802

◆ 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
3365  {
3366 
3367  STATUS status;
3368  KS_SEQLOC tmploc = loc;
3369  const int readfact = (epi->epi_readout_mode == KS_EPI_SPLITODDEVEN) ? 2 : 1;
3370 
3371  /* loc.ampscale may only be +1 or -1 and will control the read polarity.
3372  The blip polarity is controlled in run time with ks_scan_epi_shotcontrol() */
3373  if (!areSame(loc.ampscale, 1.0) && !areSame(loc.ampscale, -1.0)) {
3374  return ks_error("ks_pg_epi: loc.ampscale must be +1 or -1 (controls read polarity)");
3375  }
3376 
3377  status = ks_pg_epi_dephasers(epi, tmploc, ctrl);
3378  KS_RAISE(status);
3379 
3380  tmploc.pos += IMax(3, epi->readphaser.duration, epi->blipphaser.grad.duration, epi->zphaser.grad.duration);
3381 
3382  /* main epi train (w/o DE/REphasers) */
3383  status = ks_pg_epi_echo(epi, tmploc, ctrl);
3384  KS_RAISE(status);
3385 
3386  tmploc.pos += (epi->etl * readfact) * epi->read.grad.duration + ((epi->etl * readfact) - 1) * (epi->read_flyback.duration + epi->read_spacing);
3387 
3388  status = ks_pg_epi_rephasers(epi, tmploc, ctrl);
3389  KS_RAISE(status);
3390 
3391  return SUCCESS;
3392 } /* ks_pg_epi */
KS_TRAP read_flyback
Definition: KSFoundation.h:1933
#define areSame(a, b)
Definition: KSFoundation.h:144
KS_TRAP grad
Definition: KSFoundation.h:1719
int pos
Definition: KSFoundation.h:463
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:3263
KS_PHASER blipphaser
Definition: KSFoundation.h:1937
STATUS ks_error(const char *format,...)
Definition: KSFoundation_common.c:78
KS_PHASER zphaser
Definition: KSFoundation.h:1938
KS_TRAP grad
Definition: KSFoundation.h:1561
float ampscale
Definition: KSFoundation.h:464
int etl
Definition: KSFoundation.h:1941
KS_READTRAP read
Definition: KSFoundation.h:1932
Definition: KSFoundation.h:2329
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:3106
int epi_readout_mode
Definition: KSFoundation.h:1939
#define KS_RAISE(status)
Definition: KSFoundation.h:190
typedef struct used as argument to ks_pg_*** functions to control where and when to place a sequence ...
Definition: KSFoundation.h:461
KS_TRAP readphaser
Definition: KSFoundation.h:1934
int read_spacing
Definition: KSFoundation.h:1942
int duration
Definition: KSFoundation.h:673
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:3184

◆ ks_pg_get_trap_loc()

KS_SEQLOC ks_pg_get_trap_loc ( KS_TRAP trap,
int  instance 
)

Returns the location of a trap

This function should be called in the @pg section to find the SEQLOC of a PGed trap. If instance = -1 it returns the last SEQLOC of the last instance.

Parameters
[in]trap
[in]instance
Return values
KS_SEQLOC
3462  {
3463 
3464  if (ks_numplaced(&trap->base) <= instance || instance < -1) {
3465  KS_THROW("%s: instance (%d) out of range [-1-%d)", __FUNCTION__, instance, trap->base.ngenerated);
3466  KS_SEQLOC tmploc = KS_INIT_SEQLOC;
3467  return tmploc;
3468  }
3469 
3470  instance = (instance == -1) ? ks_numplaced(&trap->base) - 1 : instance;
3471 
3472 #ifdef HOST_TGT
3473  /* HOST */
3474  return trap->locs[instance];
3475 #else
3476  /* TARGET */
3477  return trap->wfi[instance].loc;
3478 #endif
3479 }
KS_BASE base
Definition: KSFoundation.h:667
KS_SEQLOC * locs
Definition: KSFoundation.h:678
int ks_numplaced(KS_BASE *base)
ADDTITLEHERE
Definition: KSFoundation_common.c:4838
KS_WFINSTANCE * wfi
Definition: KSFoundation.h:680
KS_SEQLOC loc
Definition: KSFoundation.h:486
typedef struct used as argument to ks_pg_*** functions to control where and when to place a sequence ...
Definition: KSFoundation.h:461
#define KS_INIT_SEQLOC
Definition: KSFoundation.h:287
int ngenerated
Definition: KSFoundation.h:496
#define KS_THROW(format,...)
Definition: KSFoundation.h:181

◆ ks_pg_get_wave_loc()

KS_SEQLOC ks_pg_get_wave_loc ( KS_WAVE wave,
int  instance 
)

Returns the location of a wave

This function should be called in the @pg section to find the SEQLOC of a PGed wave. If instance = -1 it returns the last SEQLOC of the last instance.

Parameters
[in]wave
[in]instance
Return values
KS_SEQLOC
3484  {
3485 
3486  if (ks_numplaced(&wave->base) <= instance || instance < -1) {
3487  KS_THROW("%s: instance (%d) out of range [-1-%d)", __FUNCTION__, instance, wave->base.ngenerated);
3488  KS_SEQLOC tmploc = KS_INIT_SEQLOC;
3489  return tmploc;
3490  }
3491 
3492  instance = (instance == -1) ? ks_numplaced(&wave->base) - 1 : instance;
3493 
3494 #ifdef HOST_TGT
3495  /* HOST */
3496  return wave->locs[instance];
3497 #else
3498  /* TARGET */
3499  return wave->wfi[instance].loc;
3500 #endif
3501 }
KS_SEQLOC * locs
Definition: KSFoundation.h:752
int ks_numplaced(KS_BASE *base)
ADDTITLEHERE
Definition: KSFoundation_common.c:4838
KS_WFINSTANCE * wfi
Definition: KSFoundation.h:754
KS_SEQLOC loc
Definition: KSFoundation.h:486
KS_BASE base
Definition: KSFoundation.h:744
typedef struct used as argument to ks_pg_*** functions to control where and when to place a sequence ...
Definition: KSFoundation.h:461
#define KS_INIT_SEQLOC
Definition: KSFoundation.h:287
int ngenerated
Definition: KSFoundation.h:496
#define KS_THROW(format,...)
Definition: KSFoundation.h:181

◆ ks_mat4_zero()

void ks_mat4_zero ( KS_MAT4x4  m)

Makes a 4x4 zero matrix

Parameters
[in,out]mMatrix (KS_MAT4x4)
Returns
void
3624  {
3625  int i;
3626  for (i = 0; i < 16; i++) {
3627  m[i] = 0.0;
3628  }
3629 }

◆ ks_mat4_identity()

void ks_mat4_identity ( KS_MAT4x4  m)

Creates a 4x4 identity matrix

Parameters
[in,out]mMatrix (KS_MAT4x4)
Returns
void
3635  {
3636  int i;
3637  ks_mat4_zero(m);
3638  for (i = 0; i < 4; i++) {
3639  m[i + (i * 4)] = 1.0;
3640  }
3641 }
void ks_mat4_zero(KS_MAT4x4 m)
Makes a 4x4 zero matrix
Definition: KSFoundation_common.c:3624

◆ ks_mat4_print()

void ks_mat4_print ( const KS_MAT4x4  m)

Prints a 4x4 matrix to stdout

Parameters
[in]mMatrix (KS_MAT4x4)
Returns
void
3646  {
3647  int i;
3648  for (i = 0; i < 4; i++) {
3649  printf("| %0.4f %0.4f %0.4f %0.4f |\n", m[i], m[i + 4], m[i + 8], m[i + 12]);
3650  }
3651  fflush(stdout);
3652 }

◆ 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
3658  {
3659 
3660  /* Define local variables */
3661  int m, n, k;
3662  int msize = 4;
3663  KS_MAT4x4 Mtmp;
3664  double *ptr;
3665 
3666  if (lhs == rhs_left || lhs == rhs_right) { /* Check if output is equal to any input */
3667  ptr = Mtmp;
3668  } else {
3669  ptr = lhs;
3670  }
3671  for (k = 0; k < (msize * msize); k++) { /* Zero left-hand side*/
3672  ptr[k] = 0.0;
3673  }
3674 
3675  /* Multiply the matrices*/
3676  for (n = 0; n < msize; n++) {
3677  for (m = 0; m < msize; m++) {
3678  for (k = 0; k < msize; k++) {
3679  ptr[m + (n * msize)] += rhs_left[m + (k * msize)] * rhs_right[k + (n * msize)];
3680  }
3681  }
3682  }
3683 
3684  if (lhs == rhs_left || lhs == rhs_right ) { /* Write back (if needed) */
3685  for (k = 0; k < (msize * msize); k++) {
3686  lhs[k] = ptr[k];
3687  }
3688  }
3689 }
double KS_MAT4x4[16]
Definition: KSFoundation.h:356

◆ 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
3695  {
3696  double tmp[12]; /* temp array for pairs */
3697  double src[16]; /* array of transpose source rhsrix */
3698  double det; /* determinant */
3699  int i, j;
3700 
3701  /* transpose rhsrix */
3702  for (i = 0; i < 4; i++) {
3703  src[i] = rhs[i * 4];
3704  src[i + 4] = rhs[i * 4 + 1];
3705  src[i + 8] = rhs[i * 4 + 2];
3706  src[i + 12] = rhs[i * 4 + 3];
3707  }
3708 
3709  /* calculate pairs for first 8 elements (cofactors) */
3710  tmp[0] = src[10] * src[15];
3711  tmp[1] = src[11] * src[14];
3712  tmp[2] = src[9] * src[15];
3713  tmp[3] = src[11] * src[13];
3714  tmp[4] = src[9] * src[14];
3715  tmp[5] = src[10] * src[13];
3716  tmp[6] = src[8] * src[15];
3717  tmp[7] = src[11] * src[12];
3718  tmp[8] = src[8] * src[14];
3719  tmp[9] = src[10] * src[12];
3720  tmp[10] = src[8] * src[13];
3721  tmp[11] = src[9] * src[12];
3722 
3723  /* calculate first 8 elements (cofactors) */
3724  lhs[0] = tmp[0] * src[5] + tmp[3] * src[6] + tmp[4] * src[7];
3725  lhs[0] -= tmp[1] * src[5] + tmp[2] * src[6] + tmp[5] * src[7];
3726  lhs[1] = tmp[1] * src[4] + tmp[6] * src[6] + tmp[9] * src[7];
3727  lhs[1] -= tmp[0] * src[4] + tmp[7] * src[6] + tmp[8] * src[7];
3728  lhs[2] = tmp[2] * src[4] + tmp[7] * src[5] + tmp[10] * src[7];
3729  lhs[2] -= tmp[3] * src[4] + tmp[6] * src[5] + tmp[11] * src[7];
3730  lhs[3] = tmp[5] * src[4] + tmp[8] * src[5] + tmp[11] * src[6];
3731  lhs[3] -= tmp[4] * src[4] + tmp[9] * src[5] + tmp[10] * src[6];
3732  lhs[4] = tmp[1] * src[1] + tmp[2] * src[2] + tmp[5] * src[3];
3733  lhs[4] -= tmp[0] * src[1] + tmp[3] * src[2] + tmp[4] * src[3];
3734  lhs[5] = tmp[0] * src[0] + tmp[7] * src[2] + tmp[8] * src[3];
3735  lhs[5] -= tmp[1] * src[0] + tmp[6] * src[2] + tmp[9] * src[3];
3736  lhs[6] = tmp[3] * src[0] + tmp[6] * src[1] + tmp[11] * src[3];
3737  lhs[6] -= tmp[2] * src[0] + tmp[7] * src[1] + tmp[10] * src[3];
3738  lhs[7] = tmp[4] * src[0] + tmp[9] * src[1] + tmp[10] * src[2];
3739  lhs[7] -= tmp[5] * src[0] + tmp[8] * src[1] + tmp[11] * src[2];
3740 
3741  /* calculate pairs for second 8 elements (cofactors) */
3742  tmp[0] = src[2] * src[7];
3743  tmp[1] = src[3] * src[6];
3744  tmp[2] = src[1] * src[7];
3745  tmp[3] = src[3] * src[5];
3746  tmp[4] = src[1] * src[6];
3747  tmp[5] = src[2] * src[5];
3748  tmp[6] = src[0] * src[7];
3749  tmp[7] = src[3] * src[4];
3750  tmp[8] = src[0] * src[6];
3751  tmp[9] = src[2] * src[4];
3752  tmp[10] = src[0] * src[5];
3753  tmp[11] = src[1] * src[4];
3754 
3755  /* calculate second 8 elements (cofactors) */
3756  lhs[8] = tmp[0] * src[13] + tmp[3] * src[14] + tmp[4] * src[15];
3757  lhs[8] -= tmp[1] * src[13] + tmp[2] * src[14] + tmp[5] * src[15];
3758  lhs[9] = tmp[1] * src[12] + tmp[6] * src[14] + tmp[9] * src[15];
3759  lhs[9] -= tmp[0] * src[12] + tmp[7] * src[14] + tmp[8] * src[15];
3760  lhs[10] = tmp[2] * src[12] + tmp[7] * src[13] + tmp[10] * src[15];
3761  lhs[10] -= tmp[3] * src[12] + tmp[6] * src[13] + tmp[11] * src[15];
3762  lhs[11] = tmp[5] * src[12] + tmp[8] * src[13] + tmp[11] * src[14];
3763  lhs[11] -= tmp[4] * src[12] + tmp[9] * src[13] + tmp[10] * src[14];
3764  lhs[12] = tmp[2] * src[10] + tmp[5] * src[11] + tmp[1] * src[9];
3765  lhs[12] -= tmp[4] * src[11] + tmp[0] * src[9] + tmp[3] * src[10];
3766  lhs[13] = tmp[8] * src[11] + tmp[0] * src[8] + tmp[7] * src[10];
3767  lhs[13] -= tmp[6] * src[10] + tmp[9] * src[11] + tmp[1] * src[8];
3768  lhs[14] = tmp[6] * src[9] + tmp[11] * src[11] + tmp[3] * src[8];
3769  lhs[14] -= tmp[10] * src[11] + tmp[2] * src[8] + tmp[7] * src[9];
3770  lhs[15] = tmp[10] * src[10] + tmp[4] * src[8] + tmp[9] * src[9];
3771  lhs[15] -= tmp[8] * src[9] + tmp[11] * src[10] + tmp[5] * src[8];
3772 
3773  /* calculate determinant */
3774  det = src[0] * lhs[0] + src[1] * lhs[1] + src[2] * lhs[2] + src[3] * lhs[3];
3775 
3776  /* calculate rhsrix inverse */
3777  det = 1 / det;
3778  for (j = 0; j < 16; j++)
3779  lhs[j] *= det;
3780 }

◆ ks_mat4_double_to_float()

void ks_mat4_double_to_float ( KS_MAT4x4f  out,
const KS_MAT4x4  in 
)

Conversion of a 4x4 matrix from double to float

Parameters
[out]outFloat matrix (KS_MAT4x4f)
[in]inDouble matrix (KS_MAT4x4)
Returns
void
3785  {
3786  int n = 0;
3787  for (; n < 16; n++) {
3788  out[n] = (float) in[n];
3789  }
3790 } /* ks_mat_double_to_float */

◆ ks_mat4_float_to_double()

void ks_mat4_float_to_double ( KS_MAT4x4  out,
const KS_MAT4x4f  in 
)

Conversion of a 4x4 matrix from float to double

Parameters
[out]outDouble matrix (KS_MAT4x4)
[in]inFloat matrix (KS_MAT4x4f)
Returns
void
3793  {
3794  int n = 0;
3795  for (; n < 16; n++) {
3796  out[n] = (double) in[n];
3797  }
3798 } /* ks_mat_float_to_double */

◆ 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
3800  {
3801  double cosa;
3802  double sina;
3803  double cosb;
3804  double sinb;
3805  double cosg;
3806  double sing;
3807 
3808  ks_mat4_identity(lhs);
3809 
3810  xr *= PI / 180.0;
3811  yr *= PI / 180.0;
3812  zr *= PI / 180.0;
3813 
3814  /*
3815  Rotation matrices for positive counterclockwise angles about the positive
3816  coordinate axis for a right handed coordinate system:
3817 
3818  | 1 0 0 |
3819  R1(xr) = | 0 cos(xr) -sin(xr)|
3820  | 0 sin(xr) cos(xr)|
3821 
3822 
3823  | cos(yr) 0 sin(yr) |
3824  R2(yr) = | 0 1 0 |
3825  | -sin(yr) 0 cos(yr) |
3826 
3827  | cos(zr) -sin(zr) 0 |
3828  R3(zr) = | sin(zr) cos(zr) 0 |
3829  | 0 0 1 |
3830  */
3831 
3832  cosa = cos(xr);
3833  sina = sin(xr);
3834  cosb = cos(yr);
3835  sinb = sin(yr);
3836  cosg = cos(zr);
3837  sing = sin(zr);
3838 
3839  /* Rotations: R_z * R_y * R_x */
3840  lhs[ 0] = cosb * cosg;
3841  lhs[ 1] = sina * sinb * cosg - cosa * sing;
3842  lhs[ 2] = cosa * sinb * cosg + sina * sing;
3843  lhs[ 4] = cosb * sing;
3844  lhs[ 5] = sina * sinb * sing + cosa * cosg;
3845  lhs[ 6] = cosa * sinb * sing - sina * cosg;
3846  lhs[ 8] = -sinb;
3847  lhs[ 9] = sina * cosb;
3848  lhs[10] = cosa * cosb;
3849 
3850  /* Translations */
3851  lhs[ 3] = x;
3852  lhs[ 7] = y;
3853  lhs[11] = z;
3854 } /* ks_mat4_setgeometry */
void ks_mat4_identity(KS_MAT4x4 m)
Creates a 4x4 identity matrix
Definition: KSFoundation_common.c:3635

◆ ks_mat4f_setgeometry()

void ks_mat4f_setgeometry ( KS_MAT4x4f  lhs,
float  x,
float  y,
float  z,
float  xr,
float  yr,
float  zr 
)
3856  {
3858  ks_mat4_setgeometry(tmp, x, y, z, xr, yr, zr);
3859  ks_mat4_double_to_float(lhs, tmp);
3860 } /* ks_mat4f_setgeometry */
void ks_mat4_double_to_float(KS_MAT4x4f out, const KS_MAT4x4 in)
Conversion of a 4x4 matrix from double to float
Definition: KSFoundation_common.c:3785
double KS_MAT4x4[16]
Definition: KSFoundation.h:356
#define KS_MAT4x4_IDENTITY
Definition: KSFoundation.h:340
void ks_mat4_setgeometry(KS_MAT4x4 lhs, float x, float y, float z, float xr, float yr, float zr)
Set geometry for KS_MAT4x4
Definition: KSFoundation_common.c:3800

◆ 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
3863  {
3864  ks_mat4_identity(rhs);
3865 
3866  rot *= PI / 180.0; /* deg->rad */
3867 
3868  switch (axis) {
3869  case 'x':
3870  rhs[5] = cos(rot);
3871  rhs[6] = sin(rot);
3872  rhs[9] = -sin(rot);
3873  rhs[10] = cos(rot);
3874  break;
3875  case 'y':
3876  rhs[0] = cos(rot);
3877  rhs[2] = sin(rot);
3878  rhs[8] = -sin(rot);
3879  rhs[10] = cos(rot);
3880  break;
3881  case 'z':
3882  rhs[0] = cos(rot);
3883  rhs[1] = sin(rot);
3884  rhs[4] = -sin(rot);
3885  rhs[5] = cos(rot);
3886  break;
3887  }
3888 
3889 }
void ks_mat4_identity(KS_MAT4x4 m)
Creates a 4x4 identity matrix
Definition: KSFoundation_common.c:3635

◆ 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
3894  {
3895  int i, j;
3896  for (i = 0; i < 3; ++i) {
3897  for (j = 0; j < 3; ++j) {
3898  R[3 * i + j] = M[4 * i + j];
3899  }
3900  }
3901 }

◆ 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
3906  {
3907  int i;
3908  for (i = 0; i < 3; ++i) {
3909  T[i] = M[4 * i + 3];
3910  }
3911 }

◆ ks_mat3_identity()

void ks_mat3_identity ( KS_MAT3x3  m)

Creates a 3x3 identity matrix

Parameters
[in,out]mMatrix (KS_MAT3x3)
Returns
void
3916  {
3917  int i;
3918  for (i = 0; i < 9; ++i) {
3919  m[i] = 0;
3920  }
3921  m[0] = 1;
3922  m[4] = 1;
3923  m[8] = 1;
3924 }

◆ 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
3929  {
3930 
3931  /* TODO: add checks for overlapping arrays */
3932 
3933  int i, j, k;
3934  for (i = 0; i < 3; ++i) {
3935  for (j = 0; j < 3; ++j) {
3936  double acc = 0;
3937  for (k = 0; k < 3; ++k) {
3938  acc += rhs_left[3 * i + k] * rhs_right[3 * k + j];
3939  }
3940  lhs[3 * i + j] = acc;
3941  }
3942  }
3943 }

◆ ks_mat3_print()

void ks_mat3_print ( const KS_MAT3x3  m)

Prints a 3x3 matrix to stdout

Parameters
[in]mMatrix (KS_MAT3x3)
Returns
void
3948  {
3949  int i;
3950  for (i = 0; i < 3; ++i) {
3951  fprintf(stderr, "|%.2f,\t%.2f\t%.2f|\n", m[3 * i], m[3 * i + 1], m[3 * i + 2]);
3952  }
3953  fprintf(stderr, "\n");
3954 }

◆ 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
3959  {
3960  int i, j;
3961  for (i = 0; i < 3; ++i) {
3962  double acc = 0;
3963  for (j = 0; j < 3; ++j) {
3964  acc += R[3 * i + j] * v[j];
3965  }
3966  w[i] = acc;
3967  }
3968 }

◆ ks_mat3f_apply()

void ks_mat3f_apply ( float *  w,
const KS_MAT3x3f  R,
const float *  v 
)

Rotate a vector with a 3x3 matrix

Parameters
[in]wcolumn vector (3)
[in]Rrotation matrix
vresult of R.dot(v)
Returns
void
3987  {
3988  int i, j;
3989  for (i = 0; i < 3; ++i) {
3990  double acc = 0;
3991  for (j = 0; j < 3; ++j) {
3992  acc += R[3 * i + j] * (double)v[j];
3993  }
3994  w[i] = acc;
3995  }
3996 }

◆ 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
3973  {
3974  int i, j;
3975  for (i = 0; i < 3; ++i) {
3976  double acc = 0;
3977  for (j = 0; j < 3; ++j) {
3978  acc += v[j] * R[3 * j + i];
3979  }
3980  w[i] = acc;
3981  }
3982 }

◆ ks_mat3f_invapply()

void ks_mat3f_invapply ( float *  w,
const KS_MAT3x3f  R,
const float *  v 
)

Same as ks_mat3f_apply but use transpose of Rotation (R)

Parameters
[in]wcolumn vector (3)
[in]Rrotation matrix
vresult of R.dot(v)
Returns
void
4001  {
4002  int i, j;
4003  for (i = 0; i < 3; ++i) {
4004  double acc = 0;
4005  for (j = 0; j < 3; ++j) {
4006  acc += v[j] * (double)R[3 * j + i];
4007  }
4008  w[i] = acc;
4009  }
4010 }

◆ 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
4015  {
4016  int i;
4017  KS_MAT3x3 R_m;
4018  KS_MAT3x3 R_l;
4019  KS_MAT3x3 R_ms;
4020  KS_MAT3x3 R_msl;
4021 
4022  const double T_s[3] = {orig_loc.oprloc, orig_loc.opphasoff, orig_loc.optloc};
4023  double T_m[3];
4024  double T_l[3];
4025  double T_msl[3];
4026  double T_new[3];
4027 
4028  /* initialize to identity matrix (will be used if NULL in 3rd or 4th arg) */
4029  KS_MAT4x4 Mphys = KS_MAT4x4_IDENTITY;
4031 
4032  /* copy slice rotation and translation */
4033  KS_MAT3x3 R_s;
4034  for (i = 0; i < 9; i++) {
4035  R_s[i] = orig_loc.oprot[i];
4036  }
4037 
4038  /* copy transformation matrices (if they are not NULL) to local matrices */
4039  if (M_physical != NULL) {
4040  memcpy(Mphys, M_physical, sizeof(KS_MAT4x4));
4041  }
4042  if (M_logical != NULL) {
4043  memcpy(Mlog, M_logical, sizeof(KS_MAT4x4));
4044  }
4045 
4046  ks_mat4_extractrotation(R_m, Mphys);
4047  ks_mat4_extractrotation(R_l, Mlog);
4048 
4049  ks_mat4_extracttranslation(T_m, Mphys);
4050  ks_mat4_extracttranslation(T_l, Mlog);
4051 
4052  /* R_ms = R_m * R_s */
4053  ks_mat3_multiply(R_ms, R_m, R_s);
4054 
4055  /* R_ms^-1 * T_m = T_m' * R_ms' */
4056  ks_mat3_invapply(T_msl, R_ms, T_m);
4057 
4058  for (i = 0; i < 3; ++i) {
4059  T_msl[i] += T_s[i] + T_l[i];
4060  }
4061 
4062  ks_mat3_invapply(T_new, R_l, T_msl);
4063  new_loc->oprloc = T_new[0];
4064  new_loc->opphasoff = T_new[1];
4065  new_loc->optloc = T_new[2];
4066 
4067  ks_mat3_multiply(R_msl, R_ms, R_l);
4068  for (i=0; i<9; ++i) {
4069  new_loc->oprot[i] = R_msl[i];
4070  }
4071 } /* 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:3973
double KS_MAT4x4[16]
Definition: KSFoundation.h:356
double KS_MAT3x3[9]
Definition: KSFoundation.h:354
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:3894
#define KS_MAT4x4_IDENTITY
Definition: KSFoundation.h:340
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:3906
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:3929

◆ ks_scan_update_slice_location_float()

void ks_scan_update_slice_location_float ( SCAN_INFO *  new_loc,
const SCAN_INFO  orig_loc,
const KS_MAT4x4f  M_physical,
const KS_MAT4x4f  M_logical 
)
4074  {
4075  KS_MAT4x4 Mphysical = KS_MAT4x4_IDENTITY;
4076  KS_MAT4x4 Mlogical = KS_MAT4x4_IDENTITY;
4077  ks_mat4_float_to_double(Mphysical, M_physical);
4078  ks_mat4_float_to_double(Mlogical, M_logical);
4079  ks_scan_update_slice_location(new_loc, orig_loc, Mphysical, Mlogical);
4080 } /* ks_scan_update_slice_location_float */
void ks_mat4_float_to_double(KS_MAT4x4 out, const KS_MAT4x4f in)
Conversion of a 4x4 matrix from float to double
Definition: KSFoundation_common.c:3793
double KS_MAT4x4[16]
Definition: KSFoundation.h:356
#define KS_MAT4x4_IDENTITY
Definition: KSFoundation.h:340
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
Definition: KSFoundation_common.c:4015

◆ ks_scan_rotate()

void ks_scan_rotate ( SCAN_INFO  slice_pos)

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_posSCAN_INFO struct holding new slice information
Returns
void
1179  {
1180 #ifdef IPG
1181 
1182  /* scale to long int rotation matrix (accounting for gradient scaling) */
1183  scale(&slice_pos.oprot, &rsprot[0], 1, &loggrd, &phygrd, 0);
1184 
1185  setrotatearray(1, rsprot[0]);
1186 
1187 #endif
1188 }
PHYS_GRAD phygrd
LOG_GRAD loggrd
long rsprot[TRIG_ROT_MAX][9]
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
1193  {
1194 #ifdef IPG
1195 
1196  if (isirot == NULL || isirot->numinstances < 1)
1197  return;
1198 
1199  /* wrap around after numinstances interupts */
1200  isirot->counter = isirot->counter % isirot->numinstances;
1201 
1202  /* scale to long int rotation matrix (accounting for gradient scaling) */
1203  scale(&isirot->scan_info[isirot->counter++].oprot, &rsprot[0], 1, &loggrd, &phygrd, 0);
1204 
1205  /* rotate on SSP interrupt */
1206  setrotateimm(rsprot[0], WARP_UPDATE_ON_SSP_INT);
1207 
1208 #endif
1209 }
SCAN_INFO * scan_info
Definition: KSFoundation.h:560
PHYS_GRAD phygrd
LOG_GRAD loggrd
long rsprot[TRIG_ROT_MAX][9]
int counter
Definition: KSFoundation.h:563
STATUS scale(FLOAT(*inrotmat)[9], long(*outrotmat)[9], INT slquant, LOG_GRAD *lgrad, PHYS_GRAD *pgrad, INT contdebug)
int numinstances
Definition: KSFoundation.h:564

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

ADDTITLEHERE

3434  {
3435 
3436  int i;
3437 
3438  flip_angles[0] = flip1;
3439  flip_angles[1] = flip2;
3440  for (i = 2; i < etl; i++) {
3441  flip_angles[i] = flip3;
3442  }
3443 
3444  if (flip3 <= target_flip || etl < 5) {
3445  /* flip3 must be larger than target_flip and etl must be larger than 4 */
3446  return SUCCESS;
3447  }
3448 
3449  /* after half of the echo train or third pulse, ramp down flip angles to last_flip */
3450  double first_flip = flip3;
3451  double last_flip = target_flip;
3452  int start_pulse = start_middle ? etl/2 : 2;
3453  for (i = start_pulse; i < etl; i++) {
3454  flip_angles[i] = first_flip + (((last_flip - first_flip)/(etl - start_pulse - 1)) * (i - start_pulse));
3455  }
3456 
3457  return SUCCESS;
3458 
3459 } /* 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 
)

ADDTITLEHERE

3399  {
3400 
3401  float SAR_scale_1 = 0.0;
3402  float SAR_scale_2 = 0.0;
3403  float SAR_scale_3 = 0.0;
3404  int i;
3405 
3406  SAR_scale_1 = pow(flip_angles[0] / rf1->rf.flip, 2.0);
3407  SAR_scale_2 = pow(flip_angles[1] / rf2->rf.flip, 2.0);
3408  for (i = 2; i < etl; i++) {
3409  SAR_scale_3 += pow(flip_angles[i] / rf3->rf.flip, 2.0);
3410  }
3411  SAR_scale_3 /= etl - 2;
3412 
3413  rf1->rf.rfpulse.effwidth *= SAR_scale_1;
3414  rf1->rf.rfpulse.max_rms_b1 *= sqrt(SAR_scale_1);
3415  rf1->rf.rfpulse.abswidth *= sqrt(SAR_scale_1);
3416 
3417  rf2->rf.rfpulse.effwidth *= SAR_scale_2;
3418  rf2->rf.rfpulse.max_rms_b1 *= sqrt(SAR_scale_2);
3419  rf2->rf.rfpulse.abswidth *= sqrt(SAR_scale_2);
3420 
3421  rf3->rf.rfpulse.effwidth *= SAR_scale_3;
3422  rf3->rf.rfpulse.max_rms_b1 *= sqrt(SAR_scale_3);
3423  rf3->rf.rfpulse.abswidth *= sqrt(SAR_scale_3);
3424 
3425 } /* ks_pg_mod_fse_rfpulse_structs */
RF_PULSE rfpulse
Definition: KSFoundation.h:1036
float flip
Definition: KSFoundation.h:1028
KS_RF rf
Definition: KSFoundation.h:1454

◆ 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
3527  {
3528  if (!trap) { return; }
3529 
3530  trap->base.ngenerated = 0;
3531  trap->wfpulse = NULL;
3532  trap->wfi = NULL;
3533 }
KS_BASE base
Definition: KSFoundation.h:667
KS_WFINSTANCE * wfi
Definition: KSFoundation.h:680
int ngenerated
Definition: KSFoundation.h:496
WF_PULSE ** wfpulse
Definition: KSFoundation.h:679

◆ 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
3549  {
3550  if (!wait) { return; }
3551 
3552  wait->base.ngenerated = 0;
3553  wait->wfpulse = NULL;
3554  wait->wfi = NULL;
3555 }
KS_BASE base
Definition: KSFoundation.h:543
KS_WFINSTANCE * wfi
Definition: KSFoundation.h:549
WF_PULSE * wfpulse
Definition: KSFoundation.h:548
int ngenerated
Definition: KSFoundation.h:496

◆ 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
3560  {
3561  if (!phaser) { return; }
3562 
3563  ks_instancereset_trap(&phaser->grad);
3564 }
KS_TRAP grad
Definition: KSFoundation.h:1719
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:3527

◆ 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
3569  {
3570  if (!read) { return; }
3571 
3572  read->acq.base.ngenerated = 0;
3573  read->acq.echo = NULL;
3574 
3575  ks_instancereset_trap(&read->grad);
3576  ks_instancereset_wave(&read->omega);
3577 }
void ks_instancereset_wave(KS_WAVE *wave)
Definition: KSFoundation_common.c:3538
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:3527

◆ 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
3582  {
3583  if (!rf) { return; }
3584 
3588 
3589 }
KS_WAVE thetawave
Definition: KSFoundation.h:1039
KS_WAVE rfwave
Definition: KSFoundation.h:1037
void ks_instancereset_wave(KS_WAVE *wave)
Definition: KSFoundation_common.c:3538
KS_WAVE omegawave
Definition: KSFoundation.h:1038

◆ 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
3594  {
3595  if (!selrf) { return; }
3596 
3597  ks_instancereset_rf(&selrf->rf);
3598  ks_instancereset_trap(&selrf->pregrad);
3599  ks_instancereset_trap(&selrf->grad);
3601 
3603 
3604 }
KS_TRAP grad
Definition: KSFoundation.h:1463
KS_WAVE gradwave
Definition: KSFoundation.h:1465
KS_TRAP pregrad
Definition: KSFoundation.h:1462
KS_RF rf
Definition: KSFoundation.h:1454
void ks_instancereset_wave(KS_WAVE *wave)
Definition: KSFoundation_common.c:3538
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:3582
KS_TRAP postgrad
Definition: KSFoundation.h:1464
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:3527

◆ 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
3609  {
3610  if (!epi) { return; }
3611 
3614  ks_instancereset_trap(&epi->blip);
3616 }
KS_TRAP blip
Definition: KSFoundation.h:1935
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:3560
KS_PHASER blipphaser
Definition: KSFoundation.h:1937
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:3569
KS_READTRAP read
Definition: KSFoundation.h:1932
KS_TRAP readphaser
Definition: KSFoundation.h:1934
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:3527

◆ 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
4095  {
4096 
4097  return ks_compare_loc_by_timeboard(&a->loc, &b->loc);
4098 }
int ks_compare_loc_by_timeboard(const KS_SEQLOC *a, const KS_SEQLOC *b)
Definition: KSFoundation_common.c:4086
KS_SEQLOC loc
Definition: KSFoundation.h:486

◆ 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
4103  {
4104 
4105  return (a->loc.board > b->loc.board) - (a->loc.board < b->loc.board) +
4106  (a->loc.board == b->loc.board) * ((a->loc.pos > b->loc.pos) - (a->loc.pos < b->loc.pos));
4107 }
int pos
Definition: KSFoundation.h:463
int board
Definition: KSFoundation.h:462
KS_SEQLOC loc
Definition: KSFoundation.h:486

◆ 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
4112  {
4113 
4114  return (a->inst_hdr_head->start > b->inst_hdr_head->start) - (a->inst_hdr_head->start < b->inst_hdr_head->start);
4115 }

◆ 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
4161  {
4162  const int i1 = **(const int **) v1;
4163  const int i2 = **(const int **) v2;
4164 
4165  return i1 < i2 ? -1 : (i1 > i2);
4166 }

◆ 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
4151  {
4152  const short i1 = **(const short **) v1;
4153  const short i2 = **(const short **) v2;
4154 
4155  return i1 < i2 ? -1 : (i1 > i2);
4156 }

◆ 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
4171  {
4172  const float i1 = **(const float **) v1;
4173  const float i2 = **(const float **) v2;
4174 
4175  return i1 < i2 ? -1 : (i1 > i2);
4176 }

◆ 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
4191  {
4192  const int i1 = *(const int *) v1;
4193  const int i2 = *(const int *) v2;
4194 
4195  return i1 < i2 ? -1 : (i1 > i2);
4196 }

◆ 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
4181  {
4182  const short i1 = *(const short *) v1;
4183  const short i2 = *(const short *) v2;
4184 
4185  return i1 < i2 ? -1 : (i1 > i2);
4186 }

◆ 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
4201  {
4202  const float i1 = *(const float *) v1;
4203  const float i2 = *(const float *) v2;
4204 
4205  return i1 < i2 ? -1 : (i1 > i2);
4206 }

◆ 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
4211  {
4212  int i;
4213  int *parray[n];
4214 
4215  for (i = 0; i < n; i++)
4216  parray[i] = &array[i];
4217 
4218  qsort(parray, n, sizeof * parray, ks_compare_pint);
4219 
4220  for (i = 0; i < n; i++)
4221  sortedindx[i] = parray[i] - array;
4222 
4223 }
int ks_compare_pint(const void *v1, const void *v2)
Compares two int pointers
Definition: KSFoundation_common.c:4161

◆ 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
4228  {
4229  int i;
4230  short *parray[n];
4231 
4232  for (i = 0; i < n; i++)
4233  parray[i] = &array[i];
4234 
4235  qsort(parray, n, sizeof * parray, ks_compare_pshort);
4236 
4237  for (i = 0; i < n; i++)
4238  sortedindx[i] = parray[i] - array;
4239 
4240 }
int ks_compare_pshort(const void *v1, const void *v2)
Compares two short pointers
Definition: KSFoundation_common.c:4151

◆ 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
4245  {
4246  int i;
4247  float *parray[n];
4248 
4249  for (i = 0; i < n; i++)
4250  parray[i] = &array[i];
4251 
4252  qsort(parray, n, sizeof * parray, ks_compare_pfloat);
4253 
4254  for (i = 0; i < n; i++)
4255  sortedindx[i] = parray[i] - array;
4256 
4257 }
int ks_compare_pfloat(const void *v1, const void *v2)
Compares two float pointers
Definition: KSFoundation_common.c:4171

◆ 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 on tgt

Parameters
[in,out]aArray of KS_WFINSTANCE elements
[in]nitemsNumber of elements in array
Returns
void
4120  {
4121  qsort(a, nitems, sizeof(KS_WFINSTANCE),
4122  (int(*)(const void *, const void *))ks_compare_wfi_by_timeboard);
4123 }
(Internal use) Structure being a part of various sequence objects to sort them in time across boards...
Definition: KSFoundation.h:483
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:4095

◆ ks_sort_loc_by_timeboard()

void ks_sort_loc_by_timeboard ( KS_SEQLOC a,
int  nitems 
)

Sort KS_SEQLOCs in time, then board

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

Parameters
[in,out]aArray of KS_SEQLOC elements
[in]nitemsNumber of elements in array
Returns
void
4128  {
4129  qsort(a, nitems, sizeof(KS_SEQLOC),
4130  (int(*)(const void *, const void *))ks_compare_loc_by_timeboard);
4131 }
int ks_compare_loc_by_timeboard(const KS_SEQLOC *a, const KS_SEQLOC *b)
Definition: KSFoundation_common.c:4086
typedef struct used as argument to ks_pg_*** functions to control where and when to place a sequence ...
Definition: KSFoundation.h:461

◆ 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
4136  {
4137  qsort(a, nitems, sizeof(KS_WFINSTANCE), (int(*)(const void *, const void *))ks_compare_wfi_by_boardtime);
4138 }
(Internal use) Structure being a part of various sequence objects to sort them in time across boards...
Definition: KSFoundation.h:483
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:4103

◆ 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
4143  {
4144  qsort(a, nitems, sizeof(WF_PULSE),
4145  (int(*)(const void *, const void *))ks_compare_wfp_by_time);
4146 }
int ks_compare_wfp_by_time(const WF_PULSE *a, const WF_PULSE *b)
Compares two WF_PULSES in time
Definition: KSFoundation_common.c:4112

◆ ks_file_exist()

int ks_file_exist ( char *  filename)
7280  {
7281  struct stat buffer;
7282  return (stat (filename, &buffer) == 0);
7283 }

◆ ks_plot_host_slicetime_delete()

void ks_plot_host_slicetime_delete ( )

ADDTITLEHERE

ADDDESCHERE

Returns
void
7310  {
7311  char fname[500];
7313  remove(fname);
7314  ks_plot_enable = 0;
7315 }
void ks_plot_host_slicetime_fullfile(char *fullfile)
Definition: KSFoundation_host.c:7299
int ks_plot_enable
Definition: KSFoundation_host.c:7308

◆ ks_plot_slicetime_begin()

void ks_plot_slicetime_begin ( )

ADDTITLEHERE

ADDDESCHERE

Returns
void
4266  {
4267 #ifdef IPG
4268  return;
4269 #endif
4271 }
void ks_plot_host_slicetime_begin()
ADDTITLEHERE
Definition: KSFoundation_host.c:7320

◆ ks_plot_slicetime()

void ks_plot_slicetime ( const KS_SEQ_CONTROL ctrl,
int  nslices,
float *  slicepos_mm,
float  slthick_mm,
KS_PLOT_EXCITATION_MODE  exctype 
)

ADDTITLEHERE

ADDDESCHERE

Parameters
ctrlADDTEXTHERE
[in]nslicesADDTEXTHERE
slicepos_mmADDTEXTHERE
[in]slthick_mmADDTEXTHERE
[in]exctypeADDTEXTHERE
Returns
void
4276  {
4277 #ifdef IPG
4278  return;
4279 #endif
4280  ks_plot_host_slicetime(ctrl, nslices, slicepos_mm, slthick_mm, excmode);
4281 }
void ks_plot_host_slicetime(const KS_SEQ_CONTROL *ctrl, int nslices, float *slicepos_mm, float slthick_mm, KS_PLOT_EXCITATION_MODE exctype)
ADDTITLEHERE
Definition: KSFoundation_host.c:7449

◆ ks_plot_slicetime_endofslicegroup()

void ks_plot_slicetime_endofslicegroup ( const char *  desc)

ADDTITLEHERE

ADDDESCHERE

Parameters
descADDTEXTHERE
Returns
void
4306  {
4307 #ifdef IPG
4308  return;
4309 #endif
4311 }
Definition: KSFoundation.h:415
void ks_plot_host_slicetime_endofslicegroup(const char *desc, const KS_PLOT_SLICEGROUP_MODE tag)
ADDTITLEHERE
Definition: KSFoundation_host.c:7354

◆ ks_plot_slicetime_endofslicegroup_tagged()

void ks_plot_slicetime_endofslicegroup_tagged ( const char *  desc,
const KS_PLOT_SLICEGROUP_MODE  tag 
)

ADDTITLEHERE

ADDDESCHERE

Parameters
descADDTEXTHERE
[in]tagADDTEXTHERE
Returns
void
4296  {
4297 #ifdef IPG
4298  return;
4299 #endif
4301 }
void ks_plot_host_slicetime_endofslicegroup(const char *desc, const KS_PLOT_SLICEGROUP_MODE tag)
ADDTITLEHERE
Definition: KSFoundation_host.c:7354

◆ ks_plot_slicetime_endofpass()

void ks_plot_slicetime_endofpass ( KS_PLOT_PASS_MODE  pass_mode)

ADDTITLEHERE

ADDDESCHERE

Parameters
[in]pass_modeADDTEXTHERE
Returns
void
4286  {
4287 #ifdef IPG
4288  return;
4289 #endif
4291 }
void ks_plot_host_slicetime_endofpass(KS_PLOT_PASS_MODE pass_mode)
ADDTITLEHERE
Definition: KSFoundation_host.c:7400

◆ ks_plot_slicetime_end()

void ks_plot_slicetime_end ( )

ADDTITLEHERE

ADDDESCHERE

Returns
void
4316  {
4317 #ifdef IPG
4318  return;
4319 #endif
4321 }
void ks_plot_host_slicetime_end()
ADDTITLEHERE
Definition: KSFoundation_host.c:7512

◆ ks_plot_write_peplans()

void ks_plot_write_peplans ( const KS_PHASEENCODING_PLAN *const *  plans,
const int  num_plans,
FILE *  fp 
)

ADDTITLEHERE

ADDDESCHERE

Parameters
plansADDTEXTHERE
[in]num_plansADDTEXTHERE
fpADDTEXTHERE
Returns
void
7669  {
7670 
7671  if (plans == NULL || num_plans <= 0) {
7672  return;
7673  }
7674  int plan_idx = 0;
7675  fprintf(fp, "\"phaseencplans\": [\n");
7676  for (; plan_idx < num_plans; plan_idx++) {
7677  const KS_PHASEENCODING_PLAN* const plan = plans[plan_idx];
7678  if (plan) {
7679  fprintf(fp, "{\n");
7680  fprintf(fp, "\t\"description\": \"%s\",\n", plan->description);
7681  fprintf(fp, "\t\"num_shots\": %d,\n", plan->num_shots);
7682  fprintf(fp, "\t\"coord_type\": [\"%s\", \"%s\"],\n", plan->coord_type[0] == CARTESIAN_COORD ? "CARTESIAN_COORD" : "RADIAL_COORD",
7683  plan->coord_type[1] == CARTESIAN_COORD ? "CARTESIAN_COORD" : "RADIAL_COORD");
7684  fprintf(fp, "\t\"encodes_per_shot\": %d,\n", plan->encodes_per_shot);
7685  fprintf(fp, "\t\"entries\": [\n");
7686  int shot;
7687  for (shot = 0; shot < plan->num_shots; shot++) {
7688  fprintf(fp, "\t\t[");
7689  int encode;
7690  for (encode = 0; encode < plan->encodes_per_shot; encode++) {
7691  const KS_PHASEENCODING_COORD coord = ks_phaseencoding_get(plan, encode, shot);
7692  fprintf(fp, "[%d, %d]", coord.ky, coord.kz);
7693  if (encode < (plan->encodes_per_shot - 1)) {
7694  fprintf(fp, ", ");
7695  }
7696  }
7697  fprintf(fp, "]");
7698  if (shot < (plan->num_shots - 1)) {
7699  fprintf(fp, ",\n");
7700  }
7701  }
7702  fprintf(fp, "]\n");
7703 
7704  if (plan_idx < (num_plans-1)) {
7705  fprintf(fp, "},\n");
7706  } else {
7707  fprintf(fp, "}\n");
7708  }
7709  } else {
7710  /* KS_THROW("plans[%d] is NULL", plan_idx); */
7711  }
7712  }
7713  fputs("]", fp);
7714 
7715  return;
7716 }
KS_PHASEENCODING_COORD ks_phaseencoding_get(const KS_PHASEENCODING_PLAN *phaseenc_plan_ptr, int encode, int shot)
Get [ky,kz] coordinate from KS_PHASEENCODING_PLAN for given encode and shot
Definition: KSFoundation_common.c:597
int num_shots
Definition: KSFoundation.h:1789
ADDTITLEHERE
Definition: KSFoundation.h:1787
s16 ky
Definition: KSFoundation.h:1750
Definition: KSFoundation.h:1762
Struct holding a 3D k-space phase encoding location (ky,kz)
Definition: KSFoundation.h:1749
KS_COORDINATE_TYPE coord_type[2]
Definition: KSFoundation.h:1793
KS_DESCRIPTION description
Definition: KSFoundation.h:1791
int encodes_per_shot
Definition: KSFoundation.h:1788
s16 kz
Definition: KSFoundation.h:1751

◆ ks_plot_host_slicetime_begin()

void ks_plot_host_slicetime_begin ( )

ADDTITLEHERE

ADDDESCHERE

Returns
void
7320  {
7321  if (ks_plot_filefmt == KS_PLOT_OFF) {
7322  return;
7323  }
7324  extern int optr;
7325  extern float pitscan;
7326  char path[250];
7327  char fname[500];
7328  char cmd[300];
7331 
7332  sprintf(cmd, "mkdir -p %s > /dev/null", path);
7333  system(cmd);
7334  /* Reset file */
7335  FILE* fp = fopen(fname, "w");
7336  fprintf(fp,
7337  "{\n"
7338  "\"metadata\": {\n"
7339  "\t\"psdname\": \"%s\",\n"
7340  "\t\"optr\": %d,\n"
7341  "\t\"pitscan\": %.0f\n"
7342  "},\n"
7343  "\"passes\": [ {\n"
7344  "\t\"slicegroups\": [\n\t[{},\n\tP", /* P indicates a new pass has started */
7345  ks_psdname, optr, pitscan);
7346  fclose(fp);
7347 
7348  ks_plot_enable = 1;
7349 }
void ks_plot_host_slicetime_fullfile(char *fullfile)
Definition: KSFoundation_host.c:7299
Definition: KSFoundation.h:397
void ks_plot_host_slicetime_path(char *path)
Definition: KSFoundation_host.c:7288
int ks_plot_filefmt
Definition: GERequired.e:272
int ks_plot_enable
Definition: KSFoundation_host.c:7308
char ks_psdname[256]
Definition: GERequired.e:245

◆ ks_plot_host_slicetime()

void ks_plot_host_slicetime ( const KS_SEQ_CONTROL ctrl,
int  nslices,
float *  slicepos_mm,
float  slthick_mm,
KS_PLOT_EXCITATION_MODE  exctype 
)

ADDTITLEHERE

ADDDESCHERE

Parameters
ctrlADDTEXTHERE
[in]nslicesADDTEXTHERE
slicepos_mmADDTEXTHERE
[in]slthick_mmADDTEXTHERE
[in]exctypeADDTEXTHERE
Returns
void
7453  {
7454  if (!ks_plot_enable || ks_plot_filefmt == KS_PLOT_OFF || ctrl->duration <= 0) {
7455  return;
7456  }
7457  static FILE *fp;
7458  int i;
7459  char excmode_str[50];
7460  char fname[1000];
7461 
7463  if (! ks_file_exist(fname)) {
7464  return;
7465  }
7466 
7467  switch (excmode) {
7468  case KS_PLOT_STANDARD: strcpy(excmode_str, "KS_PLOT_STANDARD"); break;
7469  case KS_PLOT_NO_EXCITATION: strcpy(excmode_str, "KS_PLOT_NO_EXCITATION"); break;
7470  }
7471 
7472  if (ctrl->duration > 0) {
7473  fp = fopen(fname, "r+");
7474  if (fp == NULL) {return;}
7475 
7476  fseek(fp, -1, SEEK_END);
7477  int lastchar = fgetc(fp);
7478  if (!(lastchar == 'S' || lastchar == 'G' || lastchar == 'P')) {
7479  KS_THROW("%c not expected", lastchar);
7480  }
7481  fseek(fp, -1, SEEK_END);
7482 
7483  fprintf(fp,
7484  "{\n"
7485  "\t\t\"description\": \"%s\",\n"
7486  "\t\t\"excitationmode\": \"%s\",\n"
7487  "\t\t\"duration\": %0.3f,\n"
7488  "\t\t\"nseqinstances\": %d,\n"
7489  "\t\t\"minduration\": %0.3f,\n"
7490  "\t\t\"numrf\": %d,\n"
7491  "\t\t\"numtrap\": %d,\n"
7492  "\t\t\"numwave\": %d,\n"
7493  "\t\t\"numacq\": %d,\n"
7494  "\t\t\"slicethickness\": %0.3f,\n"
7495  "\t\t\"slicepos\": [",
7496  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);
7497 
7498  /* SMS slice locations */
7499  for (i = 0; i < (nslices - 1); i++) {
7500  fprintf(fp, "%0.3f, ", slicepos_mm == NULL ? 0.0 : slicepos_mm[i]);
7501  }
7502  fprintf(fp, "%0.3f]\n\t},S", slicepos_mm == NULL ? 0.0 : slicepos_mm[nslices - 1]); /* S indicates a slice was added */
7503  fflush(fp);
7504  fclose(fp);
7505  }
7506 
7507 }
void ks_plot_host_slicetime_fullfile(char *fullfile)
Definition: KSFoundation_host.c:7299
Definition: KSFoundation.h:397
int numtrap
Definition: KSFoundation.h:1058
int duration
Definition: KSFoundation.h:1227
int nseqinstances
Definition: KSFoundation.h:1225
int numrf
Definition: KSFoundation.h:1056
KS_GRADRFCTRL gradrf
Definition: KSFoundation.h:1236
int min_duration
Definition: KSFoundation.h:1224
Definition: KSFoundation.h:404
Definition: KSFoundation.h:403
int ks_plot_filefmt
Definition: GERequired.e:272
int ks_plot_enable
Definition: KSFoundation_host.c:7308
int numwave
Definition: KSFoundation.h:1060
int ks_file_exist(char *filename)
Definition: KSFoundation_host.c:7280
KS_DESCRIPTION description
Definition: KSFoundation.h:1234
#define KS_THROW(format,...)
Definition: KSFoundation.h:181
int numacq
Definition: KSFoundation.h:1062

◆ ks_plot_host_slicetime_endofslicegroup()

void ks_plot_host_slicetime_endofslicegroup ( const char *  desc,
const KS_PLOT_SLICEGROUP_MODE  tag 
)

ADDTITLEHERE

ADDDESCHERE

Parameters
descADDTEXTHERE
[in]tagADDTEXTHERE
Returns
void
7354  {
7356  return;
7357  }
7358  char fname[500];
7360  if (!ks_file_exist(fname)) {
7361  return;
7362  }
7363  char modestr[128];
7364  if (mode == KS_PLOT_SG_NOTSET) { strcpy(modestr,"KS_PLOT_SG_NOTSET"); }
7365  else if (mode == KS_PLOT_SG_ACQUISITION) { strcpy(modestr,"KS_PLOT_SG_ACQUISITION"); }
7366  else if (mode == KS_PLOT_SG_DUMMY) { strcpy(modestr,"KS_PLOT_SG_DUMMY"); }
7367  else if (mode == KS_PLOT_SG_CALIBRATION) { strcpy(modestr,"KS_PLOT_SG_CALIBRATION"); }
7368 
7369  FILE* fp = fopen(fname, "r+");
7370  if (fp == NULL) {return;}
7371 
7372  fseek(fp, -1, SEEK_END);
7373  int lastchar = fgetc(fp);
7374  if (lastchar == 'S') {
7375  fseek(fp, -1, SEEK_END); /* Expected */
7376  } else if (lastchar == 'G' || lastchar == 'P') {
7377  KS_THROW("%s is an empty slice group (lastchar \"%c\")", desc, lastchar);
7378  fclose(fp);
7379  return;
7380  } else {
7381  KS_THROW("last character \"%c\" not expected", lastchar);
7382  }
7383 
7384 
7385  fseek(fp, -1, SEEK_END);
7386  fprintf(fp,
7387  "{\n\t"
7388  "\"groupdescription\": \"%s\",\n\t"
7389  "\"mode\": \"%s\"\n\t"
7390  "}", desc == NULL ? "N/A" : desc,
7391  modestr);
7392  fputs("],[\n\t{},\n\tG", fp); /* G indicates slicegroup was closed */
7393  fflush(fp);
7394  fclose(fp);
7395 }
void ks_plot_host_slicetime_fullfile(char *fullfile)
Definition: KSFoundation_host.c:7299
Definition: KSFoundation.h:397
int ks_plot_filefmt
Definition: GERequired.e:272
Definition: KSFoundation.h:415
int ks_plot_enable
Definition: KSFoundation_host.c:7308
int ks_file_exist(char *filename)
Definition: KSFoundation_host.c:7280
Definition: KSFoundation.h:413
Definition: KSFoundation.h:412
Definition: KSFoundation.h:414
#define KS_THROW(format,...)
Definition: KSFoundation.h:181

◆ ks_plot_host_slicetime_endofpass()

void ks_plot_host_slicetime_endofpass ( KS_PLOT_PASS_MODE  pass_mode)

ADDTITLEHERE

ADDDESCHERE

Parameters
[in]pass_modeADDTEXTHERE
Returns
void
7400  {
7402  return;
7403  }
7404  static FILE *fp;
7405  char fname[500];
7407  if (! ks_file_exist(fname)) {
7408  return;
7409  }
7410 
7411  char passmode_str[50];
7412  switch (pass_mode) {
7413  case KS_PLOT_PASS_WAS_DUMMY: strcpy(passmode_str, "KS_PLOT_PASS_WAS_DUMMY"); break;
7414  case KS_PLOT_PASS_WAS_CALIBRATION: strcpy(passmode_str, "KS_PLOT_PASS_WAS_CALIBRATION"); break;
7415  case KS_PLOT_PASS_WAS_STANDARD: strcpy(passmode_str, "KS_PLOT_PASS_WAS_STANDARD"); break;
7416  }
7417 
7418  fp = fopen(fname, "r+");
7419  if (fp == NULL) {return;}
7420 
7421  fseek(fp, -1, SEEK_END);
7422  int lastchar = fgetc(fp);
7423  if (lastchar == 'S') {
7424  KS_THROW("Slice group will be automatically closed");
7426  } else if (lastchar == 'G') {
7427  /* Expected - closed slice group */
7428  } else if (lastchar == 'P') {
7429  KS_THROW("Trying to close an empty pass");
7430  fclose(fp);
7431  return;
7432  } else {
7433  KS_THROW("last character \"%c\" not expected", lastchar);
7434  }
7435 
7436  fseek(fp, -10, SEEK_END);
7437  fputs("],\n", fp);
7438  fprintf(fp,
7439  "\t\"passmode\": \"%s\"\n"
7440  "\t},{\n"
7441  "\t\"slicegroups\": [\n\t[{},\n\tP", passmode_str); /* P indicates a new pass or end is expected */
7442  fflush(fp);
7443  fclose(fp);
7444 }
void ks_plot_host_slicetime_fullfile(char *fullfile)
Definition: KSFoundation_host.c:7299
Definition: KSFoundation.h:397
Definition: KSFoundation.h:407
void ks_plot_host_slicetime_endofslicegroup(const char *desc, const KS_PLOT_SLICEGROUP_MODE mode)
ADDTITLEHERE
Definition: KSFoundation_host.c:7354
int ks_plot_filefmt
Definition: GERequired.e:272
Definition: KSFoundation.h:415
int ks_plot_enable
Definition: KSFoundation_host.c:7308
int ks_file_exist(char *filename)
Definition: KSFoundation_host.c:7280
Definition: KSFoundation.h:409
#define KS_THROW(format,...)
Definition: KSFoundation.h:181
Definition: KSFoundation.h:408

◆ ks_plot_host_slicetime_end()

void ks_plot_host_slicetime_end ( )

ADDTITLEHERE

ADDDESCHERE

Returns
void
7512  {
7514  ks_plot_enable = 0;
7515  return;
7516  }
7517 
7518 
7519 
7520  char outputdir_uid[250];
7521  char cmd[1000];
7522 
7523  char fname[500];
7525  if (! ks_file_exist(fname)) {
7526  return;
7527  }
7528 
7529  FILE *fp = fopen(fname, "r+");
7530  int fd = fileno(fp);
7531  if (!fp) {
7532  return;
7533  }
7534  fseek(fp, -1, SEEK_END);
7535  int lastchar = fgetc(fp);
7536  if (lastchar == 'S') {
7537  KS_THROW("Slicegroup and pass will be closed automatically");
7540  } else if (lastchar == 'G') {
7541  /*KS_THROW("Pass will be closed automatically");*/
7543  } else if (lastchar == 'P') {
7544  /* Expected*/
7545  } else {
7546  KS_THROW("last character \"%c\" not expected", lastchar);
7547  }
7548 
7549  fseek(fp, -29, SEEK_END);
7550  fprintf(fp, "]\n}");
7551  fflush(fp);
7552  ftruncate(fd, ftell(fp));
7553  fclose(fp);
7554 
7555  outputdir_uid[0] = '\0';
7556 #ifdef PSD_HW
7557  /* on MR-scanner */
7558  char outputdir[250];
7559  char pythonpath[] = "/usr/local/bin/apython";
7560  char psdplotpath[] = "/usr/local/bin/psdplot_slicetime.py";
7561  sprintf(outputdir, "/usr/g/mrraw/plot/%s/", ks_psdname);
7562  if (ks_plot_kstmp) {
7563  sprintf(outputdir_uid, "/usr/g/mrraw/kstmp/%010d/plot/", rhkacq_uid);
7564  }
7565 #else
7566  /* in Simulation */
7567  char outputdir[] = "./plot/";
7568  char pythonpath[] = "apython";
7569  char psdplotpath[] = "../KSFoundation/psdplot/psdplot_slicetime.py";
7570 #endif
7571 
7572  sprintf(cmd, "%s %s "
7573  "%s "
7574  "--outputdir %s %s &",
7575  pythonpath, psdplotpath, fname, outputdir, outputdir_uid);
7576  system(cmd);
7577 
7578  ks_plot_enable = 0;
7579  return;
7580 }
int ftruncate(int fd, off_t length)
void ks_plot_host_slicetime_fullfile(char *fullfile)
Definition: KSFoundation_host.c:7299
Definition: KSFoundation.h:397
int rhkacq_uid
int ks_plot_kstmp
Definition: GERequired.e:273
void ks_plot_host_slicetime_endofslicegroup(const char *desc, const KS_PLOT_SLICEGROUP_MODE mode)
ADDTITLEHERE
Definition: KSFoundation_host.c:7354
void ks_plot_host_slicetime_endofpass(KS_PLOT_PASS_MODE pass_mode)
ADDTITLEHERE
Definition: KSFoundation_host.c:7400
int ks_plot_filefmt
Definition: GERequired.e:272
Definition: KSFoundation.h:415
int ks_plot_enable
Definition: KSFoundation_host.c:7308
int ks_file_exist(char *filename)
Definition: KSFoundation_host.c:7280
char ks_psdname[256]
Definition: GERequired.e:245
Definition: KSFoundation.h:409
#define KS_THROW(format,...)
Definition: KSFoundation.h:181

◆ ks_plot_host()

void ks_plot_host ( const KS_SEQ_COLLECTION seqcollection,
const KS_PHASEENCODING_PLAN plan 
)

ADDTITLEHERE

ADDDESCHERE

Parameters
seqcollectionADDTEXTHERE
planADDTEXTHERE
Returns
void
7585  {
7586  int i;
7587  for (i = 0; i < seqcollection->numseq; i++) {
7589  }
7590  return;
7591 }
KS_SEQ_CONTROL * seqctrlptr[KS_MAXUNIQUE_SEQUENCES]
Definition: KSFoundation.h:1268
void ks_plot_host_seqctrl(const KS_SEQ_CONTROL *ctrl, const KS_PHASEENCODING_PLAN *plan)
ADDTITLEHERE
Definition: KSFoundation_host.c:7596
KS_SEQ_COLLECTION seqcollection
Definition: ksepi.e:82
int numseq
Definition: KSFoundation.h:1264

◆ ks_plot_host_seqctrl()

void ks_plot_host_seqctrl ( const KS_SEQ_CONTROL ctrl,
const KS_PHASEENCODING_PLAN plan 
)

ADDTITLEHERE

ADDDESCHERE

Parameters
ctrlADDTEXTHERE
planADDTEXTHERE
Returns
void
7596  {
7597  ks_plot_host_seqctrl_manyplans(ctrl, &plan, 1);
7598 }
void ks_plot_host_seqctrl_manyplans(const KS_SEQ_CONTROL *ctrl, const KS_PHASEENCODING_PLAN **plans, const int num_plans)
ADDTITLEHERE
Definition: KSFoundation_host.c:7721

◆ ks_plot_host_seqctrl_manyplans()

void ks_plot_host_seqctrl_manyplans ( const KS_SEQ_CONTROL ctrl,
const KS_PHASEENCODING_PLAN **  plan,
const int  num_plans 
)

ADDTITLEHERE

ADDDESCHERE

Parameters
ctrlADDTEXTHERE
planADDTEXTHERE
[in]num_plansADDTEXTHERE
Returns
void
7721  {
7722 #if defined (PSD_HW) && defined (IPG)
7723  /* on IPG and on scanner, i.e. we are actually scanning so no plots for you */
7724  return;
7725 #endif
7726 
7727  if (ks_plot_filefmt == KS_PLOT_OFF || ctrl->duration == 0 || ctrl->nseqinstances == 0) {
7728  return;
7729  }
7730 
7731  char boardc[9] = "xyzrRtos"; /* x,y,z,rho1, (unused rho2), theta, omega, ssp */
7732  char fname[1000]; /* Full filename of the json file */
7733  char path[250]; /* Directory of json file */
7734  char outputdir[250]; /* Where plots are saved */
7735  char outputdir_uid[250]; /* rhkacq_uid based output directory (for automatic transfers) */
7736 
7737  ks_plot_filename(fname, path, outputdir, outputdir_uid, ctrl->description);
7738 
7739  char cmd[250];
7740  FILE* fp;
7741  int i, j;
7742  KS_SEQLOC loc;
7743 
7744 #ifdef PSD_HW
7745  /* on MR-scanner */
7746  if (ks_plot_kstmp) {
7747  sprintf(cmd, "mkdir -p %s > /dev/null", outputdir_uid);
7748  system(cmd);
7749  }
7750  char pythonpath[] = "/usr/local/bin/apython";
7751  char psdplotpath[] = "/usr/local/bin/psdplot.py";
7752 #else
7753  /* in Simulation */
7754  char pythonpath[] = "apython";
7755  char psdplotpath[] = "../KSFoundation/psdplot/psdplot.py";
7756 #endif
7757  sprintf(cmd, "mkdir -p %s > /dev/null", path);
7758  system(cmd);
7759 
7760  fp = fopen(fname, "w");
7761 
7762 
7763  fprintf(fp, "{\n");
7764  fprintf(fp, "\"metadata\": {\n");
7765  fprintf(fp, "\t\"mode\": \"host\",\n");
7766  fprintf(fp, "\t\"psdname\": \"%s\",\n", ks_psdname);
7767  fprintf(fp, "\t\"sequence\": \"%s\",\n", ctrl->description);
7768  fprintf(fp, "\t\"duration\": %0.3f,\n", ctrl->duration/1000.0);
7769  fprintf(fp, "\t\"min_duration\": %0.3f,\n", ctrl->min_duration/1000.0);
7770  fprintf(fp, "\t\"ssi_time\": %0.3f,\n", ctrl->ssi_time/1000.0);
7771  fprintf(fp, "\t\"momentstart\": %.3f,\n", ctrl->momentstart/1000.0);
7772  fprintf(fp, "\t\"optr_desc\": \"%s\"\n", _optr.descr);
7773  fprintf(fp, "},\n");
7774 
7775  /* PHASE ENCODING PLANS */
7776  ks_plot_write_peplans( plans, num_plans, fp);
7777 
7778  fputs(",\n", fp);
7779  /* TRAPEZOIDS */
7780  fprintf(fp, "\"frames\": [\n");
7781  fprintf(fp, "{},\n");
7782  fprintf(fp, "{\n\"trapz\": {\n");
7783  for (i = 0; i < ctrl->gradrf.numtrap; i++) { /* each unique trap object */
7784  KS_TRAP* trap = ctrl->gradrf.trapptr[i];
7785  fprintf(fp, "\t\"%s\": {\n", trap->description);
7786  fprintf(fp, "\t\t\"ramptime\": %0.3f,\n", trap->ramptime / 1000.0);
7787  fprintf(fp, "\t\t\"plateautime\": %0.3f,\n", trap->plateautime / 1000.0);
7788  fprintf(fp, "\t\t\"duration\": %0.3f,\n", trap->duration / 1000.0);
7789  fprintf(fp, "\t\t\"amp\": %0.6f,\n", isnan(trap->amp) ? 0.0f : trap->amp);
7790  fprintf(fp, "\t\t\"instances\": [");
7791 
7792  for (j = 0; j < trap->base.ninst; j++) { /* each instance of the trap */
7793  fprintf(fp, "{\n");
7794  loc = trap->locs[j];
7795  fprintf(fp, "\t\t\t\"board\": \"%c\",\n", boardc[loc.board]);
7796  fprintf(fp,"\t\t\t\"time\": %0.3f,\n", loc.pos / 1000.0);
7797  fputs("\t\t\t\"rtscale\": ", fp);
7798  ks_plot_host_rtscales_amps(&trap->rtscaling, fp, j);
7799  fputs("\t\t\t\"state\": ", fp);
7800  ks_plot_host_rtscales_states(&trap->rtscaling, fp, j);
7801  fprintf(fp,"\t\t\t\"ampscale\": %0.6f\n", loc.ampscale);
7802  if (j < (trap->base.ninst - 1)) {
7803  fprintf(fp, "\t\t},");
7804  } else {
7805  fprintf(fp, "\t\t}");
7806  }
7807  }
7808  fprintf(fp, "]\n");
7809 
7810  if (i < (ctrl->gradrf.numtrap - 1)) {
7811  fprintf(fp, "\t},\n");
7812  } else {
7813  fprintf(fp, "\t}\n");
7814  }
7815  }
7816  fprintf(fp, "},\n");
7817 
7818  /* ACQUISITIONS */
7819  fprintf(fp, "\"acquisitions\": [\n");
7820  for (i = 0; i < ctrl->gradrf.numacq; i++) { /* Each unique KS_READ object */
7821  KS_READ* acq = ctrl->gradrf.readptr[i];
7822  fprintf(fp, "\t{\n");
7823  fprintf(fp, "\t\t\"description\": \"%s\",\n", acq->description);
7824  fprintf(fp, "\t\t\"duration\": %0.3f,\n", acq->duration / 1000.0);
7825  fprintf(fp, "\t\t\"rbw\": %0.3f,\n", acq->rbw);
7826  fprintf(fp, "\t\t\"samples\": %d,\n", acq->filt.outputs);
7827  fprintf(fp, "\t\t\"time\": [");
7828  for (j = 0; j < acq->base.ninst; ++j) {
7829  fprintf(fp, "%0.3f", acq->pos[j] / 1000.0);
7830  if (j < (acq->base.ninst - 1)) {
7831  fprintf(fp, ", ");
7832  }
7833  }
7834  fprintf(fp, "]\n");
7835  if (i < (ctrl->gradrf.numacq - 1)) {
7836  fprintf(fp, "\t},\n");
7837  } else {
7838  fprintf(fp, "\t}\n");
7839  }
7840  }
7841  fprintf(fp, "],\n");
7842 
7843  /* RF (RHO1) */
7844  fprintf(fp, "\"rf\": {\n");
7845  for (i = 0; i < ctrl->gradrf.numrf; i++) { /* each unique RF object */
7846  KS_RF* rfptr = ctrl->gradrf.rfptr[i];
7847  if (rfptr->rfwave.res > 0) {
7848  char roledesc[50];
7849  if (rfptr->role == KS_RF_ROLE_EXC) {
7850  strcpy(roledesc,"KS_RF_ROLE_EXC");
7851  } else if (rfptr->role == KS_RF_ROLE_REF) {
7852  strcpy(roledesc,"KS_RF_ROLE_REF");
7853  } else if (rfptr->role == KS_RF_ROLE_SPSAT) {
7854  strcpy(roledesc,"KS_RF_ROLE_SPSAT");
7855  } else if (rfptr->role == KS_RF_ROLE_CHEMSAT) {
7856  strcpy(roledesc, "KS_RF_ROLE_CHEMSAT");
7857  } else if (rfptr->role == KS_RF_ROLE_INV) {
7858  strcpy(roledesc, "KS_RF_ROLE_INV");
7859  } else if (rfptr->role == KS_RF_ROLE_SPSAT) {
7860  strcpy(roledesc, "KS_RF_ROLE_SPSAT");
7861  } else {
7862  strcpy(roledesc,"NONE");
7863  }
7864  fprintf(fp, "\t\"%s\": {\n", rfptr->rfwave.description);
7865  fprintf(fp, "\t\t\"description\": \"%s\",\n", rfptr->rfwave.description);
7866  fprintf(fp, "\t\t\"flipangle\": %0.3f,\n", rfptr->flip);
7867  fprintf(fp, "\t\t\"nominal_flipangle\": %0.3f,\n", rfptr->rfpulse.nom_fa);
7868  fprintf(fp, "\t\t\"max_b1\": %0.6f,\n", rfptr->rfpulse.max_b1);
7869  fprintf(fp, "\t\t\"amp\": %0.6f,\n", rfptr->amp);
7870  fprintf(fp, "\t\t\"isodelay\": %0.3f,\n", rfptr->start2iso / 1000.0);
7871  fprintf(fp, "\t\t\"duration\": %0.3f,\n", rfptr->rfwave.duration / 1000.0);
7872  fprintf(fp, "\t\t\"role\": \"%s\",\n", roledesc);
7873 
7874  fprintf(fp, "\t\t\"omega\": {\n");
7875  if (rfptr->omegawave.res > 0) {
7876  fprintf(fp, "\t\t\t\"description\": \"%s\",\n", rfptr->omegawave.description);
7877  fprintf(fp, "\t\t\t\"duration\": %0.3f,\n", rfptr->omegawave.duration / 1000.0);
7878  ks_plot_host_wavestates(&rfptr->omegawave, fp);
7879  fprintf(fp, "\t\t\t\"instances\": [");
7880  for (j = 0; j < rfptr->omegawave.base.ninst; j++) {
7881  fprintf(fp, "{\n");
7882  loc = rfptr->omegawave.locs[j];
7883  fprintf(fp, "\t\t\t\t\"time\": %0.3f,\n", loc.pos/1000.0);
7884  fprintf(fp, "\t\t\t\t\"board\": \"%c\",\n", boardc[loc.board]);
7885  fputs("\t\t\t\t\"rtscale\": ", fp);
7887  fputs("\t\t\t\"state\": ", fp);
7889  fprintf(fp, "\t\t\t\t\"ampscale\": %0.6f\n", loc.ampscale);
7890  if (j < (rfptr->omegawave.base.ninst - 1)) {
7891  fprintf(fp, "\t\t\t},");
7892  } else {
7893  fprintf(fp, "\t\t\t}");
7894  }
7895  }
7896  fprintf(fp, "]\n");
7897  }
7898  fprintf(fp, "\t\t},\n"); /* Omega */
7899 
7900  fprintf(fp, "\t\t\"theta\": {\n");
7901  if (rfptr->thetawave.res > 0) {
7902  fprintf(fp, "\t\t\t\"description\": \"%s\",\n", rfptr->thetawave.description);
7903  fprintf(fp, "\t\t\t\"duration\": %0.3f,\n", rfptr->thetawave.duration / 1000.0);
7904 
7905  ks_plot_host_wavestates(&rfptr->thetawave, fp);
7906  fprintf(fp, "\t\t\t\"instances\": [");
7907  for (j = 0; j < rfptr->thetawave.base.ninst; j++) {
7908  fprintf(fp, "{\n");
7909  loc = rfptr->thetawave.locs[j];
7910  fprintf(fp, "\t\t\t\t\"time\": %0.3f,\n", loc.pos/1000.0);
7911  fprintf(fp, "\t\t\t\t\"board\": \"%c\",\n", boardc[loc.board]);
7912  fputs("\t\t\t\t\"rtscale\": ", fp);
7914  fputs("\t\t\t\t\"state\": ", fp);
7916  fprintf(fp, "\t\t\t\t\"ampscale\": %0.6f\n", loc.ampscale);
7917  if (j < (rfptr->thetawave.base.ninst - 1)) {
7918  fprintf(fp, "\t\t\t},");
7919  } else {
7920  fprintf(fp, "\t\t\t}");
7921  }
7922  }
7923  fprintf(fp, "]\n");
7924  }
7925  fprintf(fp, "\t\t},\n"); /* Theta */
7926 
7927 
7928  fflush(fp);
7929  ks_plot_host_wavestates(&rfptr->rfwave, fp);
7930  fflush(fp);
7931  fprintf(fp, "\t\t\"instances\": [");
7932  for (j = 0; j < rfptr->rfwave.base.ninst; j++) { /* each instance of the wave (RHO1) */
7933  fprintf(fp, "{\n");
7934  loc = rfptr->rfwave.locs[j];
7935  fprintf(fp, "\t\t\t\"time\": %0.3f,\n", loc.pos/1000.0);
7936  fprintf(fp, "\t\t\t\"board\": \"%c\",\n", boardc[loc.board]);
7937  fputs("\t\t\t\"rtscale\": ", fp);
7939  fputs("\t\t\t\"state\": ", fp);
7941  fprintf(fp, "\t\t\t\"ampscale\": %0.6f\n", loc.ampscale);
7942  if (j < (rfptr->rfwave.base.ninst - 1)) {
7943  fprintf(fp, "\t\t},");
7944  } else {
7945  fprintf(fp, "\t\t}");
7946  }
7947  }
7948  fprintf(fp, "]\n");
7949 
7950  if (i < (ctrl->gradrf.numrf - 1)) {
7951  fprintf(fp, "\t},\n");
7952  } else {
7953  fprintf(fp, "\t}\n");
7954  }
7955  } /* res > 0 */
7956  } /* rf */
7957  fprintf(fp, "},\n");
7958 
7959  /* Wait pulses */
7960  fputs("\"waits\": {\n",fp);
7961  for (i = 0; i < ctrl->gradrf.numwait; i++) {
7962  KS_WAIT* waitptr = ctrl->gradrf.waitptr[i];
7963  if (waitptr->max_duration > 0) {
7964  fprintf(fp, "\t\"%s\": {\n", waitptr->description);
7965  fprintf(fp, "\t\t\"description\": \"%s\",\n", waitptr->description);
7966  fprintf(fp, "\t\t\"max_duration\": %0.3f,\n", waitptr->max_duration / 1000.0);
7967  fprintf(fp, "\t\t\"pg_duration\": %0.3f,\n", waitptr->pg_duration / 1000.0);
7968  fprintf(fp, "\t\t\"instances\": [");
7969  for (j = 0; j < waitptr->base.ninst; j++) { /* each instance of the wave */
7970  fprintf(fp, "{\n");
7971  loc = waitptr->locs[j];
7972  fprintf(fp, "\t\t\t\"board\": \"%c\",\n", boardc[loc.board]);
7973  fputs("\t\t\t\"duration\": ", fp);
7974  if (waitptr->rtscaling.states && (waitptr->rtscaling.num_completed_playouts * waitptr->rtscaling.num_instances) > 0) {
7975  ks_plot_host_rtscales_states(&waitptr->rtscaling, fp, j);
7976  } else { /* Default to pg duration if not set in scan */
7977  fprintf(fp, "[%d],\n", waitptr->pg_duration);
7978  }
7979  fprintf(fp, "\t\t\t\"time\": %0.3f\n", loc.pos/1000.0);
7980  if (j < (waitptr->base.ninst - 1)) {
7981  fprintf(fp, "\t\t},");
7982  } else {
7983  fprintf(fp, "\t\t}");
7984  }
7985  }
7986  fprintf(fp, "]\n");
7987  if (i < (ctrl->gradrf.numwait - 1)) {
7988  fprintf(fp, "\t},\n");
7989  } else {
7990  fprintf(fp, "\t}\n");
7991  }
7992  }
7993  }
7994  fputs("},\n", fp); /* End waits */
7995 
7996  fprintf(fp, "\"waves\": {\n");
7997  /* other (non-RF) waves */
7998  for (i = 0; i < ctrl->gradrf.numwave; i++) {
7999  KS_WAVE* waveptr = ctrl->gradrf.waveptr[i];
8000  if (waveptr->res > 0) {
8001  fprintf(fp, "\t\"%s\": {\n", waveptr->description);
8002  fprintf(fp, "\t\t\"description\": \"%s\",\n", waveptr->description);
8003  fprintf(fp, "\t\t\"duration\": %0.3f,\n", waveptr->duration / 1000.0);
8004  ks_plot_host_wavestates(waveptr, fp);
8005  fprintf(fp, "\t\t\"instances\": [");
8006  for (j = 0; j < waveptr->base.ninst; j++) { /* each instance of the wave */
8007  fprintf(fp, "{\n");
8008  loc = waveptr->locs[j];
8009  fprintf(fp, "\t\t\t\"board\": \"%c\",\n", boardc[loc.board]);
8010  fprintf(fp, "\t\t\t\"time\": %0.3f,\n", loc.pos/1000.0);
8011  fputs("\t\t\t\"rtscale\": ", fp);
8012  ks_plot_host_rtscales_amps(&waveptr->rtscaling, fp, j);
8013  fputs("\t\t\t\"state\": ", fp);
8014  ks_plot_host_rtscales_states(&waveptr->rtscaling, fp, j);
8015  fprintf(fp, "\t\t\t\"ampscale\": %0.6f\n", loc.ampscale);
8016  if (j < (waveptr->base.ninst - 1)) {
8017  fprintf(fp, "\t\t},");
8018  } else {
8019  fprintf(fp, "\t\t}");
8020  }
8021  }
8022  fprintf(fp, "]\n");
8023  if (i < (ctrl->gradrf.numwave - 1)) {
8024  fprintf(fp, "\t},\n");
8025  } else {
8026  fprintf(fp, "\t}\n");
8027  }
8028  } /* res > 0 */
8029  }
8030  fputs("}\n}\n]\n}", fp); /* end waves, frame, frames, end */
8031  fflush(fp);
8032  fclose(fp);
8033 
8034  {
8035  char cmd[1000];
8036  sprintf(cmd, "%s %s %s "
8037  "--outputdir %s &",
8038  pythonpath, psdplotpath, fname,
8039  outputdir);
8040  ks_dbg("%s", cmd);
8041  system(cmd);
8042  #ifdef PSD_HW
8043 /* on MR-scanner */
8044  if (ks_plot_kstmp) {
8045  sprintf(outputdir_uid, "/usr/g/mrraw/kstmp/%010d/plot/", rhkacq_uid);
8046  sprintf(cmd, "cp %s%s_%s.html %s",
8047  outputdir, ks_psdname, ctrl->description,
8048  outputdir_uid);
8049  system(cmd);
8050  sprintf(cmd, "cp %s %s > /dev/null", fname, outputdir_uid);
8051  system(cmd);
8052  }
8053 #endif
8054  }
8055  return;
8056 }
int plateautime
Definition: KSFoundation.h:672
Core sequence object for making trapezoids on X,Y,Z, and OMEGA boards
Definition: KSFoundation.h:666
int start2iso
Definition: KSFoundation.h:1032
KS_BASE base
Definition: KSFoundation.h:667
int num_completed_playouts
Definition: KSFoundation.h:477
Definition: KSFoundation.h:397
KS_SEQLOC * locs
Definition: KSFoundation.h:752
int pos
Definition: KSFoundation.h:463
KS_RF * rfptr[KS_MAXUNIQUE_RF]
Definition: KSFoundation.h:1055
int role
Definition: KSFoundation.h:1027
Core sequence object making arbitrary waveforms on any board (using float data format)
Definition: KSFoundation.h:743
void ks_plot_host_rtscales_amps(const KS_RT_SCALE_LOG *rtscalelog, FILE *fp, const int instance)
Definition: KSFoundation_host.c:7603
RF_PULSE rfpulse
Definition: KSFoundation.h:1036
void ks_plot_host_wavestates(const KS_WAVE *waveptr, FILE *fp)
Definition: KSFoundation_host.c:7645
Definition: KSFoundation.h:2340
Definition: KSFoundation.h:2340
int numtrap
Definition: KSFoundation.h:1058
int ninst
Definition: KSFoundation.h:494
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:542
int momentstart
Definition: KSFoundation.h:1228
KS_SEQLOC * locs
Definition: KSFoundation.h:678
int board
Definition: KSFoundation.h:462
int * pos
Definition: KSFoundation.h:844
float rbw
Definition: KSFoundation.h:842
int duration
Definition: KSFoundation.h:1227
KS_DESCRIPTION description
Definition: KSFoundation.h:544
KS_RT_SCALE_LOG rtscaling
Definition: KSFoundation.h:550
int nseqinstances
Definition: KSFoundation.h:1225
Definition: KSFoundation.h:2340
KS_RT_SCALE_LOG rtscaling
Definition: KSFoundation.h:681
int numrf
Definition: KSFoundation.h:1056
float ampscale
Definition: KSFoundation.h:464
float flip
Definition: KSFoundation.h:1028
KS_BASE base
Definition: KSFoundation.h:839
void ks_plot_write_peplans(const KS_PHASEENCODING_PLAN *const *plans, const int num_plans, FILE *fp)
ADDTITLEHERE
Definition: KSFoundation_host.c:7669
KS_GRADRFCTRL gradrf
Definition: KSFoundation.h:1236
KS_DESCRIPTION description
Definition: KSFoundation.h:840
KS_RT_SCALE_LOG rtscaling
Definition: KSFoundation.h:755
Composite sequence object for RF (with optional OMEGA & THETA pulses)
Definition: KSFoundation.h:1026
KS_WAVE thetawave
Definition: KSFoundation.h:1039
int rhkacq_uid
int ks_plot_kstmp
Definition: GERequired.e:273
int numwait
Definition: KSFoundation.h:1064
void ks_plot_host_rtscales_states(const KS_RT_SCALE_LOG *rtscalelog, FILE *fp, const int instance)
Definition: KSFoundation_host.c:7624
KS_WAVE * waveptr[KS_MAXUNIQUE_WAVE]
Definition: KSFoundation.h:1059
int num_instances
Definition: KSFoundation.h:476
KS_DESCRIPTION description
Definition: KSFoundation.h:745
int * states
Definition: KSFoundation.h:473
KS_BASE base
Definition: KSFoundation.h:543
KS_WAVE rfwave
Definition: KSFoundation.h:1037
int min_duration
Definition: KSFoundation.h:1224
KS_SEQLOC * locs
Definition: KSFoundation.h:547
KS_BASE base
Definition: KSFoundation.h:744
void ks_plot_filename(char *fname, char *path, char *outputdir, char *outputdir_uid, const char *const seq_desc)
ADDTITLEHERE
Definition: KSFoundation_host.c:8061
Core sequence object that handles a data acquisition window
Definition: KSFoundation.h:838
int duration
Definition: KSFoundation.h:841
float amp
Definition: KSFoundation.h:1031
KS_WAIT * waitptr[KS_MAXUNIQUE_WAIT]
Definition: KSFoundation.h:1063
float amp
Definition: KSFoundation.h:669
typedef struct used as argument to ks_pg_*** functions to control where and when to place a sequence ...
Definition: KSFoundation.h:461
int ks_plot_filefmt
Definition: GERequired.e:272
KS_TRAP * trapptr[KS_MAXUNIQUE_TRAP]
Definition: KSFoundation.h:1057
int ssi_time
Definition: KSFoundation.h:1226
KS_DESCRIPTION description
Definition: KSFoundation.h:668
int numwave
Definition: KSFoundation.h:1060
int pg_duration
Definition: KSFoundation.h:545
int max_duration
Definition: KSFoundation.h:546
char ks_psdname[256]
Definition: GERequired.e:245
Definition: KSFoundation.h:2340
FILTER_INFO filt
Definition: KSFoundation.h:843
KS_WAVE omegawave
Definition: KSFoundation.h:1038
STATUS STATUS ks_dbg(const char *format,...) __attribute__((format(printf
Common debug message function for HOST and TGT
KS_DESCRIPTION description
Definition: KSFoundation.h:1234
int duration
Definition: KSFoundation.h:673
KS_READ * readptr[KS_MAXUNIQUE_READ]
Definition: KSFoundation.h:1061
_cvint _optr
int res
Definition: KSFoundation.h:746
Definition: KSFoundation.h:2340
int ramptime
Definition: KSFoundation.h:671
int duration
Definition: KSFoundation.h:747
int numacq
Definition: KSFoundation.h:1062

◆ ks_plot_tgt_reset()

void ks_plot_tgt_reset ( KS_SEQ_CONTROL ctrl)

ADDTITLEHERE

ADDDESCHERE

Parameters
ctrlADDTEXTHERE
Returns
void
4326  {
4327  FILE *fp;
4328  char fname_json[1000];
4329  char cmd[200];
4330  #ifdef PSD_HW
4331  return;
4332  #endif
4333 
4334 
4335  if (ks_plot_filefmt == KS_PLOT_OFF) {
4336  return;
4337  }
4338 
4339  system("mkdir -p ./plot/tgt/");
4340  sprintf(fname_json, "./plot/tgt/psdplot_tgt_%s_%s.json", ks_psdname, ctrl->description);
4341  sprintf(cmd, "rm -f %s ./plot/tgt/%s_%s_*_tgt.*",
4342  fname_json, ks_psdname, ctrl->description);
4343  system(cmd);
4344  fp = fopen(fname_json, "w");
4345  fprintf(fp, "{\n"
4346  "\"metadata\": {\n"
4347  "\t\"mode\": \"tgt\",\n"
4348  "\t\"psdname\": \"%s\",\n"
4349  "\t\"sequence\": \"%s\",\n"
4350  "\t\"duration\": %0.3f,\n"
4351  "\t\"min_duration\": %0.3f,\n"
4352  "\t\"ssi_time\": %0.3f,\n"
4353  "\t\"momentstart\": %.3f\n"
4354  "},\n"
4355  "\"frames\": [\n"
4356  "{}]}",
4357  ks_psdname,
4358  ctrl->description,
4359  ctrl->duration/1000.0,
4360  ctrl->min_duration/1000.0,
4361  ctrl->ssi_time/1000.0,
4362  ctrl->momentstart/1000.0
4363  );
4364  fclose(fp);
4365 }
Definition: KSFoundation.h:397
int momentstart
Definition: KSFoundation.h:1228
int duration
Definition: KSFoundation.h:1227
int ks_plot_filefmt
Definition: GERequired.e:272
int min_duration
Definition: KSFoundation.h:1224
int ssi_time
Definition: KSFoundation.h:1226
char ks_psdname[256]
Definition: GERequired.e:245
KS_DESCRIPTION description
Definition: KSFoundation.h:1234

◆ ks_plot_filename()

void ks_plot_filename ( char *  fname,
char *  path,
char *  outputdir,
char *  outputdir_uid,
const char *const  seq_desc 
)

ADDTITLEHERE

ADDDESCHERE

Parameters
fnameADDTEXTHERE
pathADDTEXTHERE
outputdirADDTEXTHERE
outputdir_uidADDTEXTHERE
seq_descADDTEXTHERE
Returns
void
8061  {
8062 #if defined (PSD_HW) && defined (IPG)
8063  return;
8064 #endif
8065 
8066 
8067 #ifdef PSD_HW
8068  /* on MR-scanner */
8069  sprintf(path, "/usr/g/mrraw/plot/%s/host/", ks_psdname);
8070  if (outputdir) {
8071  sprintf(outputdir, "/usr/g/mrraw/plot/%s/", ks_psdname);
8072  }
8073 
8074  if (outputdir_uid && ks_plot_kstmp) {
8075  sprintf(outputdir_uid, "/usr/g/mrraw/kstmp/%010d/plot/", rhkacq_uid);
8076  }
8077 #else
8078  /* in Simulation */
8079  sprintf(path, "./plot/host/");
8080  if (outputdir) {
8081  sprintf(outputdir, "./plot/");
8082  }
8083 #endif
8084 
8085  sprintf(fname, "%spsdplot_%s_%s.json", path, ks_psdname, seq_desc);
8086 
8087 return;
8088 }
int rhkacq_uid
int ks_plot_kstmp
Definition: GERequired.e:273
char ks_psdname[256]
Definition: GERequired.e:245

◆ ks_plot_saveconfig()

void ks_plot_saveconfig ( KS_SEQ_CONTROL ctrl)

ADDTITLEHERE

ADDDESCHERE

Parameters
ctrlADDTEXTHERE
Returns
void
8093  {
8094 #ifdef IPG
8095  return;
8096 #endif
8097  char fname[1000]; /* Full filename of the json file */
8098  char path[250]; /* Directory of json file */
8099  ks_plot_filename(fname, path, NULL, NULL, ctrl->description);
8100  int i, j;
8101  FILE* fp;
8102  if ((fp = fopen(fname, "r+"))) {
8103  ks_dbg("The file %s exists", fname);
8104  } else {
8105  ks_dbg("The file %s is not there", fname);
8106  return;
8107  }
8108  fseek(fp, -4, SEEK_END);
8109  fputs(",\n"
8110  "\t{\n", fp);
8111  for (i = 0; i < ctrl->gradrf.numtrap; i++) {
8112  KS_TRAP* trap = ctrl->gradrf.trapptr[i];
8113  fprintf(fp, "\t\t\"%s\": [", trap->description);
8114 
8115  for (j = 0; j < ks_numplaced(&trap->base); j++) { /* each instance of the trap */
8116  fprintf(fp, "%0.6f", ks_rt_scale_log_get(&trap->rtscaling, j, ctrl->current_playout));
8117  if (j < (ks_numplaced(&trap->base) - 1)) {
8118  fputs(", ", fp);
8119  }
8120  }
8121  fputs("]", fp);
8122  if (i == (ctrl->gradrf.numtrap - 1)) {
8123  fputs("\n", fp);
8124  } else {
8125  fputs(",\n", fp);
8126  }
8127  }
8128 
8129  fputs("\t}", fp);
8130 
8131 
8132  fputs("\n"
8133  "]\n"
8134  "}",fp);
8135  fflush(fp);
8136  fclose(fp);
8137 
8138 }
Core sequence object for making trapezoids on X,Y,Z, and OMEGA boards
Definition: KSFoundation.h:666
KS_BASE base
Definition: KSFoundation.h:667
float ks_rt_scale_log_get(const KS_RT_SCALE_LOG *log, int instance_idx, int playout_idx)
Definition: KSFoundation_common.c:4981
int numtrap
Definition: KSFoundation.h:1058
int current_playout
Definition: KSFoundation.h:1238
KS_RT_SCALE_LOG rtscaling
Definition: KSFoundation.h:681
KS_GRADRFCTRL gradrf
Definition: KSFoundation.h:1236
int ks_numplaced(KS_BASE *base)
ADDTITLEHERE
Definition: KSFoundation_common.c:4838
void ks_plot_filename(char *fname, char *path, char *outputdir, char *outputdir_uid, const char *const seq_desc)
ADDTITLEHERE
Definition: KSFoundation_host.c:8061
KS_TRAP * trapptr[KS_MAXUNIQUE_TRAP]
Definition: KSFoundation.h:1057
KS_DESCRIPTION description
Definition: KSFoundation.h:668
STATUS STATUS ks_dbg(const char *format,...) __attribute__((format(printf
Common debug message function for HOST and TGT
KS_DESCRIPTION description
Definition: KSFoundation.h:1234

◆ ks_plot_tgt_addframe()

void ks_plot_tgt_addframe ( KS_SEQ_CONTROL ctrl)

Writes a plot frame to file

Parameters
ctrlPointer to sequence control
4370  {
4371  #if (!(defined(SIM) && defined(IPG)))
4372  return;
4373  #endif
4374 
4375  if (ks_plot_filefmt == KS_PLOT_OFF) {
4376  return;
4377  }
4378 
4379  char boardc[8] = "xyzrRto"; /* x,y,z,rho1, rho2, theta, omega*/
4380  char fname[1000];
4381  sprintf(fname, "./plot/tgt/psdplot_tgt_%s_%s.json", ks_psdname, ctrl->description);
4382  FILE *fp;
4383  int i, w, inst, boardinstance;
4384  KS_SEQLOC loc;
4385  fp = fopen(fname, "r+");
4386  fseek(fp, -2, SEEK_END);
4387  fputs(",{\n", fp);
4388  short iamp;
4389  float gradmax;
4390  float amp;
4391 
4392  /* TRAPEZOIDS */
4393  fprintf(fp, "\"trapz\": {\n");
4394  fflush(fp);
4395  KS_TRAP* trap;
4396  for (i = 0; i < ctrl->gradrf.numtrap; i++) { /* Each unique trap object */
4397  trap = ctrl->gradrf.trapptr[i];
4398  fprintf(fp, "\t\"%s\": {\n", trap->description);
4399  fprintf(fp, "\t\t\"ramptime\": %0.3f,\n", trap->ramptime / 1000.0);
4400  fprintf(fp, "\t\t\"plateautime\": %0.3f,\n", trap->plateautime / 1000.0);
4401  fprintf(fp, "\t\t\"duration\": %0.3f,\n", trap->duration / 1000.0);
4402  fprintf(fp, "\t\t\"amp\": %0.6f,\n", 1.0);
4403  fprintf(fp, "\t\t\"instances\": [{\n");
4404  fflush(fp);
4405  for (inst = 0; inst < ctrl->gradrf.trapptr[i]->base.ngenerated; inst++) { /* Each instance of this trap */
4406  loc = trap->locs[inst];
4407  fprintf(fp, "\t\t\t\"board\": \"%c\",\n", boardc[loc.board]);
4408  fprintf(fp, "\t\t\t\"time\": %0.3f,\n", loc.pos / 1000.0);
4409 
4410  WF_PULSE* plateau_ptr = trap->wfi[inst].wf;
4411  boardinstance = trap->wfi[inst].boardinstance;
4412  /* Get the linked list of the plateau (used to get the position of all wf instances) */
4413  /* WF_INSTR_HDR instr_plateau = plateau_ptr->inst_hdr_tail; */
4414  /* WF_INSTR_HDR instr_attack = plateau_ptr->assoc_pulse->inst_hdr_tail; */ /* Attack is the associated pulse of the plateau */
4415  /* WF_INSTR_HDR instr_decay = plateau_ptr->assoc_pulse->assoc_pulse->inst_hdr_tail; */ /* Decay is the associated pulse of the attack */
4416  /*
4417  for (idx = 0; idx < (boardinstance); idx++) {
4418  instr_plateau = instr_plateau->next;
4419  instr_attack = instr_attack->next;
4420  instr_decay = instr_decay->next;
4421  }
4422  */
4423  /* Plateau amplitude is written in G/cm */
4424  getiamp(&iamp, plateau_ptr, boardinstance);
4425  fprintf(fp, "\t\t\t\"ampscale\": 1.0,\n");
4426  gradmax = ks_syslimits_gradtarget(loggrd, trap->wfi[inst].loc.board);
4427  if (loc.board == OMEGA || loc.board == THETA) {
4428  gradmax = 1;
4429  }
4430  amp = gradmax * (float)iamp / MAX_PG_IAMP;
4431  fprintf(fp, "\t\t\t\"rtscale\": %.12f\n", amp);
4432 
4433  /* char pulsename[100]; */
4434  /* (float)pbeg(plateau_ptr, pulsename, boardinstance)/1000.0 */
4435  /* sprintf(pulsename, "%s%s", plateau_ptr->pulsename, "a"); */
4436 
4437  if (inst == (trap->base.ngenerated -1)) {
4438  fprintf(fp, "\t\t}]\n");
4439  } else {
4440  fprintf(fp, "\t\t},{\n");
4441  }
4442  } /* inst */
4443  if (i == (ctrl->gradrf.numtrap -1)) {
4444  fprintf(fp, "\t}\n");
4445  } else {
4446  fprintf(fp, "\t},\n");
4447  }
4448  }
4449  fprintf(fp, "},\n"); /* Trapezoids */
4450 
4451  fprintf(fp, "\"acquisitions\": [\n");
4452  /* ACQUISITIONS */
4453  for (i = 0; i < ctrl->gradrf.numacq; i++) { /* Each unique KS_READ object */
4454  KS_READ* acq = ctrl->gradrf.readptr[i];
4455  fprintf(fp, "\t{\n");
4456  fprintf(fp, "\t\t\"description\": \"%s\",\n", acq->description);
4457  fprintf(fp, "\t\t\"duration\": %0.3f,\n", acq->duration / 1000.0);
4458  fprintf(fp, "\t\t\"rbw\": %0.3f,\n", acq->rbw);
4459  fprintf(fp, "\t\t\"samples\": %d,\n", acq->filt.outputs);
4460  fprintf(fp, "\t\t\"time\": [");
4461  for (inst = 0; inst < acq->base.ngenerated; ++inst) {
4462  fprintf(fp, "%0.3f", acq->pos[inst] / 1000.0);
4463  if (inst == (acq->base.ninst -1)) {
4464  fprintf(fp, "]\n");
4465  } else {
4466  fprintf(fp, ", ");
4467  }
4468  } /* inst */
4469  if (i == (ctrl->gradrf.numacq -1)) {
4470  fprintf(fp, "\t}\n");
4471  } else {
4472  fprintf(fp, "\t},\n");
4473  }
4474  } /* ACQUISITIONS */
4475  fprintf(fp, "],\n"); /* End acquisitions */
4476 
4477  /* RF */
4478  WF_PULSE* rf_wf;
4479  WF_PULSE* omega_wf;
4480  WF_PULSE* theta_wf;
4481  short new_wave_iamp;
4482  KS_IWAVE acquired_wave; /* short int array */
4483  fprintf(fp, "\"rf\": {\n");
4484  for (i = 0; i < ctrl->gradrf.numrf; ++i) { /* Each unique KS_RF object */
4485  KS_RF* rfptr = ctrl->gradrf.rfptr[i];
4486 
4487  if (rfptr->rfwave.res > 0) {
4488  /* RHO */
4489  char roledesc[50];
4490  if (rfptr->role == KS_RF_ROLE_EXC) {
4491  strcpy(roledesc,"KS_RF_ROLE_EXC");
4492  } else if (rfptr->role == KS_RF_ROLE_REF) {
4493  strcpy(roledesc,"KS_RF_ROLE_REF");
4494  } else if (rfptr->role == KS_RF_ROLE_CHEMSAT) {
4495  strcpy(roledesc, "KS_RF_ROLE_CHEMSAT");
4496  } else if (rfptr->role == KS_RF_ROLE_INV) {
4497  strcpy(roledesc, "KS_RF_ROLE_INV");
4498  } else if (rfptr->role == KS_RF_ROLE_SPSAT) {
4499  strcpy(roledesc, "KS_RF_ROLE_SPSAT");
4500  } else {
4501  strcpy(roledesc,"NONE");
4502  }
4503  fprintf(fp, "\t\"%s\": {\n", rfptr->rfwave.description);
4504  fprintf(fp, "\t\t\"description\": \"%s\",\n", rfptr->rfwave.description);
4505  fprintf(fp, "\t\t\"flipangle\": %0.3f,\n", rfptr->flip);
4506  fprintf(fp, "\t\t\"nominal_flipangle\": %0.3f,\n", rfptr->rfpulse.nom_fa);
4507  fprintf(fp, "\t\t\"max_b1\": %0.6f,\n", rfptr->rfpulse.max_b1);
4508  fprintf(fp, "\t\t\"isodelay\": %0.3f,\n", rfptr->start2iso / 1000.0);
4509  fprintf(fp, "\t\t\"duration\": %0.3f,\n", rfptr->rfwave.duration / 1000.0);
4510  fprintf(fp, "\t\t\"role\": \"%s\",\n", roledesc);
4511  fprintf(fp, "\t\t\"amp\": %.3f,\n", rfptr->amp);
4512  /* OMEGA */
4513  fprintf(fp, "\t\t\"omega\": {\n");
4514  if (rfptr->omegawave.res > 0) {
4515  fprintf(fp, "\t\t\t\"description\": \"%s\",\n", rfptr->omegawave.description);
4516  fprintf(fp, "\t\t\t\"duration\": %0.3f,\n", rfptr->omegawave.duration / 1000.0);
4517  fprintf(fp, "\t\t\t\"instances\": [{\n");
4518  for (inst = 0; inst < rfptr->omegawave.base.ngenerated; ++inst) {
4519  loc = rfptr->omegawave.locs[inst];
4520  boardinstance = rfptr->omegawave.wfi[inst].boardinstance;
4521  omega_wf = &(rfptr->omegawave.wfpulse[OMEGA][0]);
4522  movewaveimm(acquired_wave, omega_wf, boardinstance, rfptr->omegawave.res, FROMHARDWARE);
4523  getiamp(&new_wave_iamp, omega_wf, boardinstance);
4524  fprintf(fp, "\t\t\t\t\"waveform\": [0,");
4525  for (w = 0; w < rfptr->omegawave.res; w++) {
4526  fprintf(fp, "%d,", acquired_wave[w]);
4527  }
4528  fprintf(fp, "0],\n");
4529  fprintf(fp, "\t\t\t\t\"time\": %0.3f,\n", loc.pos/1000.0);
4530  fprintf(fp, "\t\t\t\t\"board\": \"%c\",\n", boardc[loc.board]);
4531  fprintf(fp, "\t\t\t\t\"ampscale\": 1.0,\n");
4532  fprintf(fp, "\t\t\t\t\"rtscale\": %0.12f\n", (float)(new_wave_iamp) / (max_pg_wamp * max_pg_wamp));
4533  if (inst == (rfptr->omegawave.base.ngenerated -1)) {
4534  fprintf(fp, "\t\t\t}]\n");
4535  } else {
4536  fprintf(fp, "\t\t\t},{\n");
4537  }
4538  }
4539  }
4540  fprintf(fp, "\t\t},\n"); /* Omega */
4541 
4542  /* THETA */
4543  fprintf(fp, "\t\t\"theta\": {\n");
4544  if (rfptr->thetawave.res > 0) {
4545  fprintf(fp, "\t\t\t\"description\": \"%s\",\n", rfptr->thetawave.description);
4546  fprintf(fp, "\t\t\t\"duration\": %0.3f,\n", rfptr->thetawave.duration / 1000.0);
4547  fprintf(fp, "\t\t\t\"instances\": [{\n");
4548  for (inst = 0; inst < rfptr->thetawave.base.ngenerated; ++inst) {
4549  loc = rfptr->thetawave.locs[inst];
4550  boardinstance = rfptr->thetawave.wfi[inst].boardinstance;
4551  theta_wf = &(rfptr->thetawave.wfpulse[THETA][0]);
4552  movewaveimm(acquired_wave, theta_wf, boardinstance, rfptr->thetawave.res, FROMHARDWARE);
4553  getiamp(&new_wave_iamp, theta_wf, boardinstance);
4554  fprintf(fp, "\t\t\t\t\"waveform\": [0");
4555  for (w = 0; w < rfptr->thetawave.res; w++) {
4556  fprintf(fp, "%d,", acquired_wave[w]);
4557  }
4558  fprintf(fp, "0],\n");
4559  fprintf(fp, "\t\t\t\t\"time\": %0.3f,\n", loc.pos/1000.0);
4560  fprintf(fp, "\t\t\t\t\"board\": \"%c\",\n", boardc[loc.board]);
4561  fprintf(fp, "\t\t\t\t\"ampscale\": 1.0,\n");
4562  fprintf(fp, "\t\t\t\t\"rtscale\": %0.12f\n", (float)(new_wave_iamp) / (max_pg_wamp * max_pg_wamp));
4563  if (inst == (rfptr->thetawave.base.ngenerated -1)) {
4564  fprintf(fp, "\t\t\t}]\n");
4565  } else {
4566  fprintf(fp, "\t\t\t},{\n");
4567  }
4568  }
4569  }
4570  fprintf(fp, "\t\t},\n"); /* Theta */
4571 
4572  fprintf(fp, "\t\t\"instances\": [{\n");
4573  for (inst = 0; inst < rfptr->rfwave.base.ngenerated; ++inst) {
4574  loc = rfptr->rfwave.locs[inst];
4575  boardinstance = rfptr->rfwave.wfi[inst].boardinstance;
4576  rf_wf = &(rfptr->rfwave.wfpulse[RHO][0]);
4577  /* Get waveform from hardware */
4578  movewaveimm(acquired_wave, rf_wf, boardinstance, rfptr->rfwave.res, FROMHARDWARE);
4579  /* B1max recalculations */
4580  getiamp(&new_wave_iamp, rf_wf, boardinstance);
4581  fprintf(fp, "\t\t\t\"waveform\": [0,");
4582  for (w = 0; w < rfptr->rfwave.res; w++) {
4583  fprintf(fp, "%d,", acquired_wave[w]);
4584  }
4585  fprintf(fp, "0],\n");
4586  fprintf(fp, "\t\t\t\"board\": \"%c\",\n", boardc[loc.board]);
4587  fprintf(fp, "\t\t\t\"time\": %0.3f,\n", loc.pos/1000.0);
4588  fprintf(fp, "\t\t\t\"ampscale\": 1.0,\n");
4589  fprintf(fp, "\t\t\t\"rtscale\": %0.12f\n", (float)new_wave_iamp / (rfptr->amp * max_pg_wamp * max_pg_wamp));
4590  if (inst == (rfptr->rfwave.base.ngenerated -1)) {
4591  fprintf(fp, "\t\t}]\n");
4592  } else {
4593  fprintf(fp, "\t\t},{\n");
4594  }
4595  }
4596  if (i == (ctrl->gradrf.numrf -1)) {
4597  fprintf(fp, "\t}\n");
4598  } else {
4599  fprintf(fp, "\t},\n");
4600  }
4601  } /* res > 0 */
4602 
4603 
4604  } /* End RF */
4605  fprintf(fp, "},\n"); /* End RF */
4606  /* other (non-RF) waves */
4607  fprintf(fp, "\"waves\": {\n");
4608 
4609  for (i = 0; i < ctrl->gradrf.numwave; i++) { /* each unique wave object */
4610  KS_WAVE* waveptr = ctrl->gradrf.waveptr[i];
4611  if (waveptr->res > 0) {
4612  fprintf(fp, "\t\"%s\": {\n", waveptr->description);
4613  fprintf(fp, "\t\t\"description\": \"%s\",\n", waveptr->description);
4614  fprintf(fp, "\t\t\"duration\": %0.3f,\n", waveptr->duration / 1000.0);
4615  fprintf(fp, "\t\t\"instances\": [{\n");
4616  for (inst = 0; inst < waveptr->base.ninst; inst++) { /* each instance of the wave */
4617  loc = waveptr->locs[inst];
4618  boardinstance = waveptr->wfi[inst].boardinstance;
4619  WF_PULSE* other_wf = &(waveptr->wfpulse[loc.board][0]);
4620  movewaveimm(acquired_wave, other_wf, boardinstance, waveptr->res, FROMHARDWARE);
4621  getiamp(&new_wave_iamp, other_wf, boardinstance);
4622  fprintf(fp, "\t\t\t\"waveform\": [0,");
4623  for (w = 0; w < waveptr->res; w++) {
4624  fprintf(fp, "%d,", acquired_wave[w]); /* Or just waveptr->waveform[w] ? */
4625  }
4626  fprintf(fp, "0],\n");
4627  fprintf(fp, "\t\t\t\"board\": \"%c\",\n", boardc[loc.board]);
4628  fprintf(fp, "\t\t\t\"time\": %0.3f,\n", loc.pos/1000.0);
4629  fprintf(fp, "\t\t\t\"ampscale\": 1.0,\n");
4630  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));
4631  if (inst == (waveptr->base.ninst -1)) {
4632  fprintf(fp, "\t\t}]\n");
4633  } else {
4634  fprintf(fp, "\t\t},{\n");
4635  }
4636  }
4637  if (i == (ctrl->gradrf.numwave -1)) {
4638  fprintf(fp, "\t}\n");
4639  } else {
4640  fprintf(fp, "\t},\n");
4641  }
4642  } /* res > 0 */
4643  }
4644  fprintf(fp, "}\n"); /* End waves */
4645  fprintf(fp, "}\n"); /* End frame */
4646  fprintf(fp,"]}"); /* End frames*/
4647 
4648  fflush(fp);
4649  fclose(fp);
4650  return;
4651 }
int plateautime
Definition: KSFoundation.h:672
short KS_IWAVE[KS_MAXWAVELEN]
Definition: KSFoundation.h:353
int boardinstance
Definition: KSFoundation.h:484
Core sequence object for making trapezoids on X,Y,Z, and OMEGA boards
Definition: KSFoundation.h:666
int start2iso
Definition: KSFoundation.h:1032
KS_BASE base
Definition: KSFoundation.h:667
Definition: KSFoundation.h:397
KS_SEQLOC * locs
Definition: KSFoundation.h:752
int pos
Definition: KSFoundation.h:463
KS_RF * rfptr[KS_MAXUNIQUE_RF]
Definition: KSFoundation.h:1055
int role
Definition: KSFoundation.h:1027
Core sequence object making arbitrary waveforms on any board (using float data format)
Definition: KSFoundation.h:743
RF_PULSE rfpulse
Definition: KSFoundation.h:1036
Definition: KSFoundation.h:2340
Definition: KSFoundation.h:2340
int numtrap
Definition: KSFoundation.h:1058
int ninst
Definition: KSFoundation.h:494
KS_SEQLOC * locs
Definition: KSFoundation.h:678
int board
Definition: KSFoundation.h:462
int * pos
Definition: KSFoundation.h:844
float rbw
Definition: KSFoundation.h:842
float ks_syslimits_gradtarget(LOG_GRAD loggrd, int board)
Returns the maximum target amplitude for a board (internal use)
Definition: KSFoundation_common.c:264
Definition: KSFoundation.h:2340
int numrf
Definition: KSFoundation.h:1056
float flip
Definition: KSFoundation.h:1028
KS_BASE base
Definition: KSFoundation.h:839
KS_GRADRFCTRL gradrf
Definition: KSFoundation.h:1236
KS_WFINSTANCE * wfi
Definition: KSFoundation.h:680
KS_DESCRIPTION description
Definition: KSFoundation.h:840
KS_WFINSTANCE * wfi
Definition: KSFoundation.h:754
Composite sequence object for RF (with optional OMEGA & THETA pulses)
Definition: KSFoundation.h:1026
KS_WAVE thetawave
Definition: KSFoundation.h:1039
KS_SEQLOC loc
Definition: KSFoundation.h:486
KS_WAVE * waveptr[KS_MAXUNIQUE_WAVE]
Definition: KSFoundation.h:1059
KS_DESCRIPTION description
Definition: KSFoundation.h:745
LOG_GRAD loggrd
WF_PULSE ** wfpulse
Definition: KSFoundation.h:753
KS_WAVE rfwave
Definition: KSFoundation.h:1037
int ks_plot_filefmt
Definition: GERequired.e:272
KS_BASE base
Definition: KSFoundation.h:744
Core sequence object that handles a data acquisition window
Definition: KSFoundation.h:838
int duration
Definition: KSFoundation.h:841
float amp
Definition: KSFoundation.h:1031
typedef struct used as argument to ks_pg_*** functions to control where and when to place a sequence ...
Definition: KSFoundation.h:461
WF_PULSE * wf
Definition: KSFoundation.h:485
KS_TRAP * trapptr[KS_MAXUNIQUE_TRAP]
Definition: KSFoundation.h:1057
KS_DESCRIPTION description
Definition: KSFoundation.h:668
int numwave
Definition: KSFoundation.h:1060
Definition: KSFoundation.h:2340
FILTER_INFO filt
Definition: KSFoundation.h:843
char ks_psdname[256]
Definition: GERequired.e:245
KS_WAVE omegawave
Definition: KSFoundation.h:1038
KS_DESCRIPTION description
Definition: KSFoundation.h:1234
int ngenerated
Definition: KSFoundation.h:496
int duration
Definition: KSFoundation.h:673
KS_READ * readptr[KS_MAXUNIQUE_READ]
Definition: KSFoundation.h:1061
int res
Definition: KSFoundation.h:746
Definition: KSFoundation.h:2340
int ramptime
Definition: KSFoundation.h:671
int duration
Definition: KSFoundation.h:747
int numacq
Definition: KSFoundation.h:1062

◆ ks_get_center_encode()

int ks_get_center_encode ( const KS_PHASEENCODING_PLAN *const  peplan,
int  shot,
int  yres,
int  zres 
)

Finds the center encode in a phase encoding plan

TODO: Support searching more than one shot

6567  {
6568 
6569  int best_encode = -1;
6570  KS_PHASEENCODING_COORD closest_coord;
6571  float closest_distance = 1.0f/0.0f; /* +Inf */
6572 
6573  const float y_center = (yres - 1) / 2.0;
6574  const float z_center = (zres - 1) / 2.0;
6575 
6576  int encode = 0;
6577  for (; encode < peplan->encodes_per_shot; encode++) {
6578  KS_PHASEENCODING_COORD coord = ks_phaseencoding_get(peplan, encode, shot);
6579 
6580  const float distance = (zres == KS_NOTSET || coord.kz == KS_NOTSET) ?
6581  fabs(coord.ky - y_center) :
6582  (coord.ky - y_center)*(coord.ky - y_center) + (coord.kz - z_center)*(coord.kz - z_center);
6583 
6584  if (distance < closest_distance) {
6585  best_encode = encode;
6586  closest_coord = coord;
6587  closest_distance = distance;
6588  }
6589  }
6590 
6591  return best_encode;
6592 
6593 } /* ks_get_center_encode() */
KS_PHASEENCODING_COORD ks_phaseencoding_get(const KS_PHASEENCODING_PLAN *phaseenc_plan_ptr, int encode, int shot)
Get [ky,kz] coordinate from KS_PHASEENCODING_PLAN for given encode and shot
Definition: KSFoundation_common.c:597
#define KS_NOTSET
Definition: KSFoundation.h:115
s16 ky
Definition: KSFoundation.h:1750
Struct holding a 3D k-space phase encoding location (ky,kz)
Definition: KSFoundation.h:1749
int encodes_per_shot
Definition: KSFoundation.h:1788
s16 kz
Definition: KSFoundation.h:1751

◆ ks_peplan_find_center_from_linear_sweep()

int ks_peplan_find_center_from_linear_sweep ( KS_KCOORD *const  K,
KS_VIEW view,
const int  num_coords,
const KS_MTF_DIRECTION  mtf_direction,
const int  sweep_sign,
const int  segment_size,
const int  start_encode 
)

ADDTITLEHERE

8982  {
8983  int idx;
8984  if (sweep_sign != 1 && sweep_sign != -1) {
8985  KS_THROW("Sweep sign must be +-1, not%d", sweep_sign);
8986  return KS_NOTSET;
8987  }
8988  KS_VIEW* views = (KS_VIEW*) malloc(num_coords * sizeof(KS_VIEW));
8989  if (views_in == NULL) {
8990  if (K == NULL) {
8991  KS_THROW("K and views cannot both be null");
8992  return KS_NOTSET;
8993  }
8994 
8995  for (idx = 0; idx < num_coords; idx++) {
8996  views[idx].coord = &(K[idx]);
8997  }
8998  } else if (K != NULL) {
8999  KS_THROW("Both K and views != NULL");
9000  return KS_NOTSET;
9001  } else {
9002  memcpy(views, views_in, num_coords * sizeof(KS_VIEW));
9003  }
9004 
9005  if (mtf_direction == MTF_Z) {
9006  qsort(views, num_coords, sizeof(KS_VIEW), ks_get_comp_kview_z_y(sweep_sign));
9007  } else if (mtf_direction == MTF_Y) {
9008  qsort(views, num_coords, sizeof(KS_VIEW), ks_get_comp_kview_y_z(sweep_sign));
9009  } else if (mtf_direction == MTF_R) {
9010  qsort(views, num_coords, sizeof(KS_VIEW), &ks_comp_kview_r_t);
9011  }
9012 
9013  for (idx = 0; idx < num_coords; idx++) {
9014  views[idx].encode = idx / segment_size + start_encode;
9015  }
9016 
9017  if (mtf_direction == MTF_Y) {
9018  qsort(views, num_coords, sizeof(KS_VIEW), &ks_comp_kview_dist_y_z);
9019  /* qsort(views, num_coords, sizeof(KS_VIEW), &ks_comp_kview_r_t); */
9020 
9021  } else if (mtf_direction == MTF_Z) {
9022  qsort(views, num_coords, sizeof(KS_VIEW), &ks_comp_kview_dist_z_y);
9023  /* qsort(views, num_coords, sizeof(KS_VIEW), &ks_comp_kview_r_t); */
9024 
9025  } else if (mtf_direction == MTF_R) {
9026  qsort(views, num_coords, sizeof(KS_VIEW), &ks_comp_kview_r_t);
9027  }
9028  int center = views[0].encode;
9029  /* ks_dbg("Center %d at [%d,%d]", views[0].encode, views[0].coord->y, views[0].coord->z); */
9030 
9031 
9032  free(views);
9033 
9034 
9035  return center;
9036 }
#define KS_NOTSET
Definition: KSFoundation.h:115
Definition: KSFoundation.h:2105
KS_KCOORD * coord
Definition: KSFoundation.h:2215
ks_comparator ks_get_comp_kview_z_y(int ascend)
Definition: KSFoundation_host.c:8629
int ks_comp_kview_r_t(const void *a, const void *b)
ADDTITLEHERE
Definition: KSFoundation_host.c:8335
ADDTITLEHERE
Definition: KSFoundation.h:2214
ks_comparator ks_get_comp_kview_y_z(int ascend)
Definition: KSFoundation_host.c:8622
int encode
Definition: KSFoundation.h:2216
int ks_comp_kview_dist_y_z(const void *a, const void *b)
Definition: KSFoundation_host.c:8284
int ks_comp_kview_dist_z_y(const void *a, const void *b)
Definition: KSFoundation_host.c:8256
Definition: KSFoundation.h:2107
#define KS_THROW(format,...)
Definition: KSFoundation.h:181
Definition: KSFoundation.h:2106

◆ ks_get_center_encode_linearsweep()

int ks_get_center_encode_linearsweep ( const KS_KSPACE_ACQ kacq,
const int  etl,
const KS_PF_EARLYLATE  pf_earlylate_te 
)

ADDTITLEHERE

6601  {
6602 
6603  const KS_PEPLAN_SHOT_DISTRIBUTION shot_dist = ks_peplan_distribute_shots(etl, kacq->num_coords, KS_NOTSET /* center not known */);
6604  const int searched_encode_low_to_high = ks_peplan_find_center_from_linear_sweep(kacq->coords, NULL, kacq->num_coords, MTF_Y, 1, shot_dist.shots, 0 /* start from zero */);
6605  const int searched_encode_high_to_low = ks_peplan_find_center_from_linear_sweep(kacq->coords, NULL, kacq->num_coords, MTF_Y, -1, shot_dist.shots, 0 /* start from zero */);
6606 /* ks_dbg("searched_encode_low_to_high = %d, searched_encode_high_to_low = %d", searched_encode_low_to_high, searched_encode_high_to_low); */
6607  int out = searched_encode_low_to_high;
6608  if (pf_earlylate_te == KS_PF_EARLY) {
6609  out = IMin(2, searched_encode_low_to_high, searched_encode_high_to_low);
6610  } else if (pf_earlylate_te == KS_PF_LATE) {
6611  out = IMax(2, searched_encode_low_to_high, searched_encode_high_to_low);
6612  }
6613  return out;
6614 
6615 } /* ks_get_center_encode_linearsweep() */
int shots
Definition: KSFoundation.h:2166
Definition: KSFoundation.h:388
#define KS_NOTSET
Definition: KSFoundation.h:115
KS_KCOORD * coords
Definition: KSFoundation.h:2228
Definition: KSFoundation.h:2105
Definition: KSFoundation.h:386
int ks_peplan_find_center_from_linear_sweep(KS_KCOORD *const K, KS_VIEW *views_in, const int num_coords, const KS_MTF_DIRECTION mtf_direction, const int sweep_sign, const int segment_size, const int start_encode)
ADDTITLEHERE
Definition: KSFoundation_host.c:8977
KS_PEPLAN_SHOT_DISTRIBUTION ks_peplan_distribute_shots(const int etl, const int num_coords, const int center)
ADDTITLEHERE
Definition: KSFoundation_host.c:8955
ADDTITLEHERE
Definition: KSFoundation.h:2165
int num_coords
Definition: KSFoundation.h:2229
KS_KSPACE_ACQ kacq
Definition: ksepi_implementation.e:355

◆ ks_calc_caipi_scale()

float ks_calc_caipi_scale ( const int  ky,
const int  R,
const int  caipi_factor 
)

ADDTITLEHERE

520  {
521  float caipi_scale = 0;
522 
523  /* Get target caipi-phase with pattern linspace(-1.0, 1.0, sms_caipi_factor) */
524  if (ky != KS_NOTSET && caipi_factor > 1) {
525  caipi_scale = ( (double) ((ky / R) % caipi_factor) / (caipi_factor - 1.0) ) * 2.0 - 1.0;
526  }
527 
528  return caipi_scale;
529 }
#define KS_NOTSET
Definition: KSFoundation.h:115

◆ ks_calc_caipi_phase()

float ks_calc_caipi_phase ( const float  sms_slice_gap,
const int  sms_multiband_factor,
const SCAN_INFO *  slice_info,
const float  caipi_blip_area,
const int  slice_encode_dir 
)

ADDTITLEHERE

534  {
535 
536  int sign = slice_encode_dir == 0 ? -1 : 1;
537  float offset = (sms_slice_gap / 2) * (sms_multiband_factor - 1) * sign; /* bottom slice's distance from RF-scan_info slice position (optloc) [mm] */
538  float A_blip = (caipi_blip_area / 10.0) * 1e-6 ; /* [(G/cm) * us] -> [(G/mm) * s] */
539  float gamma = (2.0*PI*GAM); /* [Hz/G] */
540 
541  float caipi_phase = (slice_info->optloc + offset) * A_blip * gamma; /* radians */
542 
543  return caipi_phase * (180.0/PI); /* convert to degrees and return */
544 }

◆ ks_calc_caipi_offset_phase()

float ks_calc_caipi_offset_phase ( const float  sms_slice_gap,
const int  sms_multiband_factor,
const SCAN_INFO *  slice_info,
const int  caipi_factor,
const float  caipi_blip_area,
const int  ky,
const int  R,
const int  slice_encode_dir 
)

ADDTITLEHERE

550  {
551 
552  float caipi_phase = ks_calc_caipi_phase(sms_slice_gap, sms_multiband_factor, slice_info, caipi_blip_area, slice_encode_dir);
553 
554  float caipi_scale = ks_calc_caipi_scale(ky, R, caipi_factor);
555 
556  return caipi_phase *= caipi_scale;
557 
558 }
float ks_calc_caipi_phase(const float sms_slice_gap, const int sms_multiband_factor, const SCAN_INFO *slice_info, const float caipi_blip_area, const int slice_encode_dir)
ADDTITLEHERE
Definition: KSFoundation_common.c:534
float ks_calc_caipi_scale(const int ky, const int R, const int caipi_factor)
ADDTITLEHERE
Definition: KSFoundation_common.c:520

◆ ks_eval_caipiblip()

STATUS ks_eval_caipiblip ( KS_TRAP caipiblip,
const int  caipi_factor,
const float  sms_slice_gap,
const KS_DESCRIPTION  seq_desc 
)

ADDTITLEHERE

3883  {
3884  KS_DESCRIPTION tmpdesc;
3885 
3886  ks_create_suffixed_description(tmpdesc, seq_desc, "_caipiblip");
3887  caipiblip->area = ks_eval_sms_calc_caipi_area(caipi_factor, sms_slice_gap);
3888  caipiblip->fs_factor = 0; /* since the amplitude of a caipi-blip is so low, the fs_factor=0 minimizes potential round of errors while scaling */
3889  return ks_eval_trap(caipiblip, tmpdesc);
3890 }
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
Definition: KSFoundation_host.c:497
float fs_factor
Definition: KSFoundation.h:677
void ks_create_suffixed_description(char *const out, const char *const prefix, const char *suffix,...) __attribute__((format(printf
float area
Definition: KSFoundation.h:670
char KS_DESCRIPTION[KS_DESCRIPTION_LENGTH]
Definition: KSFoundation.h:351
float ks_eval_sms_calc_caipi_area(int caipi_fov_shift, float sms_slice_gap)
Calculates the CAIPI blip area
Definition: KSFoundation_host.c:3871

◆ ks_eval_sms_rf_scan_info()

int ks_eval_sms_rf_scan_info ( SCAN_INFO *  sms_slice_positions,
const SCAN_INFO *  org_slice_positions,
int  sms_multiband_factor,
int  nslices 
)

Returns a new SCAN_INFO struct with a shifted slice postions

The slice positions used for SMS needs to be shifted, since the SMS pulses are created symetrically around the center freq.

Even sms factor case: slice pos: | | | | | | | | sms slices: o o center freq. pos: v

Odd sms factor case: slice pos: | | | | | | | | | sms slices: o o o center freq. pos: v

Parameters
[in,out]sms_slice_positions
[in]org_slice_positions
[in]sms_multiband_factor
[in]nslices
Return values
sms_nslices
3895  {
3896 
3897  if (sms_factor == 1) {
3898  ks_error("%s: sms_factor = %d. Only works with sms_factor > 1.", __FUNCTION__, sms_factor);
3899  return KS_NOTSET;
3900  }
3901 
3902  int sms_nslices = CEIL_DIV(nslices, sms_factor);
3903 
3904  /* Index offset between the org. slice postion and the new one */
3905  int idx_offset = (sms_nslices) * ((int)ceil((float)sms_factor/2.0) - 1);
3906  idx_offset += sms_factor % 2 ? 0 : (int)floor((float)nslices / ((float)sms_factor * 2.0));
3907 
3908  /* Allocate new struct and fill with shifted postions */
3909  memcpy(sms_slice_positions, org_slice_positions, sms_nslices * sizeof(SCAN_INFO));
3910  int idx;
3911  for (idx = 0; idx < sms_nslices; idx++) {
3912  if ((sms_nslices) % 2 && !(sms_factor % 2)) {
3913  /* if sms_nslices is odd and sms_factor is even, calc pos between indexes */
3914  float a = org_slice_positions[idx+idx_offset].optloc;
3915  float b = org_slice_positions[idx+idx_offset+1].optloc;
3916  sms_slice_positions[idx].optloc = a - (a-b)/2.0;
3917  } else {
3918  sms_slice_positions[idx].optloc = org_slice_positions[idx+idx_offset].optloc;
3919  }
3920  }
3921 
3922  return sms_nslices;
3923 }
#define KS_NOTSET
Definition: KSFoundation.h:115
STATUS ks_error(const char *format,...) __attribute__((format(printf
Common error message function for HOST and TGT

◆ 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]
506  {
507  int firstinstance, lastinstance, i;
508 #ifdef IPG
509  int instramp;
510  KS_WFINSTANCE *wfi;
511  int numplaced = rf->rfwave.base.ngenerated;
512 #else
513  int numplaced = rf->rfwave.base.ninst;
514  if (!numplaced) {
515  return;
516  }
517 
518  rf->rfwave.rtscaling.num_instances = numplaced;
519 
520 #endif
521 
522  if (instanceno == INSTRALL) {
523  firstinstance = 0;
524  lastinstance = numplaced - 1;
525  } else {
526  firstinstance = instanceno;
527  lastinstance = instanceno;
528  }
529 
530  /* input validation */
531  if (numplaced == 0) {
532  return;
533  }
534 
535  if (firstinstance < 0 || lastinstance >= numplaced) {
536  ks_error("ks_scan_rf_ampscale(%s): instanceno (%d) out of range [0,%d]", rf->rfwave.description, instanceno, numplaced - 1);
537  return;
538  }
539 
540  if (fabs(ampscale) > 1.0) {
541  ks_error("ks_scan_rf_ampscale(%s): ampscale too large (%f)", rf->rfwave.description, ampscale);
542  return;
543  }
544 
545 #ifdef IPG
546 
547  /* set amplitude(s) */
548  for (i = firstinstance; i <= lastinstance; i++) {
549  wfi = &rf->rfwave.wfi[i];
550  instramp = (int)(rf->amp * wfi->loc.ampscale * ampscale * MAX_PG_IAMP);
551  setiamp(instramp, wfi->wf, wfi->boardinstance);
552  } /* for */
553 
554 #else
555  for (i = firstinstance; i <= lastinstance; i++) {
556  ks_rt_scale_log_set(&rf->rfwave.rtscaling, i, ampscale);
557  }
558 #endif
559 
560 } /* ks_scan_rf_ampscale */
int boardinstance
Definition: KSFoundation.h:484
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:483
int ninst
Definition: KSFoundation.h:494
float ampscale
Definition: KSFoundation.h:464
KS_RT_SCALE_LOG rtscaling
Definition: KSFoundation.h:755
KS_WFINSTANCE * wfi
Definition: KSFoundation.h:754
KS_SEQLOC loc
Definition: KSFoundation.h:486
int num_instances
Definition: KSFoundation.h:476
KS_DESCRIPTION description
Definition: KSFoundation.h:745
KS_WAVE rfwave
Definition: KSFoundation.h:1037
KS_BASE base
Definition: KSFoundation.h:744
float amp
Definition: KSFoundation.h:1031
WF_PULSE * wf
Definition: KSFoundation.h:485
int ngenerated
Definition: KSFoundation.h:496
void ks_rt_scale_log_set(KS_RT_SCALE_LOG *log, int instance_idx, float ampscale)
Definition: KSFoundation_common.c:4890

◆ 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)
566  {
567 
568  ks_scan_rf_ampscale(rf, instanceno, 1.0);
569 
570 }
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:506

◆ 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)
583  {
584  static float chopsign = 1.0;
585 
586  ks_scan_rf_ampscale(rf, instanceno, chopsign);
587 
588  chopsign *= -1.0;
589 
590 }
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:506

◆ 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)
597  {
598 
599  ks_scan_rf_ampscale(rf, instanceno, 0.0);
600 
601 }
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:506

◆ 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
683  {
684  int firstinstance, lastinstance;
685  int i;
686  float freqoffHz, hertzPerMm, ampscale;
687 #ifdef IPG
688  float tloc = sliceinfo.optloc;
689  int numplaced = selrf->rf.rfwave.base.ngenerated;
690 #else
691  float tloc = 1.0; /* Simulate offset of 1 mm for plotting in units of [Hz/mm] */
692  int numplaced = selrf->rf.rfwave.base.ninst;
693 #endif
694 
695  if (instanceno == INSTRALL) {
696  firstinstance = 0;
697  lastinstance = numplaced - 1;
698  } else {
699  firstinstance = instanceno;
700  lastinstance = instanceno;
701  }
702 
703  if (numplaced == 0) {
704  return;
705  }
706  if (firstinstance < 0 || lastinstance >= numplaced) {
707  ks_error("ks_scan_selrf_setfreqphase(%s): instanceno (%d) out of range [0,%d]", selrf->rf.rfwave.description, instanceno, numplaced - 1);
708  return;
709  }
710 
711  /* set frequency */
712  for (i = firstinstance; i <= lastinstance; i++) {
713 #ifdef IPG
714  if (!(i < selrf->rf.rfwave.base.ngenerated &&
715  selrf->rf.rfwave.wfi[i].wf->wavegen_type == TYPRHO1 &&
716  selrf->rf.rfwave.wfi[i].wf->assoc_pulse->tag == SSPFREQ)) { /* make sure it has been set up properly */
717  ks_error("ks_scan_rf_setfreqphase(%s): RF pulse not properly set up or instance # too high", selrf->rf.rfwave.description);
718  return;
719  }
720  if (selrf->gradwave.res > 0) {
721  /* gradwave */
722  if (selrf->gradwave.wfi != NULL) {
723  ampscale = selrf->gradwave.wfi[i].loc.ampscale;
724  } else {
725  ks_error("ks_scan_rf_setfreqphase(%s): no gradwave has been set up, setting ampscale = 0", selrf->rf.rfwave.description);
726  ampscale = 0;
727  }
728  } else {
729  /* trapezoid */
730  if (selrf->grad.wfi != NULL) {
731  ampscale = selrf->grad.wfi[i].loc.ampscale;
732  } else {
733  ks_error("ks_scan_rf_setfreqphase(%s): no grad has been set up, setting ampscale = 0", selrf->rf.rfwave.description);
734  ampscale = 0; /* protect against wfi[i] = NULL, then return 0 amp */
735  }
736  }
737 #else
738  ampscale = (selrf->gradwave.res > 0) ? selrf->gradwave.locs[i].ampscale
739  : selrf->grad.locs[i].ampscale;
740 #endif
741 
742  /* ampscale (a.u.) * GAM [Hz/G] / 10.0 [cm->mm] * gradamp [G/cm] = [Hz/mm] */
743  hertzPerMm = ampscale * GAM / 10.0 *
744  ((selrf->gradwave.res > 0) ? ks_calc_selgradamp(selrf->rf.bw, selrf->slthick) /* gradwave slice selection */
745  : selrf->grad.amp); /* trapezoid slice selection */
746  freqoffHz = selrf->rf.cf_offset; /* [Hz] */
747  unsigned short iphase_omega = 0;
748  /* optloc-dependent freq offset */
749  if (selrf->rf.omegawave.res > 0) {
750  iphase_omega = -ks_scan_omegawave_hz(&selrf->rf.omegawave, i, hertzPerMm * tloc /* [Hz] */, selrf->rf.omega_iwave_phase_at_iso);
751  } else {
752  freqoffHz += hertzPerMm * tloc; /* [Hz] */
753  }
754 
755 #ifdef IPG
756  /* set RF frequency offset */
757  setfrequency((int)(freqoffHz / TARDIS_FREQ_RES), selrf->rf.rfwave.wfi[i].wf, selrf->rf.rfwave.wfi[i].boardinstance);
758 
759  /* calculate phase */
760  {
761  double ftime_delay; /* floating point time delay in seconds */
762  double temp_freq; /* frequency offset */
763  unsigned short tmpphase;
764  int syncpos, rfstartpos, time_delay;
765 
766  /* start of RF pulse wave form (for 1st boardinstance) */
767  rfstartpos = selrf->rf.rfwave.wfi[i].wf->inst_hdr_tail->start;
768  /* start of RF SSP frq pulse (for 1st boardinstance) */
769  syncpos = selrf->rf.rfwave.wfi[i].wf->assoc_pulse->inst_hdr_tail->start;
770 
771  /* time difference between SSP frq and start of RF pulse */
772  time_delay = rfstartpos - syncpos - 9 /* frq2sync_dly */;
773 
774  /*** Modified from GE's setupphases() ***/
775  ftime_delay = ((double)(time_delay + selrf->rf.start2iso)) / ((double) 1.0e6);
776  temp_freq = (double) freqoffHz;
777  tmpphase = ks_degrees_to_iphase(rfphase) /* deg->iphase */ + ks_cycles_to_iphase( - temp_freq * ftime_delay ) + iphase_omega; /* iphase */
778  setiphase((long)tmpphase, selrf->rf.rfwave.wfi[i].wf, selrf->rf.rfwave.wfi[i].boardinstance);
779  }
780 #else
781  (void)iphase_omega;
782 #endif
783  } /* for each instance */
784 } /* ks_scan_selrf_setfreqphase */
KS_TRAP grad
Definition: KSFoundation.h:1463
int boardinstance
Definition: KSFoundation.h:484
float cf_offset
Definition: KSFoundation.h:1030
int start2iso
Definition: KSFoundation.h:1032
KS_SEQLOC * locs
Definition: KSFoundation.h:752
unsigned short ks_scan_omegawave_hz(KS_WAVE *wave, int instanceno, float Hz, int omega_iwave_phase_at_null)
Updates a KS_WAVE object on the OMEGA board to produce a frequency offset
Definition: KSFoundation_tgt.c:56
KS_WAVE gradwave
Definition: KSFoundation.h:1465
int omega_iwave_phase_at_iso
Definition: KSFoundation.h:1040
STATUS ks_error(const char *format,...) __attribute__((format(printf
Common error message function for HOST and TGT
int ninst
Definition: KSFoundation.h:494
KS_SEQLOC * locs
Definition: KSFoundation.h:678
float ampscale
Definition: KSFoundation.h:464
KS_WFINSTANCE * wfi
Definition: KSFoundation.h:680
KS_WFINSTANCE * wfi
Definition: KSFoundation.h:754
KS_SEQLOC loc
Definition: KSFoundation.h:486
KS_RF rf
Definition: KSFoundation.h:1454
KS_DESCRIPTION description
Definition: KSFoundation.h:745
KS_WAVE rfwave
Definition: KSFoundation.h:1037
KS_BASE base
Definition: KSFoundation.h:744
WF_PULSE * wf
Definition: KSFoundation.h:485
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:448
unsigned short ks_cycles_to_iphase(double cycles)
Returns the integer phase (internal use)
Definition: KSFoundation_common.c:367
KS_WAVE omegawave
Definition: KSFoundation.h:1038
int ngenerated
Definition: KSFoundation.h:496
unsigned short ks_degrees_to_iphase(double degrees)
Definition: KSFoundation_common.c:379
int res
Definition: KSFoundation.h:746
float slthick
Definition: KSFoundation.h:1456
float bw
Definition: KSFoundation.h:1029

◆ 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
611  {
612 
613  int firstinstance, lastinstance, j;
614  int phaseIndex = 1;
615  KS_WAVE newwave = KS_INIT_WAVE;
616  float locOffset;
617  float phaseIncrement;
618 #ifdef IPG
619  int i;
620  int numplaced = selrf->rf.rfwave.base.ngenerated;
621 #else
622  int numplaced = selrf->rf.rfwave.base.ninst;
623 #endif
624 
625  if (instanceno == INSTRALL) {
626  firstinstance = 0;
627  lastinstance = numplaced - 1;
628  } else {
629  firstinstance = instanceno;
630  lastinstance = instanceno;
631  }
632 
633  if (numplaced == 0) {
634  return;
635  }
636 
637  if (firstinstance < 0 || lastinstance >= numplaced) {
638  ks_error("ks_scan_selrf_setfreqphase_pins(%s): instanceno (%d) out of range [0,%d]", selrf->rf.rfwave.description, instanceno, numplaced - 1);
639  return;
640  }
641 
642  newwave = selrf->rf.thetawave;
643 
644  locOffset = sliceinfo.optloc;
645  phaseIncrement = (360.0 / sms_slice_gap) * locOffset;
646 
647  /* If the MB-factor is even, center the slice group */
648  if (sms_multiband_factor % 2 == 0) {
649  phaseIncrement += 180.0;
650  }
651 
652  for (j = 0; j < selrf->rf.thetawave.res; j++) {
653 
654  if (!areSame(selrf->rf.rfwave.waveform[j], 0.0)) {
655  newwave.waveform[j] = selrf->rf.thetawave.waveform[j] + ((float)phaseIndex * phaseIncrement) + rfphase;
656  }
657 
658  if (j < selrf->rf.thetawave.res - 1) {
659  if (!areSame(selrf->rf.rfwave.waveform[j], 0.0) && areSame(selrf->rf.rfwave.waveform[j + 1], 0.0)) {
660  phaseIndex++;
661  }
662  }
663  }
664 
665 #ifdef IPG
666 
667  for (i = firstinstance; i <= lastinstance; i++) {
668  /* set RF frequency & phase offset to zero */
669  setfrequency(0, selrf->rf.rfwave.wfi[i].wf, selrf->rf.rfwave.wfi[i].boardinstance);
670  setphase(0.0, selrf->rf.rfwave.wfi[i].wf, selrf->rf.rfwave.wfi[i].boardinstance);
671 
672  /* move modified thetawave to hardware */
673  ks_scan_wave2hardware(&selrf->rf.thetawave, newwave.waveform, (selrf->rf.thetawave.current_state[i])+1 % selrf->rf.thetawave.base.nstates);
674  }
675 
676 #endif
677 
678 } /* ks_scan_selrf_setfreqphase_pins */
int boardinstance
Definition: KSFoundation.h:484
#define areSame(a, b)
Definition: KSFoundation.h:144
Core sequence object making arbitrary waveforms on any board (using float data format)
Definition: KSFoundation.h:743
#define KS_INIT_WAVE
Definition: KSFoundation.h:295
STATUS ks_error(const char *format,...) __attribute__((format(printf
Common error message function for HOST and TGT
int ninst
Definition: KSFoundation.h:494
void ks_scan_wave2hardware(KS_WAVE *wave, const KS_WAVEFORM newwaveform, int state)
Writes over an existing waveform state in sequencer memory
Definition: KSFoundation_tgt.c:876
KS_WFINSTANCE * wfi
Definition: KSFoundation.h:754
KS_WAVE thetawave
Definition: KSFoundation.h:1039
KS_RF rf
Definition: KSFoundation.h:1454
KS_DESCRIPTION description
Definition: KSFoundation.h:745
KS_WAVE rfwave
Definition: KSFoundation.h:1037
KS_BASE base
Definition: KSFoundation.h:744
WF_PULSE * wf
Definition: KSFoundation.h:485
int nstates
Definition: KSFoundation.h:495
int * current_state
Definition: KSFoundation.h:762
int ngenerated
Definition: KSFoundation.h:496
int res
Definition: KSFoundation.h:746
KS_WAVEFORM waveform
Definition: KSFoundation.h:748

◆ 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]
789  {
790  int firstinstance, lastinstance;
791  #ifdef IPG
792  int i;
793  int numplaced = rf->rfwave.base.ngenerated;
794  #else
795  int numplaced = rf->rfwave.base.ninst;
796  #endif
797 
798  if (instanceno == INSTRALL) {
799  firstinstance = 0;
800  lastinstance = numplaced - 1;
801  } else {
802  firstinstance = instanceno;
803  lastinstance = instanceno;
804  }
805 
806  if (numplaced == 0) {
807  return;
808  }
809  if (firstinstance < 0 || lastinstance >= numplaced) {
810  ks_error("%s(%s): instanceno (%d) out of range [0,%d]", __FUNCTION__, rf->rfwave.description, instanceno, numplaced - 1);
811  return;
812  }
813 
814  #ifdef IPG
815  /* set frequency */
816  for (i = firstinstance; i <= lastinstance; i++) {
817  /* calculate phase */
818  {
819  double ftime_delay; /* floating point time delay in seconds */
820  double cf_offset = (double) rf->cf_offset; /* frequency offset */
821  unsigned short tmpphase;
822  int syncpos, rfstartpos, time_delay;
823 
824  /* start of RF pulse wave form (for 1st boardinstance) */
825  rfstartpos = rf->rfwave.wfi[i].wf->inst_hdr_tail->start;
826  /* start of RF SSP frq pulse (for 1st boardinstance) */
827  syncpos = rf->rfwave.wfi[i].wf->assoc_pulse->inst_hdr_tail->start;
828 
829  /* time difference between SSP frq and isocenter of RF pulse */
830  time_delay = rfstartpos - syncpos - 9 /* frq2sync_dly */ + RUP_FACTOR(rf->rfwave.duration - rf->iso2end, 2);
831 
832  /*** Modified from GE's setupphases() ***/
833  ftime_delay = ((double)time_delay) / ((double) 1.0e6);
834  tmpphase = ks_degrees_to_iphase(rfphase) /* deg->iphase */ + ks_cycles_to_iphase( - cf_offset * ftime_delay ); /* iphase */
835  setiphase((long)tmpphase, rf->rfwave.wfi[i].wf, rf->rfwave.wfi[i].boardinstance);
836  }
837 
838  } /* for each instance */
839 
840  #endif
841 
842 
843 } /* ks_scan_rf_setphase */
int boardinstance
Definition: KSFoundation.h:484
float cf_offset
Definition: KSFoundation.h:1030
STATUS ks_error(const char *format,...) __attribute__((format(printf
Common error message function for HOST and TGT
int ninst
Definition: KSFoundation.h:494
KS_WFINSTANCE * wfi
Definition: KSFoundation.h:754
KS_DESCRIPTION description
Definition: KSFoundation.h:745
KS_WAVE rfwave
Definition: KSFoundation.h:1037
KS_BASE base
Definition: KSFoundation.h:744
int iso2end
Definition: KSFoundation.h:1033
WF_PULSE * wf
Definition: KSFoundation.h:485
unsigned short ks_cycles_to_iphase(double cycles)
Returns the integer phase (internal use)
Definition: KSFoundation_common.c:367
int ngenerated
Definition: KSFoundation.h:496
unsigned short ks_degrees_to_iphase(double degrees)
Definition: KSFoundation_common.c:379
int duration
Definition: KSFoundation.h:747

◆ ks_scan_wave2hardware()

void ks_scan_wave2hardware ( KS_WAVE wave,
const KS_WAVEFORM  newwaveform,
int  state 
)

Writes over an existing waveform state in sequencer memory

scan_wave2hardware.svg
graphical description

This function copies a waveform to the iwave buffer on the sequencer associated with the specified wave state. In order to use this function multiple wave states are required, these are added using ks_pg_addwavestate. During run time, it is the responsibility of the programmer to not copy a waveform into the currently active wave state. This is because the sequencer and your psd-code are asynchronous and this function writes into the sequence immediately. If the KS_WAVE is currently playing this could trip you scanner! Note: This function affects all instructions (instances) pointing to this state.

Example

@ipgexport (HOST)
@host
cveval() {
ks_eval_wave(&mywave, "mywavedesc", res, duration, some_floatarray);
}
@pg
tmploc.pos = 10ms;
tmploc.board = XGRAD;
ks_pg_wave(&mywave, tmploc, &seqctrl); // Same wave object *can* be placed on multiple boards (but not recommended as the units differ between gradient and RF/OMEGA boards)
ks_pg_addwaveformstate(mywave, some_other_waveform, 1); // We add state 1, we will use this as our buffer later
@rsp (TGT)
scan() {
float * your_waveform = (float *) AllocNode(1024 * sizeof(float)); // allocate some memory
make_your_waveform(your_waveform, some_inputs); // create a float array
const int new_state = (mywave.current_state+1) % mywave.base.nstates; // safely fetch the inactive state (for nstate=2 -> 1010101010.. etc)
ks_scan_wave2hardware(&mywave, your_waveform, new_state); // replace the waveform at the address of <new_state> (this affects all instances pointing to this state)
ks_scan_setwavestate(&mywave, new_state, INSTRALL); // change all instances of the instruction addresses to point to the <new_state> (so it plays out on the next time the seqctrl executes)
FreeNode(your_waveform); // free the memory on the heap
}
Parameters
[in,out]wavePointer to KS_WAVE
[in]newwaveformKS_WAVEFORM to copy to hardware (if NULL, the .waveform field in KS_WAVE will be used instead)
[in]stateThe wave state to write the waveform into; this can't be equal to the currently active wavestate
876  {
877 #ifndef IPG
878  if(wave->max_amp < ks_waveform_absmax(newwaveform, wave->res)) {
879  ks_error("%s [%s]: can't add a newwaveform state with a larger amplitude than state0.", __FUNCTION__, wave->description);
880  return;
881  }
882 
883  float slew_max = ks_waveform_maxslew(newwaveform, wave->res, wave->duration, NULL);
884  /* times > 1 in case of float rounding */
885  extern float cfxfs, cfyfs, cfzfs;
886  extern int cfrmp2xfs, cfrmp2yfs, cfrmp2zfs;
887  int i;
888  float slew_limit = FMin(3, cfxfs, cfyfs, cfzfs)/IMax(3,cfrmp2xfs,cfrmp2yfs,cfrmp2zfs)* 1.00001;
889  /* float slew_limit = ks_syslimits_slewrate1(loggrd) * 1.00001; */
890  for (i = 0; i < ks_numplaced(&wave->base); i++) {
891  KS_SEQLOC loc = wave->locs[i];
892  int isGrad = (loc.board == XGRAD || loc.board == YGRAD || loc.board == ZGRAD);
893  if ((isGrad) && (slew_max > slew_limit)) {
894  KS_THROW("[%s]: state %i exceeds the least conservative gradient slew rate limit. %f > %f",
895  wave->description, state, slew_max, slew_limit);
896  return;
897  }
898  }
899  if(state > wave->base.nstates) {
900  KS_THROW("This state (%d) was not pulsegen'd", state);
901  return;
902  }
903 
904 #endif
905 
906 #ifdef IPG
907  /* wave -> short int wave for hardware use. All waveforms but THETA are autoscaled to +/- max_pg_wamp (32766)*/
908  WF_PULSE * wfpulse;
909  int board;
910  short * iwave = (short *) AllocNode(wave->res * sizeof(short));
911  int skippedboards = 0;
912  int i;
913  for (i = 0; i < ks_numplaced(&wave->base); i++) {
914  if(state == wave->current_state[i]) {
915  ks_error("Can't move a waveform to the currently active state, instance = %i!", i);
916  return;
917  }
918  }
919 
920  const int board_instance = 0;
921  for(board = 0; board < 7; board++) {
922  wfpulse = wave->wfpulse[board];
923  if (!wfpulse) {
924  skippedboards++;
925  continue;
926  }
927  float max_amp = 0.0;
928  if (board == RHO) {
929  max_amp = 1.0;
930  } else {
931  max_amp = wave->abs_max_amp;
932  }
933 
934  /* Here we adjust the iwave amplitude to achieve the correct final amplitude iamp*iwave*/
935  if (ks_waveform2iwave(iwave,
936  newwaveform,
937  wave->res,
938  board,
939  max_amp,
940  wave->fs_factor) == FAILURE)
941  return;
942  LONG wave_ptr = 0;
943  getwave(&wave_ptr, &wfpulse[0]); /* Get current ptr */
944  setwave(wave->wave_ptrs[state], &wfpulse[0], board_instance); /* Load state ptr */
945  movewaveimm(iwave, &wfpulse[0], (int) board_instance, wave->res, TOHARDWARE); /* Write to wave to state ptr */
946  setwave(wave_ptr, &wfpulse[0], board_instance); /* Restore current ptr */
947  }
948  FreeNode(iwave);
949 
950  if (skippedboards == 7) {
951  ks_error("%s [%s] -> state %d: no WF_PULSES have been allocated, something is very wrong!", __FUNCTION__, wave->description, state);
952  return;
953  }
954 
955 #endif
956 
957 } /* ks_scan_wave2hardware */
float fs_factor
Definition: KSFoundation.h:756
float ks_waveform_maxslew(const KS_WAVEFORM waveform, int res, int duration, int *index)
Returns the maximum absolute value in a KS_WAVEFORM
Definition: KSFoundation_common.c:1366
KS_SEQLOC * locs
Definition: KSFoundation.h:752
STATUS ks_error(const char *format,...) __attribute__((format(printf
Common error message function for HOST and TGT
int board
Definition: KSFoundation.h:462
float abs_max_amp
Definition: KSFoundation.h:761
KS_DESCRIPTION description
Definition: KSFoundation.h:745
LONG * wave_ptrs
Definition: KSFoundation.h:763
WF_PULSE ** wfpulse
Definition: KSFoundation.h:753
int ks_numplaced(KS_BASE *base)
ADDTITLEHERE
Definition: KSFoundation_common.c:4838
KS_BASE base
Definition: KSFoundation.h:744
float max_amp
Definition: KSFoundation.h:759
typedef struct used as argument to ks_pg_*** functions to control where and when to place a sequence ...
Definition: KSFoundation.h:461
STATUS ks_waveform2iwave(KS_IWAVE iwave, const KS_WAVEFORM waveform, int res, int board, float max, float fs_factor) WARN_UNUSED_RESULT
(Internal use) Conversion of a KS_WAVEFORM to a short int array for use on hardware
Definition: KSFoundation_common.c:1698
int nstates
Definition: KSFoundation.h:495
int * current_state
Definition: KSFoundation.h:762
float ks_waveform_absmax(const KS_WAVEFORM waveform, int res)
Returns the maximum absolute value in a KS_WAVEFORM
Definition: KSFoundation_common.c:1336
#define KS_THROW(format,...)
Definition: KSFoundation.h:181
int res
Definition: KSFoundation.h:746
int duration
Definition: KSFoundation.h:747

◆ ks_scan_setwavestate()

void ks_scan_setwavestate ( KS_WAVE wave,
int  state,
int  instance 
)

Change the address of an instruction instance to point to a different state

scan_setwavestate.svg
graphical description

During the ks_pg_addwaveform state calls we keep track of the address of each new waveform that was added. This function then allows you to switch to a new waveform by simply changing the address in the intstruction. Be aware that each instance of the KS_WAVE has a unique instruction. This allows you change wave_state on an instance granularity level.

Parameters
[in]wavePointer to a KS_WAVE sequence object
[in]stateThe new state to switch to (previously added by ks_pg_addwaveformstate)
[in]instanceWhich instance you would like to change state (INSTRALL affects all)
845  {
846 
847 
848  if (ks_numplaced(&wave->base) == 0) {
849  return;
850  }
851 
852 #ifdef IPG
853  setwave(wave->wave_ptrs[state], wave->wfi[instance].wf, wave->wfi[instance].boardinstance);
854 #else
855  if (state < 0) {
856  KS_THROW("%s: tried to set to negative state: %i", wave->description, state);
857  } else if (state >= KS_WAVE_MAXNSTATES) {
858  KS_THROW("%s: invalid state: %d > %d ", wave->description, state, KS_WAVE_MAXNSTATES-1);
859  } else if (wave->p_waveformstates[state] == NULL || state >= wave->base.nstates) {
860  KS_THROW("%s: invalid state %d. Did you forget to call ks_pg_addwaveformstate() ?", wave->description, state);
861  }
862 
863  if (instance < 0) {
864  KS_THROW("%s: tried to modify negative instance: %i [this function is not compatible with INSTRALL(=%d)]", wave->description, instance, INSTRALL);
865  } else if (instance > ks_numplaced(&wave->base)) {
866  KS_THROW("%s: Invalid instance: %d > %d", wave->description, instance, ks_numplaced(&wave->base)-1);
867  }
868 
869  wave->rtscaling.num_instances = ks_numplaced(&wave->base);
870  ks_rt_scale_log_set_state(&wave->rtscaling, instance, state);
871 #endif
872  wave->current_state[instance] = state;
873 
874 } /* ks_scan_setwavestate */
int boardinstance
Definition: KSFoundation.h:484
KS_RT_SCALE_LOG rtscaling
Definition: KSFoundation.h:755
KS_WFINSTANCE * wfi
Definition: KSFoundation.h:754
int num_instances
Definition: KSFoundation.h:476
KS_DESCRIPTION description
Definition: KSFoundation.h:745
LONG * wave_ptrs
Definition: KSFoundation.h:763
int ks_numplaced(KS_BASE *base)
ADDTITLEHERE
Definition: KSFoundation_common.c:4838
KS_BASE base
Definition: KSFoundation.h:744
#define KS_WAVE_MAXNSTATES
Definition: KSFoundation.h:293
WF_PULSE * wf
Definition: KSFoundation.h:485
void ks_rt_scale_log_set_state(KS_RT_SCALE_LOG *log, int instance_idx, int state)
Definition: KSFoundation_common.c:4929
int nstates
Definition: KSFoundation.h:495
int * current_state
Definition: KSFoundation.h:762
float * p_waveformstates[KS_WAVE_MAXNSTATES]
Definition: KSFoundation.h:749
#define KS_THROW(format,...)
Definition: KSFoundation.h:181

◆ 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
961  {
962  /* Warning: We never have more than one instance (i.e. instruction) per readtrap.echo[i] (see ks_pg_readtrap())
963  unlike KS_TRAP and KS_RF. This means that the 2nd input arg here ('instanceno') means the instanceno'th echo. */
964 
965 #ifdef IPG
966  const int numplaced = readtrap->acq.base.ngenerated;
967 #else
968  const int numplaced = readtrap->acq.base.ninst;
969 #endif
970 
971  int firstreadout, lastreadout;
972  if (instanceno == INSTRALL) {
973  firstreadout = 0;
974  lastreadout = numplaced - 1;
975  } else {
976  firstreadout = instanceno;
977  lastreadout = instanceno;
978  }
979 
980  if (numplaced == 0) {
981  return;
982  }
983 
984  if (firstreadout < 0 || lastreadout >= numplaced) {
985  ks_error("ks_scan_offsetfov(%s): readout instance (%d) out of range [0,%d]", readtrap->grad.description, instanceno, numplaced - 1);
986  return;
987  }
988 
989  int i;
990  for (i = firstreadout; i <= lastreadout; i++) {
991 #ifdef IPG
992  /* gradient amplitude for this instance -> Hz */
993  const float ampscale = readtrap->grad.wfi[i].loc.ampscale;
994  float rloc = sliceinfo.oprloc;
995 #else
996  const float ampscale = readtrap->grad.locs[i].ampscale;
997  float rloc = 1;
998 #endif
999 
1000  const double hertz_per_mm = (readtrap->grad.amp * ampscale) * GAM / 10.0; /* [Hz/mm] = amp [G/cm] * GAM [Hz/G] / 10.0 [cm->mm] */
1001 
1002  /* iphase_omega for rampsampled cases includes the entire ramp up (+acqdelay) */
1003  unsigned short iphase_omega = 0;
1004  if (readtrap->omega.duration > 0 && !areSame(ampscale, 0.0)) {
1005  iphase_omega = -ks_scan_omegawave_hz(&readtrap->omega, i, hertz_per_mm * rloc /* [Hz] */, readtrap->omega_iwave_phase_at_echo);
1006  }
1007 
1008 #ifdef IPG
1009  const int gradsign = (hertz_per_mm >= 0) ? 1 : -1;
1010  /* Frequency offsets [Hz]:
1011  - cfreceiveroffsetfreq: GE's standard frequency offset
1012  - readtrap->freqoffHz: Additional frequency offset for current KS_READTRAP. This value is multiplied with the gradient polarity sign
1013  - hertz_per_mm * sliceinfo.oprloc: Slice-dependent and gradient polarity dependent frequency offset (when omega is off) */
1014  const double freqoffHz = cfreceiveroffsetfreq
1015  + readtrap->freqoffHz * gradsign
1016  + (readtrap->omega.duration == 0) * (hertz_per_mm * rloc); /* [Hz] */
1017 
1018  /* set readout frequency offset */
1019  if (i < readtrap->acq.base.ngenerated && readtrap->acq.echo[i].tag == SSPDAB) { /* make sure it has been set up properly */
1020  setfrequency((int)(freqoffHz / TARDIS_FREQ_RES), &readtrap->acq.echo[i], 0);
1021  }
1022  /* set phase offset */
1023  if (i < readtrap->acq.base.ngenerated && readtrap->acq.echo[i].tag == SSPDAB) {
1024 
1025  /* The period of time between when the oscillator starts accumulating phase and
1026  the centre of the first ADC sample is XTRSETLNG (132 us) + XTR_TAIL (5 us). XTRSETLNG is
1027  used in the placement of the XTR, DAB and RBA messages in acqq_longdab/acqq.
1028  If the pulse placement in the acqq functions are modified from the GE defaults,
1029  this timing is no longer valid! */
1030  int freqstart2readstart = XTRSETLNG + XTR_TAIL + 2 - (int)(readtrap->acq.filt.tsp / 2.0f);
1031  int readstart2echo = readtrap->time2center - readtrap->acqdelay;
1032 
1033  const unsigned short iphase_due_to_freqoffset = ks_cycles_to_iphase(- freqoffHz * (freqstart2readstart + readstart2echo) * 1e-6);
1034  const unsigned short iphase_ky = ks_cycles_to_iphase(- ky * sliceinfo.opphasoff / readtrap->fov);
1035  const unsigned short iphase_kz = ks_cycles_to_iphase(- kz * sliceinfo.optloc / readtrap->fov);
1036  const unsigned short ircvphase = ks_degrees_to_iphase(rcvphase);
1037  unsigned short iphase = (iphase_ky + iphase_kz + ircvphase + iphase_due_to_freqoffset + iphase_omega);
1038  setiphase((long)iphase, &readtrap->acq.echo[i], 0);
1039 
1040  }
1041 #else
1042  (void)iphase_omega;
1043 #endif
1044 
1045  } /* for */
1046 } /* ks_scan_offsetfov_iso */
WF_PULSE * echo
Definition: KSFoundation.h:846
#define areSame(a, b)
Definition: KSFoundation.h:144
unsigned short ks_scan_omegawave_hz(KS_WAVE *wave, int instanceno, float Hz, int omega_iwave_phase_at_null)
Updates a KS_WAVE object on the OMEGA board to produce a frequency offset
Definition: KSFoundation_tgt.c:56
STATUS ks_error(const char *format,...) __attribute__((format(printf
Common error message function for HOST and TGT
int ninst
Definition: KSFoundation.h:494
float fov
Definition: KSFoundation.h:1551
KS_WAVE omega
Definition: KSFoundation.h:1562
KS_SEQLOC * locs
Definition: KSFoundation.h:678
int cfreceiveroffsetfreq
KS_TRAP grad
Definition: KSFoundation.h:1561
float ampscale
Definition: KSFoundation.h:464
KS_BASE base
Definition: KSFoundation.h:839
KS_WFINSTANCE * wfi
Definition: KSFoundation.h:680
int omega_iwave_phase_at_echo
Definition: KSFoundation.h:1564
KS_SEQLOC loc
Definition: KSFoundation.h:486
float amp
Definition: KSFoundation.h:669
KS_DESCRIPTION description
Definition: KSFoundation.h:668
FILTER_INFO filt
Definition: KSFoundation.h:843
unsigned short ks_cycles_to_iphase(double cycles)
Returns the integer phase (internal use)
Definition: KSFoundation_common.c:367
float freqoffHz
Definition: KSFoundation.h:1557
int ngenerated
Definition: KSFoundation.h:496
unsigned short ks_degrees_to_iphase(double degrees)
Definition: KSFoundation_common.c:379
KS_READ acq
Definition: KSFoundation.h:1549
int time2center
Definition: KSFoundation.h:1560
int acqdelay
Definition: KSFoundation.h:1555
int duration
Definition: KSFoundation.h:747

◆ 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
1126  {
1127  const double koffset = (double)(ky) / phasefovratio;
1128  ks_scan_offsetfov_iso(readtrap, instanceno, sliceinfo, koffset, 0, rcvphase);
1129 }
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:961

◆ 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
1134  {
1135  const double kyoffset = (double)(ky) / phasefovratio;
1136  const double kzoffset = (double)(kz) / zphasefovratio;
1137  ks_scan_offsetfov_iso(readtrap, instanceno, sliceinfo, kyoffset, kzoffset, rcvphase);
1138 }
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:961

◆ ks_scan_offsetfov3D_readwave()

void ks_scan_offsetfov3D_readwave ( KS_READWAVE readwave,
int  instanceno,
SCAN_INFO  sliceinfo,
float  ky,
float  phasefovratio,
float  kz,
float  zphasefovratio,
float  rcvphase 
)

ADDTITLEHERE

1151  {
1152  const double koffset = (double)(ky) / phasefovratio;
1153  const double kzoffset = (double)(kz) / zphasefovratio;
1154  ks_scan_offsetfov_iso_readwave(readwave, instanceno, sliceinfo, koffset, kzoffset, rcvphase);
1155 }
void ks_scan_offsetfov_iso_readwave(KS_READWAVE *readwave, int instanceno, SCAN_INFO sliceinfo, double ky, double kz, double rcvphase)
ADDTITLEHERE
Definition: KSFoundation_tgt.c:1049

◆ ks_scan_offsetfov_iso_readwave()

void ks_scan_offsetfov_iso_readwave ( KS_READWAVE readwave,
int  instanceno,
SCAN_INFO  sliceinfo,
double  ky,
double  kz,
double  rcvphase 
)

ADDTITLEHERE

1049  {
1050  KS_READ * read = &readwave->acq;
1051  const int numplaced = ks_numplaced(&read->base);
1052 
1053  if (numplaced == 0) {
1054  return;
1055  }
1056 
1057  int firstgrad, lastgrad;
1058  if (instanceno == INSTRALL) {
1059  firstgrad = 0;
1060  lastgrad = numplaced - 1;
1061  } else {
1062  firstgrad = instanceno;
1063  lastgrad = instanceno;
1064  }
1065 
1066  if (firstgrad < 0 || lastgrad >= numplaced) {
1067  ks_error("%s: readwave gradinstance (%d) out of range [0,%d]", __FUNCTION__, instanceno, numplaced - 1);
1068  return;
1069  }
1070 
1071  int i;
1072  for (i = firstgrad; i <= lastgrad; i++) {
1073 #ifdef IPG
1074  /* gradient amplitude for this instance -> Hz */
1075  const float ampscale = readwave->grad.wfi[i].loc.ampscale;
1076  const float rloc = sliceinfo.oprloc;
1077 #else
1078  const float ampscale = readwave->grad.locs[i].ampscale;
1079  const float rloc = 1;
1080 #endif
1081 
1082  const double hertz_per_mm = readwave->omega.abs_max_amp * ampscale; /* [Hz/mm] * ampscale (to match polarity of read gradient) */
1083  unsigned short iphase_omega = 0;
1084  iphase_omega = -ks_scan_omegawave_hz(&readwave->omega, i, hertz_per_mm * rloc /* [Hz] */, readwave->omega_iwave_phase_at_echo);
1085 
1086 #ifdef IPG
1087  const int gradsign = (ampscale >= 0) ? 1 : -1;
1088 
1089  /* Frequency offsets [Hz]:
1090  - cfreceiveroffsetfreq: GE's standard frequency offset
1091  - readwave->freqoffHz: Additional frequency offset for current KS_WAVE. This value is multiplied with the gradient polarity sign */
1092  const double freqoffHz = cfreceiveroffsetfreq
1093  + readwave->freqoffHz * gradsign;
1094 
1095  /* set readout frequency offset */
1096  if (i < read->base.ngenerated && read->echo[i].tag == SSPDAB) {/* make sure it has been set up properly */
1097  setfrequency((int)(freqoffHz / TARDIS_FREQ_RES), &read->echo[i], 0);
1098  }
1099 
1100  /* set phase offset */
1101  if (i < numplaced && read->echo[i].tag == SSPDAB) {
1102 
1103  /* The period of time between when the oscillator starts accumulating phase and
1104  the centre of the first ADC sample is XTRSETLNG (132 us) + XTR_TAIL (5 us). XTRSETLNG is
1105  used in the placement of the XTR, DAB and RBA messages in acqq_longdab/acqq.
1106  If the pulse placement in the acqq functions are modified from the GE defaults,
1107  this timing is no longer valid! */
1108  int freqstart2readstart = XTRSETLNG + XTR_TAIL + 2 - (int)(readwave->acq.filt.tsp / 2.0f);
1109  int readstart2echo = readwave->time2center - readwave->acqdelay;
1110 
1111  const unsigned short iphase_due_to_freqoffset = ks_cycles_to_iphase(- freqoffHz * (freqstart2readstart + readstart2echo) * 1e-6);
1112  const unsigned short iphase_ky = ks_cycles_to_iphase(- ky * sliceinfo.opphasoff / readwave->fov);
1113  const unsigned short iphase_kz = ks_cycles_to_iphase(- kz * sliceinfo.optloc / readwave->fov);
1114  const unsigned short ircvphase = ks_degrees_to_iphase(rcvphase);
1115  unsigned short iphase = (iphase_ky + iphase_kz + ircvphase + iphase_due_to_freqoffset + iphase_omega);
1116  setiphase(iphase, &read->echo[i], 0);
1117  }
1118 #else
1119  (void)iphase_omega;
1120 #endif
1121 
1122  } /* for */
1123 } /* ks_scan_offsetfov_iso_readwave */
float fov
Definition: KSFoundation.h:1610
WF_PULSE * echo
Definition: KSFoundation.h:846
KS_SEQLOC * locs
Definition: KSFoundation.h:752
unsigned short ks_scan_omegawave_hz(KS_WAVE *wave, int instanceno, float Hz, int omega_iwave_phase_at_null)
Updates a KS_WAVE object on the OMEGA board to produce a frequency offset
Definition: KSFoundation_tgt.c:56
STATUS ks_error(const char *format,...) __attribute__((format(printf
Common error message function for HOST and TGT
KS_WAVE omega
Definition: KSFoundation.h:1605
int time2center
Definition: KSFoundation.h:1615
int cfreceiveroffsetfreq
float freqoffHz
Definition: KSFoundation.h:1609
KS_WAVE grad
Definition: KSFoundation.h:1604
float ampscale
Definition: KSFoundation.h:464
KS_BASE base
Definition: KSFoundation.h:839
KS_WFINSTANCE * wfi
Definition: KSFoundation.h:754
float abs_max_amp
Definition: KSFoundation.h:761
KS_SEQLOC loc
Definition: KSFoundation.h:486
int ks_numplaced(KS_BASE *base)
ADDTITLEHERE
Definition: KSFoundation_common.c:4838
Core sequence object that handles a data acquisition window
Definition: KSFoundation.h:838
int omega_iwave_phase_at_echo
Definition: KSFoundation.h:1617
FILTER_INFO filt
Definition: KSFoundation.h:843
unsigned short ks_cycles_to_iphase(double cycles)
Returns the integer phase (internal use)
Definition: KSFoundation_common.c:367
int acqdelay
Definition: KSFoundation.h:1608
unsigned short ks_degrees_to_iphase(double degrees)
Definition: KSFoundation_common.c:379
KS_READ acq
Definition: KSFoundation.h:1606

◆ ks_scan_offsetfov_iso_readwave_oneread()

void ks_scan_offsetfov_iso_readwave_oneread ( KS_READWAVE readwave,
KS_READ read,
int  gradinstanceno,
int  readinstanceno,
SCAN_INFO  sliceinfo,
double  ky,
double  kz,
double  rcvphase 
)

ADDTITLEHERE

◆ ks_scan_offsetfov_readwave()

void ks_scan_offsetfov_readwave ( KS_READWAVE readwave,
int  instanceno,
SCAN_INFO  sliceinfo,
float  ky,
float  phasefovratio,
float  rcvphase 
)

ADDTITLEHERE

1143  {
1144  const double koffset = (double)(ky) / phasefovratio;
1145  ks_scan_offsetfov_iso_readwave(readwave, instanceno, sliceinfo, koffset, 0, rcvphase);
1146 }
void ks_scan_offsetfov_iso_readwave(KS_READWAVE *readwave, int instanceno, SCAN_INFO sliceinfo, double ky, double kz, double rcvphase)
ADDTITLEHERE
Definition: KSFoundation_tgt.c:1049

◆ ks_eval_append_blip()

int ks_eval_append_blip ( KS_WAVE wave,
const float  s,
const int  max_points,
const float  area 
)

ADDTITLEHERE

7229  {
7230  float* prev = &wave->waveform[wave->res -1];
7231  float start_amp = *prev;
7232 
7233  /* Calculate tb slightly less than specified slewrate */
7234  const int tb = RUP_GRD(ceil(start_amp / max_s));
7235  const float sb = start_amp/tb;
7236  const float Mb = tb * start_amp / 2.0f;
7237  const float Ma = area - Mb;
7238  const int ta = RUP_GRD(ceil(( -start_amp + sqrt(start_amp*start_amp + max_s*Ma)) / max_s));
7239  if (ta < 0) {
7240  return ks_error("Ops");
7241  }
7242  const float sa = Ma/(ta*ta) - 2*start_amp/ta;
7243 
7244  const int num_a = ta / GRAD_UPDATE_TIME;
7245  const int num_b = tb / GRAD_UPDATE_TIME;
7246  if ((2*num_a + num_b) > max_points) {
7247  return ks_error("%s: num_a (%d) + num_b (%d) exceeds max points (%d)", __FUNCTION__, num_a, num_b, max_points);
7248  }
7249  int idx;
7250  float c_area = 0;
7251  for (idx = 0; idx < num_a; idx++) {
7252  float* cur = prev + 1;
7253  *cur = *prev + sa*GRAD_UPDATE_TIME;
7254  c_area += GRAD_UPDATE_TIME * (*prev + *cur) / 2.0f;
7255  prev++;
7256  }
7257 
7258  for (idx = 0; idx < num_a; idx++) {
7259  float* cur = prev + 1;
7260  *cur = *prev - sa*GRAD_UPDATE_TIME;
7261  c_area += GRAD_UPDATE_TIME * (*prev + *cur) / 2.0f;
7262  prev++;
7263  }
7264  for (idx = 0; idx < num_b; idx++) {
7265  float* cur = prev + 1;
7266  *cur = *prev - sb*GRAD_UPDATE_TIME;
7267  c_area += GRAD_UPDATE_TIME * (*prev + *cur) / 2.0f;
7268  prev++;
7269  }
7270 
7271  wave->res += 2*num_a + num_b;
7272  wave->duration = wave->res * GRAD_UPDATE_TIME;
7273 
7274  return SUCCESS;
7275 }
STATUS ks_error(const char *format,...) __attribute__((format(printf
Common error message function for HOST and TGT
int res
Definition: KSFoundation.h:746
KS_WAVEFORM waveform
Definition: KSFoundation.h:748
int duration
Definition: KSFoundation.h:747

◆ ks_scan_omegatrap_hz()

unsigned short ks_scan_omegatrap_hz ( KS_TRAP trap,
int  instanceno,
float  Hz,
int  omega_iwave_phase_at_null 
)

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
[in]omega_iwave_phase_at_nullthe area of the integer waveform at the null point
Return values
iphasecorrection to be applied with XTR packet
108  {
109  int iamp;
110  float omega_scale = 256.0;
111  short iphase_omega = 0;
112 
113  iamp = (int) (Hz / (TARDIS_FREQ_RES * omega_scale * trp->fs_factor));
114  if (abs(iamp) > 32768) {
115  ks_error("ks_scan_omegatrap_hz(%s): the integer amplitude is too large (%d) for instance %d", trp->description, iamp, instanceno);
116  return 0;
117  }
118 
119 #ifdef IPG
120 
121  if (trp->wfi[instanceno].loc.board != OMEGA) {
122  ks_error("ks_scan_omegatrap_hz(%s): Wrong function - use ks_scan_trap_ampscale() KS_TRAPs on XGRAD, YGRAD, ZGRAD", trp->description);
123  return 0;
124  }
125 
126  /* input validation */
127  if (instanceno < 0 || instanceno >= trp->base.ngenerated) {
128  ks_error("ks_scan_omegatrap_hz(%s): instanceno (%d) out of range [0,%d)", trp->description, instanceno, trp->base.ngenerated);
129  return 0;
130  }
131 
132  if (setiampt((short) iamp, trp->wfi[instanceno].wf, trp->wfi[instanceno].boardinstance) != SUCCESS) {
133  ks_error("ks_scan_omegatrap_hz(%s): An error occurred calling 'setiampt()'", trp->description);
134  return 0;
135  }
136 
137 
138  int phase_accum = 0;
139  phase_accum = (omega_iwave_phase_at_null * iamp) >> 7;
140  /* Convert integer phase with 24-bit period to integer phase
141  with 16-bit period compatible with setiphase function */
142  iphase_omega = phase_accum >> 8;
143 
144 #else
145  int numplaced = trp->base.ninst;
146  if (!numplaced) {
147  return 0;
148  }
149 
150  trp->rtscaling.num_instances = numplaced;
151  ks_rt_scale_log_set(&trp->rtscaling, instanceno, Hz < 0 ? -1 : 1);
152 
153 #endif
154 
155  return iphase_omega;
156 
157 } /* ks_scan_omegatrap_hz */
STATUS ks_error(const char *format,...) __attribute__((format(printf
Common error message function for HOST and TGT
void ks_rt_scale_log_set(KS_RT_SCALE_LOG *log, int instance_idx, float ampscale)
Definition: KSFoundation_common.c:4890

◆ ks_scan_omegawave_hz()

unsigned short ks_scan_omegawave_hz ( KS_WAVE wave,
int  instanceno,
float  Hz,
int  omega_iwave_phase_at_null 
)

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
[in]omega_iwave_phase_at_nullthe area of the integer waveform at the null point
Return values
iphasecorrection to be applied with XTR packet
56  {
57  int iamp;
58  float omega_scale = 256.0;
59  unsigned short iphase_omega = 0;
60 
61  iamp = (int) (Hz / (TARDIS_FREQ_RES * omega_scale * wave->fs_factor));
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 0;
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 0;
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 0;
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 'setiamp()'", wave->description);
83  return 0;
84  }
85 
86  int phase_accum = 0;
87  phase_accum = (omega_iwave_phase_at_null * iamp) >> 7;
88  /* Convert integer phase with 24-bit period to integer phase with 16-bit period compatible with setiphase function */
89  iphase_omega = phase_accum >> 8;
90 
91 #else
92  int numplaced = wave->base.ninst;
93  if (!numplaced) {
94  return 0;
95  }
96 
97  wave->rtscaling.num_instances = numplaced;
98  ks_rt_scale_log_set(&wave->rtscaling, instanceno, Hz < 0 ? -1 : 1);
99 
100 #endif
101 
102  return iphase_omega;
103 
104 } /* ks_scan_omegawave_hz */
float fs_factor
Definition: KSFoundation.h:756
int boardinstance
Definition: KSFoundation.h:484
STATUS ks_error(const char *format,...) __attribute__((format(printf
Common error message function for HOST and TGT
int ninst
Definition: KSFoundation.h:494
int board
Definition: KSFoundation.h:462
KS_RT_SCALE_LOG rtscaling
Definition: KSFoundation.h:755
KS_WFINSTANCE * wfi
Definition: KSFoundation.h:754
KS_SEQLOC loc
Definition: KSFoundation.h:486
int num_instances
Definition: KSFoundation.h:476
KS_DESCRIPTION description
Definition: KSFoundation.h:745
KS_BASE base
Definition: KSFoundation.h:744
WF_PULSE * wf
Definition: KSFoundation.h:485
int ngenerated
Definition: KSFoundation.h:496
void ks_rt_scale_log_set(KS_RT_SCALE_LOG *log, int instance_idx, float ampscale)
Definition: KSFoundation_common.c:4890

◆ 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
161  {
162  int numplaced = ks_numplaced(&wait->base);
163  if (numplaced == 0) {
164  return;
165  }
166 
167  int i;
168 
169  /* input validation */
170  if (waitperiod < 0) {
171  ks_error("ks_scan_wait(%s): wait period %d is negative", wait->description, waitperiod);
172  return;
173  }
174  if (waitperiod < GRAD_UPDATE_TIME) {
175  waitperiod = GRAD_UPDATE_TIME;
176  }
177  if (waitperiod % GRAD_UPDATE_TIME) {
178  ks_error("ks_scan_wait(%s): waitperiod (3rd arg) on gradient boards must be divisible by 4", wait->description);
179  return;
180  }
181 
182 #ifdef IPG
183 
184  if (wait->wfi == NULL) {
185  ks_error("ks_scan_wait(%s): WF_INSTANCE of trap is NULL", wait->description);
186  }
187 
188  /* set amplitude(s) */
189  for (i = 0; i < numplaced; i++) {
190 
191  if (setperiod(waitperiod, wait->wfi[i].wf, wait->wfi[i].boardinstance) != SUCCESS) {
192  ks_error("ks_scan_wait(%s): An error occurred calling 'setperiod()'", wait->description);
193  return;
194  }
195  }
196 # else
197  wait->rtscaling.num_instances = numplaced;
198  for (i = 0; i < numplaced; i++) {
199  /* We use state to indicate the duration in µs instead of ampscaling since the former is an int */
200  ks_rt_scale_log_set_state(&wait->rtscaling, i, waitperiod);
201  }
202 
203 #endif
204 
205 } /* ks_scan_wait */
int boardinstance
Definition: KSFoundation.h:484
STATUS ks_error(const char *format,...) __attribute__((format(printf
Common error message function for HOST and TGT
KS_DESCRIPTION description
Definition: KSFoundation.h:544
KS_RT_SCALE_LOG rtscaling
Definition: KSFoundation.h:550
int num_instances
Definition: KSFoundation.h:476
KS_BASE base
Definition: KSFoundation.h:543
int ks_numplaced(KS_BASE *base)
ADDTITLEHERE
Definition: KSFoundation_common.c:4838
KS_WFINSTANCE * wfi
Definition: KSFoundation.h:549
WF_PULSE * wf
Definition: KSFoundation.h:485
void ks_rt_scale_log_set_state(KS_RT_SCALE_LOG *log, int instance_idx, int state)
Definition: KSFoundation_common.c:4929

◆ 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
306  {
307  int firstinstance, lastinstance, i;
308 #ifdef IPG
309  int iamp;
310  float gradmax;
311  int numplaced = trp->base.ngenerated;
312 #else
313  int numplaced = trp->base.ninst;
314  if (!numplaced) {
315  return;
316  }
317 
318  trp->rtscaling.num_instances = numplaced;
319 
320 #endif
321 
322  if (instanceno == INSTRALL) {
323  firstinstance = 0;
324  lastinstance = numplaced - 1;
325  } else {
326  firstinstance = instanceno;
327  lastinstance = instanceno;
328  }
329 
330  /* input validation */
331  if (numplaced == 0) {
332  return;
333  }
334  if (firstinstance < 0 || lastinstance >= numplaced) {
335  ks_error("ks_scan_trap_ampscale(%s): instanceno (%d) out of range [0,%d]", trp->description, instanceno, numplaced - 1);
336  return;
337  }
338 
339  /* Allow ampscale to be 20% larger than 1. This, to allow future minor runtime changes of trapezoid amplitudes
340  for e.g. (future possible implementations):
341  - oblique ghost correction for EPI, where odd/even blip amps need to be made smaller/larger depending on slice angle and delays
342  - eddy current correction for DW-EPI, where readout lobe amp needs to be changed per diffusion direction etc.
343  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
344  under the very limit of the system (and the dB/dt limits for PNS). Use with caution. */
345 
346  if (fabs(ampscale) > sqrt(3)) {
347  ks_error("ks_scan_trap_ampscale(%s): ampscale too large (%f)", trp->description, ampscale);
348  return;
349  }
350 
351  /*
352  - trp->amp: The amplitude that was initially designed for the KS_TRAP. Should never be exceeded, or slew rate issues will occur
353  - trp->wfi[i].loc.ampscale: -1.0 -> +1.0. Amp scale factor for each instance of the KS_TRAP placed in the sequence.
354  - ampscale (3rd input arg): -1.0 -> +1.0 (in normal cases). The dynamic amp scale (i.e. over time, or TRs).
355  The product [trp->wfi[i].loc.ampscale * ampscale] should be in range [-1, 1] to avoid slew rate issues.
356  */
357 
358 #ifdef IPG
359 
360  if (trp->wfi == NULL) {
361  ks_error("ks_scan_trap_ampscale(%s): WF_INSTANCE of trap is NULL", trp->description);
362  return;
363  }
364 
365  /* set amplitude(s) */
366  for (i = firstinstance; i <= lastinstance; i++) {
367  gradmax = ks_syslimits_gradtarget(loggrd, trp->wfi[i].loc.board);
368  iamp = (int)((ampscale * trp->wfi[i].loc.ampscale * trp->amp / (gradmax * trp->fs_factor)) * MAX_PG_IAMP);
369 
370  if (abs(iamp) > 32768) {
371  ks_error("ks_scan_trap_ampscale(%s): the integer amplitude is too large (%d) (ampscale %1.5f, amplitude %1.5f G/cm, boardmax %1.5f, fs_factor %1.5f), for instance %d", trp->description, iamp, ampscale, trp->wfi[i].loc.ampscale * trp->amp, gradmax, trp->fs_factor, i);
372  return;
373  }
374  if (trp->wfi[i].loc.board == OMEGA) {
375  ks_error("ks_scan_trap_ampscale(%s): Instance %d on board OMEGA. Please use ks_scan_omegatrap_hz() instead", trp->description, i);
376  return;
377  }
378  if (setiampt((short) iamp, trp->wfi[i].wf, trp->wfi[i].boardinstance) != SUCCESS) {
379  ks_error("ks_scan_trap_ampscale(%s): An error occurred calling 'setiampt()'", trp->description);
380  return;
381  }
382 
383  }
384 
385 #else
386  for (i = firstinstance; i <= lastinstance; i++) {
387  ks_rt_scale_log_set(&trp->rtscaling, i, ampscale);
388  }
389 #endif
390 } /* 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:264
STATUS ks_error(const char *format,...) __attribute__((format(printf
Common error message function for HOST and TGT
LOG_GRAD loggrd
void ks_rt_scale_log_set(KS_RT_SCALE_LOG *log, int instance_idx, float ampscale)
Definition: KSFoundation_common.c:4890

◆ ks_scan_wave_ampscale()

void ks_scan_wave_ampscale ( KS_WAVE wave,
int  instanceno,
float  ampscale 
)

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

This function multiplies one instance of a KS_WAVE 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_WAVE object is the multiplication of the three factors:

  1. The designed amplitude [G/cm] in the KS_WAVEFORM array
  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]wavePointer to KS_WAVE
[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
209  {
210  int firstinstance, lastinstance, i;
211 #ifdef IPG
212  int iamp;
213  float gradmax;
214  int numplaced = wave->base.ngenerated;
215 #else
216  int numplaced = wave->base.ninst;
217  if (!numplaced) {
218  return;
219  }
220 
221  wave->rtscaling.num_instances = numplaced;
222 
223 #endif
224 
225  if (instanceno == INSTRALL) {
226  firstinstance = 0;
227  lastinstance = numplaced - 1;
228  } else {
229  firstinstance = instanceno;
230  lastinstance = instanceno;
231  }
232 
233  /* input validation */
234  if (numplaced == 0) {
235  return;
236  }
237  if (firstinstance < 0 || lastinstance >= numplaced) {
238  ks_error("ks_scan_wave_ampscale(%s): instanceno (%d) out of range [0,%d]", wave->description, instanceno, numplaced - 1);
239  return;
240  }
241 
242  if (fabs(ampscale) > 1.0) {
243  ks_error("ks_scan_wave_ampscale(%s): ampscale too large (%f)", wave->description, ampscale);
244  return;
245  }
246 
247 #ifdef IPG
248 
249  if (wave->wfi == NULL) {
250  ks_error("ks_scan_wave_ampscale(%s): WF_INSTANCE of trap is NULL", wave->description);
251  return;
252  }
253 
254  /* set amplitude(s) */
255  for (i = firstinstance; i <= lastinstance; i++) {
256  const int board = wave->wfi[i].loc.board;
257  gradmax = ks_syslimits_gradtarget(loggrd, board);
258  switch (board) {
259  case XGRAD:
260  case YGRAD:
261  case ZGRAD:
262  {
263  iamp = (int)((wave->abs_max_amp * ampscale * wave->wfi[i].loc.ampscale / (wave->fs_factor * gradmax)) * MAX_PG_IAMP);
264  break;
265  }
266  case OMEGA:
267  {
268  /* The TARDIS_FREQ_RES = 10e6 / (1 << 24) is the effect of one bit. This is the overflow period of the 24-bit register
269  when incremented by 1 at 10 MHz = 0.586 Hz.
270  The product of iwave and iamp is 32 bit wide [the most significant 8-bits appear to be discarded as it is truncated to 24-bits],
271  this effectively mulitplies this value by 256 - so we need to shift 8-bits to the right here to be in the correct units [Hz] */
272  iamp = (int)((wave->abs_max_amp * ampscale * wave->wfi[i].loc.ampscale / (wave->fs_factor * TARDIS_FREQ_RES))) >> 8;
273  break;
274  }
275  case THETA:
276  {
277  iamp = (int)(wave->wfi[i].loc.ampscale * MAX_PG_IAMP);
278  break;
279  }
280  default:
281  ks_error("Wave scaling is currently not implemented for board: %i", board);
282  return;
283  }
284 
285  if (abs(iamp) > MAX_PG_IAMP) {
286  ks_error("ks_scan_wave_ampscale(%s): the integer amplitude is too large (%d) for instance %d", wave->description, iamp, i);
287  return;
288  }
289  if (setiamp((short) iamp, wave->wfi[i].wf, wave->wfi[i].boardinstance) != SUCCESS) {
290  ks_error("ks_scan_wave_ampscale(%s): An error occurred calling 'setiamp()'", wave->description);
291  return;
292  }
293 
294  }
295 
296 #else
297  for (i = firstinstance; i <= lastinstance; i++) {
298  ks_rt_scale_log_set(&wave->rtscaling, i, ampscale);
299  }
300 #endif
301 } /* ks_scan_wave_ampscale */
float fs_factor
Definition: KSFoundation.h:756
int boardinstance
Definition: KSFoundation.h:484
float ks_syslimits_gradtarget(LOG_GRAD loggrd, int board)
Returns the maximum target amplitude for a board (internal use)
Definition: KSFoundation_common.c:264
STATUS ks_error(const char *format,...) __attribute__((format(printf
Common error message function for HOST and TGT
int ninst
Definition: KSFoundation.h:494
int board
Definition: KSFoundation.h:462
float ampscale
Definition: KSFoundation.h:464
KS_RT_SCALE_LOG rtscaling
Definition: KSFoundation.h:755
KS_WFINSTANCE * wfi
Definition: KSFoundation.h:754
float abs_max_amp
Definition: KSFoundation.h:761
KS_SEQLOC loc
Definition: KSFoundation.h:486
int num_instances
Definition: KSFoundation.h:476
KS_DESCRIPTION description
Definition: KSFoundation.h:745
LOG_GRAD loggrd
KS_BASE base
Definition: KSFoundation.h:744
WF_PULSE * wf
Definition: KSFoundation.h:485
int ngenerated
Definition: KSFoundation.h:496
void ks_rt_scale_log_set(KS_RT_SCALE_LOG *log, int instance_idx, float ampscale)
Definition: KSFoundation_common.c:4890

◆ 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
394  {
395  int i;
396 #ifdef IPG
397  int numplaced = trp->base.ngenerated;
398 #else
399  int numplaced = trp->base.ninst;
400 #endif
401 
402  if (numplaced == 0) {
403  return;
404  }
405 
406  /* check range */
407  const int end = start + (count - 1) * skip;
408 
409  if (start < 0 || start >= numplaced || end < 0 || end >= numplaced || count < 0) {
410  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);
411  return;
412  }
413 
414  for (i = 0; i < count; ++i) {
415  ks_scan_trap_ampscale(trp, start + i * skip, ampscale);
416  }
417 
418 } /* 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:306

◆ 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
422  {
423  double phasepixelarea = (double) ks_calc_fov2gradareapixel(phaser->fov);
424  double newarea = pixelunits * phasepixelarea + (double) phaser->areaoffset;
425  double ampscale = newarea / phaser->grad.area;
426 
427  if (phaser == NULL)
428  return;
429 
430  if (fabs(ampscale) > 1.00003) { /* 1.00003 corresponds to 32768 / 32767, i.e. excess of one short int */
431 #ifndef IPG
432  ks_error("ks_scan_phaser_kmove(%s): Too large value (%g) in 3rd arg", phaser->grad.description, pixelunits);
433 #endif
434  } else if (fabs(ampscale) > 1.0) {
435  /* small round-off fix when ampscale is in range [1.0, 1.00003] */
436  ampscale = ampscale < 0 ? -1.0 : 1.0;
437  }
438 
439  ks_scan_trap_ampscale(&phaser->grad, instanceno, ampscale);
440 
441 } /* ks_scan_phaser_kmove */
KS_TRAP grad
Definition: KSFoundation.h:1719
float ks_calc_fov2gradareapixel(float fov)
Calculates the gradient area needed to move one pixel in k-space
Definition: KSFoundation_common.c:492
STATUS ks_error(const char *format,...) __attribute__((format(printf
Common error message function for HOST and TGT
float areaoffset
Definition: KSFoundation.h:1726
float area
Definition: KSFoundation.h:670
KS_DESCRIPTION description
Definition: KSFoundation.h:668
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:306
float fov
Definition: KSFoundation.h:1720

◆ 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
460  {
461 
462  if (phaser == NULL)
463  return;
464 
465  const double kmove = ks_scan_phaser_compute_kmove(phaser, view);
466 
467  ks_scan_phaser_kmove(phaser, instanceno, kmove);
468 
469 } /* ks_scan_phaser_toline */
double ks_scan_phaser_compute_kmove(KS_PHASER *phaser, int view)
Definition: KSFoundation_tgt.c:444
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:422
int view
Definition: GERequired.e:3728

◆ 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
472  {
473  double kmax;
474 
475  if (phaser == NULL)
476  return;
477 
478  if (view < 0 || view >= phaser->res) {
479  ks_scan_phaser_kmove(phaser, instanceno, 0.0);
480  return;
481  }
482 
483  /* KSFoundation 'view' is always 0-based: e.g. res = 256: view = 0->255. */
484  kmax = ((phaser->res - 1.0) / 2.0);
485  ks_scan_phaser_kmove(phaser, instanceno, view - kmax);
486 
487 } /* ks_scan_phaser_fromline */
int res
Definition: KSFoundation.h:1721
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:422
int view
Definition: GERequired.e:3728

◆ ks_scan_phaser_average()

void ks_scan_phaser_average ( KS_PHASER phaser,
int  instanceno 
)

ADDTITLEHERE

490  {
491  ks_scan_trap_ampscale(&phaser->grad, instanceno, 0.58);
492 }
KS_TRAP grad
Definition: KSFoundation.h:1719
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:306

◆ ks_scan_phaser_max()

void ks_scan_phaser_max ( KS_PHASER phaser,
int  instanceno 
)

ADDTITLEHERE

497  {
498  ks_scan_trap_ampscale(&phaser->grad, instanceno, 1.0);
499 }
KS_TRAP grad
Definition: KSFoundation.h:1719
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:306

◆ 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
1214  {
1215  int i;
1216  for (i = 0; i < slice_plan->nslices; i++) {
1217  if (slice_plan->acq_order[i].slpass == passindx && slice_plan->acq_order[i].sltime == sltimeinpass) {
1218  return slice_plan->acq_order[i].slloc;
1219  }
1220  }
1221 
1222  return KS_NOTSET;
1223 
1224 }
DATA_ACQ_ORDER acq_order[SLICE_FACTOR *DATA_ACQ_MAX]
Definition: KSFoundation.h:1321
#define KS_NOTSET
Definition: KSFoundation.h:115
int passindx
Definition: ksgre_tutorial_implementation.e:1046
int nslices
Definition: KSFoundation.h:1318

◆ 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])
1229  {
1230  int i;
1231  for (i = 0; i < slice_plan->nslices; i++) {
1232  if (slice_plan->acq_order[i].slpass == passindx && slice_plan->acq_order[i].slloc == slloc) {
1233  return slice_plan->acq_order[i].sltime;
1234  }
1235  }
1236 
1237  return KS_NOTSET;
1238 
1239 }
DATA_ACQ_ORDER acq_order[SLICE_FACTOR *DATA_ACQ_MAX]
Definition: KSFoundation.h:1321
#define KS_NOTSET
Definition: KSFoundation.h:115
int passindx
Definition: ksgre_tutorial_implementation.e:1046
int nslices
Definition: KSFoundation.h:1318

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

ADDTITLEHERE

1244  {
1245  int i;
1246  int kystep[epi->etl];
1249 
1250  if (phaseenc_plan == NULL) {
1251  return blipsign;
1252  }
1253 
1254  /* verify phase encoding plan has equidistant ky step size */
1255  /* first store ky coords */
1256  for (i = 0; i < epi->etl; i++) {
1257  coord = ks_phaseencoding_get(phaseenc_plan, i, shot);
1258  kystep[i] = coord.ky;
1259  }
1260 
1261  for (i = 0; i < epi->etl-1; i++) {
1262  kystep[i] = kystep[i+1] - kystep[i]; /* calculate ky step */
1263  if (i > 0) {
1264  /* check that kystep is consistent */
1265  if (kystep[i] != kystep[0]) {
1266  KS_THROW("%s: KS_EPI does not support varying ky step size", phaseenc_plan->description);
1267  return KS_EPI_NOBLIPS;
1268  }
1269  }
1270  }
1271 
1272  if (kystep[0] < 0) {
1273  blipsign = KS_EPI_POSBLIPS;
1274  } else if (kystep[0] > 0) {
1275  blipsign = KS_EPI_NEGBLIPS;
1276  }
1277 
1278  return blipsign;
1279 }
KS_PHASEENCODING_COORD ks_phaseencoding_get(const KS_PHASEENCODING_PLAN *phaseenc_plan_ptr, int encode, int shot)
Get [ky,kz] coordinate from KS_PHASEENCODING_PLAN for given encode and shot
Definition: KSFoundation_common.c:597
#define KS_INIT_PHASEENCODING_COORD
Definition: KSFoundation.h:306
s16 ky
Definition: KSFoundation.h:1750
int etl
Definition: KSFoundation.h:1941
Struct holding a 3D k-space phase encoding location (ky,kz)
Definition: KSFoundation.h:1749
KS_DESCRIPTION description
Definition: KSFoundation.h:1791
Definition: KSFoundation.h:2330
ks_enum_epiblipsign
Definition: KSFoundation.h:2330
#define KS_THROW(format,...)
Definition: KSFoundation.h:181
Definition: KSFoundation.h:2330
Definition: KSFoundation.h:2330

◆ ks_scan_epi_shotcontrol_sms()

void ks_scan_epi_shotcontrol_sms ( KS_EPI epi,
int  echo,
SCAN_INFO  sliceinfo,
KS_PHASEENCODING_COORD  starting_coord,
ks_enum_epiblipsign  blipsign,
float  rcvphase,
int  sms_factor,
float  sms_slice_gap,
int  caipi_factor,
int  sms_slice_encoding_direction 
)

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]starting_coordcoordinate in k-space from where the EPI train starts
[in]blipsignKS_EPI_POSBLIPS or KS_EPI_NEGBLIPS
[in]rcvphaseReceiver phase in degrees, for RF spoiling.
[in]sms_factorADDTEXTHERE
[in]sms_slice_gapADDTEXTHERE
[in]caipi_factorADDTEXTHERE
[in]sms_slice_encoding_directionADDTEXTHERE
Returns
void
1304  {
1305  float blipampscale = 1.0;
1306  float yfovratio, zfovratio;
1307  int i, s;
1308  int numblips = (epi->etl - 1);
1309  int readindx, blipindx, kyview, kzview;
1310  float caipi_rcvphase = 0.0;
1311 
1312  if (blipsign == KS_EPI_NOBLIPS) {
1313  starting_coord.ky = KS_NOTSET;
1314  starting_coord.kz = KS_NOTSET;
1315  }
1316 
1317  const int readfact = (epi->epi_readout_mode == KS_EPI_SPLITODDEVEN) ? 2 : 1;
1318 
1319  /* Blips-off triggers: If at least one of KS_EPI_NOBLIPS or ky = KS_NOTSET, make sure
1320  both blipdephasers and blips are off */
1321  if (blipsign == KS_EPI_NOBLIPS || starting_coord.ky == KS_NOTSET) {
1322  blipsign = KS_EPI_NOBLIPS; /* blip amp off */
1323  starting_coord.ky = KS_NOTSET; /* blipdephaser off */
1324  }
1325 
1326  /* EPI dephaser. From k-space center to this line */
1327  ks_scan_phaser_toline(&epi->blipphaser, 0 + 2 * echo, starting_coord.ky); /* KS_NOTSET => zero amplitude */
1328  if (epi->zphaser.grad.duration > 0) {
1329  /* 3D epi z dephaser(s) for even instances '2*echo' */
1330  ks_scan_phaser_toline(&epi->zphaser, 0 + 2 * echo, starting_coord.kz);
1331  }
1332 
1333  if (epi->zphaser.grad.duration > 0) {
1334  /* 3D epi z rephaser(s) for odd instances '1+2*echo' */
1335  ks_scan_phaser_fromline(&epi->zphaser, 1 + 2 * echo, starting_coord.kz);
1336  }
1337 
1338  /* EPI read & blips for current 'echo' = EPI train. */
1339  float caipi_phase = ks_calc_caipi_phase(sms_slice_gap, sms_factor, &sliceinfo, epi->caipiblip.area, sms_slice_encoding_direction);
1340  float caipi_previous = 0.0;
1341  for (i = 0; i < epi->etl; i++) {
1342  kyview = starting_coord.ky - blipsign * epi->blipphaser.R * i;
1343  kzview = starting_coord.kz;
1344 
1345  readindx = readfact * (i + echo * epi->etl); /* ETL# of readouts */
1346  blipindx = i + echo * numblips; /* (ETL-1)# of blips */
1347 
1348  if (i < numblips) {
1349  /* FUTURE: at this point, we could increase/decrease blipampscale for odd/even blips for oblique ghost correction */
1350  blipampscale = blipsign / epi->blipoversize;
1351  ks_scan_trap_ampscale(&epi->blip, blipindx, blipampscale);
1352  }
1353 
1354  /* CAIPI-blip */
1355  float caipi_target = ks_calc_caipi_scale(kyview, epi->R_ky, caipi_factor) * abs(blipsign) * 0.5; /* Get target caipi-phase with pattern linspace(-0.5, 0.5, sms_caipi_factor) */
1356  float caipi_scale = caipi_target - caipi_previous;
1357  ks_scan_trap_ampscale(&epi->caipiblip, blipindx, caipi_scale);
1358  caipi_previous = caipi_target;
1359 
1360  if (i == epi->etl-1) {
1361  /* EPI rephaser. From this line to k-space center */
1362  ks_scan_phaser_fromline(&epi->blipphaser, 1 + 2 * echo, kyview); /* KS_NOTSET => zero amplitude */
1363  /* CAIPI rephaser */
1364  ks_scan_trap_ampscale(&epi->caipiblip, blipindx+1, -caipi_previous);
1365  }
1366 
1367  /* FOV offsets (by changing freq/phase of epi.read) */
1368  float ky_centred, kz_centred;
1369  if (kyview < 0 || blipsign == KS_EPI_NOBLIPS) {
1370  ky_centred = 0.0f;
1371  } else {
1372  ky_centred = (float)kyview - ((epi->blipphaser.res - 1) / 2.0f);
1373  }
1374  yfovratio = epi->blipphaser.fov / epi->read.fov;
1375 
1376  int n_readouts_per_blip = (epi->epi_readout_mode == KS_EPI_SPLITODDEVEN ? 2 : 1);
1377  if (epi->zphaser.grad.duration > 0) {
1378  if (kzview < 0) {
1379  kz_centred = 0.0f;
1380  } else {
1381  kz_centred = (float)kzview - ((epi->zphaser.res - 1) / 2.0f);
1382  }
1383  zfovratio = epi->zphaser.fov / epi->read.fov;
1384  for (s = 0; s < n_readouts_per_blip; s++) {
1385  ks_scan_offsetfov3D(&epi->read, readindx + s, sliceinfo, ky_centred, yfovratio, kz_centred, zfovratio, rcvphase);
1386  }
1387  } else {
1388 
1389  if (caipi_factor > 1) {
1390  caipi_rcvphase = caipi_phase * caipi_target;
1391  } else {
1392  caipi_rcvphase = 0.0;
1393  }
1394 
1395  for (s = 0; s < n_readouts_per_blip; s++) {
1396  ks_scan_offsetfov(&epi->read, readindx + s, sliceinfo, ky_centred, yfovratio, rcvphase + caipi_rcvphase);
1397  }
1398  }
1399 
1400  } /* for etl */
1401 
1402 } /* ks_scan_epi_shotcontrol_sms */
float blipoversize
Definition: KSFoundation.h:1945
KS_TRAP blip
Definition: KSFoundation.h:1935
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:460
int R
Definition: KSFoundation.h:1723
int res
Definition: KSFoundation.h:1721
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:1134
int R_ky
Definition: KSFoundation.h:1940
KS_TRAP grad
Definition: KSFoundation.h:1719
#define KS_NOTSET
Definition: KSFoundation.h:115
float fov
Definition: KSFoundation.h:1551
KS_PHASER blipphaser
Definition: KSFoundation.h:1937
KS_PHASER zphaser
Definition: KSFoundation.h:1938
s16 ky
Definition: KSFoundation.h:1750
int etl
Definition: KSFoundation.h:1941
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:1126
float ks_calc_caipi_phase(const float sms_slice_gap, const int sms_multiband_factor, const SCAN_INFO *slice_info, const float caipi_blip_area, const int slice_encode_dir)
ADDTITLEHERE
Definition: KSFoundation_common.c:534
KS_READTRAP read
Definition: KSFoundation.h:1932
Definition: KSFoundation.h:2329
int epi_readout_mode
Definition: KSFoundation.h:1939
KS_TRAP caipiblip
Definition: KSFoundation.h:1936
float area
Definition: KSFoundation.h:670
s16 kz
Definition: KSFoundation.h:1751
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:306
float ks_calc_caipi_scale(const int ky, const int R, const int caipi_factor)
ADDTITLEHERE
Definition: KSFoundation_common.c:520
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:472
int duration
Definition: KSFoundation.h:673
Definition: KSFoundation.h:2330
float fov
Definition: KSFoundation.h:1720

◆ ks_scan_epi_shotcontrol()

void ks_scan_epi_shotcontrol ( KS_EPI epi,
int  echo,
SCAN_INFO  sliceinfo,
KS_PHASEENCODING_COORD  starting_coord,
ks_enum_epiblipsign  blipsign,
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]starting_coordcoordinate in k-space from where the EPI train starts
[in]blipsignKS_EPI_POSBLIPS or KS_EPI_NEGBLIPS
[in]rcvphaseReceiver phase in degrees, for RF spoiling.
1405  {
1406 
1407  int sms_factor = 1;
1408  float sms_slice_gap = 0.0;
1409  int caipi_factor = 1;
1410  int sms_slice_encoding_direction = 1;
1411 
1412  ks_scan_epi_shotcontrol_sms(epi, echo, sliceinfo, starting_coord, blipsign, rcvphase, sms_factor, sms_slice_gap, caipi_factor, sms_slice_encoding_direction);
1413 
1414 }
void ks_scan_epi_shotcontrol_sms(KS_EPI *epi, int echo, SCAN_INFO sliceinfo, KS_PHASEENCODING_COORD starting_coord, ks_enum_epiblipsign blipsign, float rcvphase, int sms_factor, float sms_slice_gap, int caipi_factor, int sms_slice_encoding_direction)
Changes the gradient state of a KS_EPI object for the given slice information
Definition: KSFoundation_tgt.c:1304

◆ ks_scan_epi_loadecho()

void ks_scan_epi_loadecho ( KS_EPI epi,
int  echo,
int  storeecho,
int  slice,
KS_PHASEENCODING_COORD  starting_coord,
ks_enum_epiblipsign  blipsign,
KS_DYNAMIC_STATE *  dynamic 
)

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

ADDTEXTHERE

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]starting_coordcoordinate in k-space from where the EPI train starts
[in]blipsignKS_EPI_POSBLIPS or KS_EPI_NEGBLIPS
dynamicPointer to KS_DYNAMIC_STATE struct, which has elements being automatically updated by the scan looping functions
Returns
void
1421  {
1422 
1423  int i, readindx;
1424  const int n_readouts_per_blip = (epi->epi_readout_mode == KS_EPI_SPLITODDEVEN) ? 2 : 1;
1425  const int numplaced = ks_numplaced(&epi->read.acq.base);
1426  if (numplaced == 0) {
1427  return;
1428  }
1429 
1430 
1431  /* Data routing control */
1432  TYPDAB_PACKETS dabacqctrl = dynamic->slloc >= 0 ? DABON : DABOFF;
1433  if ((dynamic->prescan || !dynamic->force_acq) && (dynamic->shot < 0 || dynamic->average < 0)) {
1434  dabacqctrl = DABOFF;
1435  }
1436  (void)dabacqctrl;
1437 
1438 
1439  if (echo < 0 || (echo + 1) * (n_readouts_per_blip * epi->etl) > numplaced) {
1440  KS_THROW("echo (%d) must be in range [0:%d]", echo, numplaced/(n_readouts_per_blip * epi->etl) - 1);
1441  return;
1442  }
1443 
1444  const int view_factor = epi->compressed_bam ? epi->R_ky : 1;
1445  (void) view_factor;
1446 
1447  for (i = 0; i < epi->etl; i++) {
1448 
1449  readindx = n_readouts_per_blip * (i + echo * epi->etl); /* N.B.: 'echo' is # of EPI trains, not EPI readout lobes (up to 16 possible, set by opnecho) */
1450 
1451  if ((readindx + (epi->epi_readout_mode == KS_EPI_SPLITODDEVEN)) >= numplaced) {
1452  KS_THROW("%s: Readout lobe index %d has not been placed in PG", epi->read.grad.description, readindx);
1453  return;
1454  }
1455 
1456 #ifdef IPG
1457  int kyview = starting_coord.ky - blipsign * epi->blipphaser.R * i;
1458  int s;
1459  int centerread = epi->time2center / epi->read.grad.duration; /* etl index corresponding to k-space center */
1460  /* 'pscR1' is the R1 receive gain made by prescan */
1461  int currentR1 = (epi->read.acq.override_R1 > 0 && epi->read.acq.override_R1 <= 11) ? epi->read.acq.override_R1 : pscR1;
1462 
1463  kyview = kyview < 0 ? kyview : kyview / view_factor;
1464 
1465  for (s = 0; s < n_readouts_per_blip; s++) {
1466 
1467  WF_PULSE_ADDR current_wf_pulse = &epi->read.acq.echo[readindx + s];
1468 
1469  if (dynamic->prescan) {
1470  /* Prescan (R1 & R2), turn on acquisition for center echo of 1st EPI train */
1471  TYPDAB_PACKETS psc_dabacqctrl = ((readindx + s) == centerread && echo == 0) ? dabacqctrl : DABOFF;
1472  loaddab(current_wf_pulse, slice, 0, DABSTORE, 0, psc_dabacqctrl, PSD_LOAD_DAB_ALL);
1473  } else {
1474  loaddab_hub_r1(current_wf_pulse, slice, n_readouts_per_blip * storeecho + s, DABSTORE, kyview + 1 /* GE's views are 1-based */, 0, currentR1, dabacqctrl, PSD_LOAD_DAB_ALL_WITH_HUB_R1);
1475  }
1476 
1477  } /* for s */
1478 #endif
1479 
1480  } /* per acq window (readout lobe) */
1481 
1482 } /* ks_scan_epi_loadecho() */
int R
Definition: KSFoundation.h:1723
WF_PULSE * echo
Definition: KSFoundation.h:846
int R_ky
Definition: KSFoundation.h:1940
LONG override_R1
Definition: KSFoundation.h:845
KS_PHASER blipphaser
Definition: KSFoundation.h:1937
s16 ky
Definition: KSFoundation.h:1750
KS_TRAP grad
Definition: KSFoundation.h:1561
KS_BASE base
Definition: KSFoundation.h:839
int etl
Definition: KSFoundation.h:1941
int pscR1
KS_READTRAP read
Definition: KSFoundation.h:1932
Definition: KSFoundation.h:2329
int ks_numplaced(KS_BASE *base)
ADDTITLEHERE
Definition: KSFoundation_common.c:4838
int epi_readout_mode
Definition: KSFoundation.h:1939
KS_DESCRIPTION description
Definition: KSFoundation.h:668
int time2center
Definition: KSFoundation.h:1944
int compressed_bam
Definition: KSFoundation.h:1947
KS_READ acq
Definition: KSFoundation.h:1549
int duration
Definition: KSFoundation.h:673
#define KS_THROW(format,...)
Definition: KSFoundation.h:181

◆ ks_loaddab_reserved()

void ks_loaddab_reserved ( WF_PULSE_ADDR  echo,
short  state,
short  resampler_index 
)

ADDTITLEHERE

Parameters
[in]echoADDTEXTHERE
[in]stateADDTEXTHERE
[in]resampler_indexADDTEXTHERE
Returns
void
1494  {
1495  #ifdef IPG
1496  sspload(&state, echo, 28, 1, (HW_DIRECTION)TOHARDWARE, (SSP_S_ATTRIB)SSPS1);
1497  sspload(&resampler_index, echo, 29, 1, (HW_DIRECTION)TOHARDWARE, (SSP_S_ATTRIB)SSPS1);
1498 #endif
1499 }

◆ ks_loaddab()

void ks_loaddab ( WF_PULSE_ADDR  echo,
const char *  dab_array 
)

ADDTITLEHERE

Parameters
[in]echoADDTEXTHERE
[in]dab_arrayADDTEXTHERE
Returns
void
1504  {
1505  int i;
1506  static const int dab_indices[KS_DABARRAY_AVAILABLE_BYTES] = {2,3,4,5,6,7,14,15,16,17,18,19,20,21,22,23,24,25,26,27}; /* 20 */
1507 
1508  if (!custom_dab_array) {
1509  return;
1510  }
1511  short dabbyte;
1512  for (i = 0; i < KS_DABARRAY_AVAILABLE_BYTES; i++) { /* KS_DABARRAY_AVAILABLE_BYTES = 20 */
1513  dabbyte = custom_dab_array[i];
1514 #ifdef IPG
1515  sspload(&dabbyte, echo, dab_indices[i], 1, (HW_DIRECTION)TOHARDWARE, (SSP_S_ATTRIB)SSPS1);
1516 #else
1517  (void) dabbyte;
1518  (void) dab_indices;
1519 #endif
1520  }
1521 
1522 } /* ks_loaddab() */
#define KS_DABARRAY_AVAILABLE_BYTES
Definition: KSFoundation.h:1839

◆ ks_loaddab_full()

void ks_loaddab_full ( WF_PULSE_ADDR  echo,
const char *  custom_dab_array 
)

ADDTITLEHERE

Parameters
[in]echoADDTEXTHERE
[in]custom_dab_arrayADDTEXTHERE
Returns
void
1527  {
1528  int i;
1529 
1530  if (!custom_dab_array) {
1531  return;
1532  }
1533  static const int dab_indices_full[KS_DABARRAY_FULL_LENGTH] = {2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29};
1534 
1535  for (i=0; i<KS_DABARRAY_FULL_LENGTH; i++){
1536  short dabbyte = custom_dab_array[i];
1537 #ifdef IPG
1538  sspload(&dabbyte, echo, dab_indices_full[i], 1, (HW_DIRECTION)TOHARDWARE, (SSP_S_ATTRIB)SSPS1);
1539 #else
1540  (void) dabbyte;
1541  (void) dab_indices_full;
1542 #endif
1543  }
1544 }
#define KS_DABARRAY_FULL_LENGTH
Definition: KSFoundation.h:1837

◆ ks_get_read_in_echotrain()

const KS_READ* ks_get_read_in_echotrain ( const KS_ECHOTRAIN echotrain,
const int  readout_index 
)

ADDTITLEHERE

Parameters
[in]echotrainADDTEXTHERE
[in]readout_indexADDTEXTHERE
Returns
void
4877  {
4878  int object_index = echotrain->controls[readout_index].pg.object_index;
4879  KS_READ_TYPE type = echotrain->controls[readout_index].pg.read_type;
4880  if (type == KS_READ_TRAP) {
4881  return &echotrain->readtraps[object_index].acq;
4882  } else {
4883  return &echotrain->readwaves[object_index].acq;
4884  }
4885 }
KS_READTRAP readtraps[KS_ECHOTRAIN_MAX_TRAPS]
Definition: KSFoundation.h:2016
KS_READWAVE readwaves[KS_ECHOTRAIN_MAX_WAVES]
Definition: KSFoundation.h:2017
int object_index
Definition: KSFoundation.h:1985
KS_READ_TYPE
The read object type, used in KS_READCONTROL_PULSEGEN
Definition: KSFoundation.h:1957
KS_READCONTROL_PULSEGEN pg
Definition: KSFoundation.h:2001
KS_READ_TYPE read_type
Definition: KSFoundation.h:1984
Definition: KSFoundation.h:1959
KS_READ acq
Definition: KSFoundation.h:1549
KS_READ acq
Definition: KSFoundation.h:1606
KS_READCONTROL controls[KS_MAXUNIQUE_READ]
Definition: KSFoundation.h:2015

◆ ks_get_wf_pulse()

WF_PULSE_ADDR ks_get_wf_pulse ( KS_ECHOTRAIN echotrain,
const int  readout_index 
)

ADDTITLEHERE

Parameters
[in]echotrainADDTEXTHERE
[in]readout_indexADDTEXTHERE
Return values
WF_PULSE_ADDR
1549  {
1550  int object_index = echotrain->controls[readout_index].pg.object_index;
1551  int instance_index = echotrain->controls[readout_index].pg.instance_index;
1552  KS_READ_TYPE type = echotrain->controls[readout_index].pg.read_type;
1553  if (type == KS_READ_TRAP) {
1554  return &echotrain->readtraps[object_index].acq.echo[instance_index];
1555  } else {
1556  return &echotrain->readwaves[object_index].acq.echo[instance_index];
1557  }
1558 }
KS_READTRAP readtraps[KS_ECHOTRAIN_MAX_TRAPS]
Definition: KSFoundation.h:2016
KS_READWAVE readwaves[KS_ECHOTRAIN_MAX_WAVES]
Definition: KSFoundation.h:2017
WF_PULSE * echo
Definition: KSFoundation.h:846
int object_index
Definition: KSFoundation.h:1985
KS_READ_TYPE
The read object type, used in KS_READCONTROL_PULSEGEN
Definition: KSFoundation.h:1957
int instance_index
Definition: KSFoundation.h:1988
KS_READCONTROL_PULSEGEN pg
Definition: KSFoundation.h:2001
KS_READ_TYPE read_type
Definition: KSFoundation.h:1984
Definition: KSFoundation.h:1959
KS_READ acq
Definition: KSFoundation.h:1549
KS_READ acq
Definition: KSFoundation.h:1606
KS_READCONTROL controls[KS_MAXUNIQUE_READ]
Definition: KSFoundation.h:2015

◆ ks_get_override_R1()

LONG ks_get_override_R1 ( KS_ECHOTRAIN echotrain,
const int  readout_index 
)

ADDTITLEHERE

Parameters
[in]echotrainADDTEXTHERE
[in]readout_indexADDTEXTHERE
Return values
LONG
1563  {
1564  int object_index = echotrain->controls[readout_index].pg.object_index;
1565  KS_READ_TYPE type = echotrain->controls[readout_index].pg.read_type;
1566  const int override_R1 = (type == KS_READ_TRAP) ?
1567  echotrain->readtraps[object_index].acq.override_R1 :
1568  echotrain->readwaves[object_index].acq.override_R1;
1569  return override_R1;
1570 }
KS_READTRAP readtraps[KS_ECHOTRAIN_MAX_TRAPS]
Definition: KSFoundation.h:2016
KS_READWAVE readwaves[KS_ECHOTRAIN_MAX_WAVES]
Definition: KSFoundation.h:2017
int object_index
Definition: KSFoundation.h:1985
KS_READ_TYPE
The read object type, used in KS_READCONTROL_PULSEGEN
Definition: KSFoundation.h:1957
LONG override_R1
Definition: KSFoundation.h:845
KS_READCONTROL_PULSEGEN pg
Definition: KSFoundation.h:2001
KS_READ_TYPE read_type
Definition: KSFoundation.h:1984
Definition: KSFoundation.h:1959
KS_READ acq
Definition: KSFoundation.h:1549
KS_READ acq
Definition: KSFoundation.h:1606
KS_READCONTROL controls[KS_MAXUNIQUE_READ]
Definition: KSFoundation.h:2015

◆ 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
1582  {
1583 #ifdef IPG
1584 
1585  WF_PULSE *echo_ptr;
1586  WF_PULSE *xtr_ptr;
1587  WF_PULSE *rba_ptr;
1588 
1589  int i;
1590  for (i = 0; i < acq->base.ngenerated; ++i) {
1591  echo_ptr = &acq->echo[i];
1592  setrfltrs((int) acq->filt.fslot, echo_ptr);
1593 
1594  getssppulse(&rba_ptr, echo_ptr, "rba", 0);
1595  getssppulse(&xtr_ptr, echo_ptr, "xtr", 0);
1596  acqctrl(dabacqctrl, (RECEIVER_SPEED_E)0, rba_ptr);
1597 
1598  setfreqphase((int)(fatoffset / TARDIS_FREQ_RES), /* freq ctrl */
1599  0, /* phase ctrl */
1600  xtr_ptr);
1601 
1602  /* TODO: Add r1 scaling here! */
1603  /*if (r1_scale != 1.0 && (rspent==L_SCAN)) {
1604  newR1 = ceil(pscR1 * r1_scale);
1605  if (newR1 > 13)
1606  newR1 = 13;
1607  else if (newR1 < 1)
1608  newR1 = 1;
1609 
1610  loaddab_hub_r1(echo_ptr, dabslice, dabecho, DABSTORE, dabview, 0, newR1, dabacqctrl, PSD_LOAD_DAB_ALL | PSD_LOAD_DAB_R1);
1611  }*/ /* if R1 change */
1612  routeDataFrameDab(echo_ptr, ROUTE_TO_RTP, cfcoilswitchmethod);
1613  }
1614 
1615 #endif
1616 
1617 }
int cfcoilswitchmethod

◆ ks_scan_switch_to_sequence()

void ks_scan_switch_to_sequence ( KS_SEQ_CONTROL ctrl)
1625  {
1626 #ifdef IPG
1627  if (ctrl->duration > 0) {
1628  if (ctrl->handle.offset == NULL) {
1629  ks_error("%s: ctrl->handle.offset is NULL. Missing calls to pg and KS_SEQLENGTH in pulsegen?", __FUNCTION__);
1630  } else {
1631  boffset(ctrl->handle.offset);
1632  }
1633  }
1634 #endif
1635 }
STATUS boffset(long *offsets)
STATUS ks_error(const char *format,...) __attribute__((format(printf
Common error message function for HOST and TGT
long * offset
Definition: KSFoundation.h:1077
int duration
Definition: KSFoundation.h:1227
KS_SEQ_HANDLE handle
Definition: KSFoundation.h:1235

◆ ks_scan_playsequence()

int ks_scan_playsequence ( KS_SEQ_CONTROL ctrl)
1644  {
1645 
1646  if (ctrl->duration > 0) {
1647 
1648 #ifdef IPG
1649  {
1651  setssitime(ctrl->ssi_time / cfhwgut);
1652  startseq(0, (short) MAY_PAUSE); /* play out the sequence in real time */
1653  }
1654 #else
1655  /* HOST */
1656  ctrl->nseqinstances++;
1657 
1658  /* Register the playout to the seq. collection */
1659  if (ctrl->collection->numplayouts < KS_MAX_SEQUENCE_PLAYOUTS) {
1660  ctrl->collection->seqplayouts[ctrl->collection->numplayouts++] = ctrl;
1661  }
1662 
1663  /* Advance scaling logs for all objects */
1664  int i;
1665  for (i = 0; i < ctrl->gradrf.numrf; ++i) {
1667  if (ks_numplaced(&ctrl->gradrf.rfptr[i]->omegawave.base)) {
1669  }
1670  if (ks_numplaced(&ctrl->gradrf.rfptr[i]->thetawave.base)) {
1672  }
1673  }
1674  for (i = 0; i < ctrl->gradrf.numtrap; ++i) {
1676  }
1677  for (i = 0; i < ctrl->gradrf.numwave; ++i) {
1679  }
1680  for (i = 0; i < ctrl->gradrf.numwait; ++i) {
1682  }
1683 #endif
1684  } /* ctrl->duration > 0 */
1685 
1686  return ctrl->duration;
1687 }
STATUS startseq(s16, s16)
STATUS setssitime(long)
KS_RF * rfptr[KS_MAXUNIQUE_RF]
Definition: KSFoundation.h:1055
int numtrap
Definition: KSFoundation.h:1058
int cfhwgut
int duration
Definition: KSFoundation.h:1227
KS_RT_SCALE_LOG rtscaling
Definition: KSFoundation.h:550
int nseqinstances
Definition: KSFoundation.h:1225
KS_RT_SCALE_LOG rtscaling
Definition: KSFoundation.h:681
int numrf
Definition: KSFoundation.h:1056
KS_GRADRFCTRL gradrf
Definition: KSFoundation.h:1236
KS_RT_SCALE_LOG rtscaling
Definition: KSFoundation.h:755
void ks_rt_scale_log_next(KS_RT_SCALE_LOG *log)
Definition: KSFoundation_common.c:4969
#define KS_MAX_SEQUENCE_PLAYOUTS
Definition: KSFoundation.h:262
KS_WAVE thetawave
Definition: KSFoundation.h:1039
int numwait
Definition: KSFoundation.h:1064
KS_WAVE * waveptr[KS_MAXUNIQUE_WAVE]
Definition: KSFoundation.h:1059
KS_WAVE rfwave
Definition: KSFoundation.h:1037
int ks_numplaced(KS_BASE *base)
ADDTITLEHERE
Definition: KSFoundation_common.c:4838
KS_BASE base
Definition: KSFoundation.h:744
KS_WAIT * waitptr[KS_MAXUNIQUE_WAIT]
Definition: KSFoundation.h:1063
void ks_scan_switch_to_sequence(KS_SEQ_CONTROL *ctrl)
Definition: KSFoundation_tgt.c:1625
KS_TRAP * trapptr[KS_MAXUNIQUE_TRAP]
Definition: KSFoundation.h:1057
int ssi_time
Definition: KSFoundation.h:1226
int numwave
Definition: KSFoundation.h:1060
KS_WAVE omegawave
Definition: KSFoundation.h:1038
struct _seq_collection_s * collection
Definition: KSFoundation.h:1237

◆ 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
1721  {
1722  return ks_scan_loaddabwithindices_nex(pulse, slice, echo, view, acq, vol, DABSTORE, acqon_flag);
1723 }
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:1697
int view
Definition: GERequired.e:3728

◆ 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
1704  {
1705 
1706  union Indices idx;
1707  idx.c[0] = acq;
1708  idx.c[1] = vol;
1709  return loaddabwithangle(pulse, idx.i, slice, echo, operation, view, acqon_flag, PSD_LOAD_DAB_ALL);
1710 }
Definition: KSFoundation_tgt.c:1692
int view
Definition: GERequired.e:3728
uint8_t c[4]
Definition: KSFoundation_tgt.c:1693

◆ 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
1728  {
1729  int maxplayouts;
1730  int nBytes = 0;
1731  int i;
1732  n32 packed = 1;
1733 #ifdef IPG
1734  setscantimestop();
1735 #endif
1736 
1737  maxplayouts = waitctrl ? maxwait / waitctrl->duration : 0;
1738  if (maxplayouts < 1) {
1739  waitctrl = NULL;
1740  maxplayouts = 0;
1741  }
1742 
1743  for (i = 0; i <= maxplayouts; i++) {
1744 
1745 #ifdef IPG
1746  if (i != 0 && waitctrl) {
1747  ks_scan_playsequence(waitctrl);
1748  }
1749 #endif
1750 
1751  nBytes = 0;
1752 
1753 #if defined(MGD_TGT) && defined(PSD_HW)
1754 
1755 
1756  nBytes = rtp_get_feedback_data(rtpmsg, maxmsgsize, &packed, 2, RTP_QUEUE_NEWEST); /*according to RTP_RESULT_PROMO=2, defined in lx/include/PromoCommon.h*/
1757 
1758 
1759 #endif
1760 
1761  if (nBytes>0 && !packed) {
1762  break;
1763  }
1764  }
1765 
1766 #ifdef IPG
1767  setscantimestart();
1768 #endif
1769 
1770  return nBytes;
1771 }
int32_t i
Definition: KSFoundation_tgt.c:1694
int duration
Definition: KSFoundation.h:1227
int ks_scan_playsequence(KS_SEQ_CONTROL *ctrl)
Definition: KSFoundation_tgt.c:1644

◆ 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
1777  {
1778 
1779  KS_BASE *head = (KS_BASE*) pobj;
1780 
1781  /* create a copy */
1782  KS_BASE *copy = (KS_BASE*) AllocNode(head->size);
1783  memcpy(copy, head, head->size);
1784 
1785  /* link to the copy */
1786  head->next = copy;
1787 
1788  /* reset the count of generated instances */
1789  head->ngenerated = 0;
1790 }
void * next
Definition: KSFoundation.h:497
(Internal use) Structure being a part of various sequence object to keep count of instances on HOST a...
Definition: KSFoundation.h:493
int ngenerated
Definition: KSFoundation.h:496
int size
Definition: KSFoundation.h:498

◆ ks_show_clock()

void ks_show_clock ( FLOAT  scantime)

Show the clock in the UI with the remaining scan time

1829  {
1830 #ifdef IPG
1831  setscantimestop();
1832  setscantimeimm(PSD_CLOCK_NORM, (float)scantime, piviews, (float)pitslice, opslicecnt);
1833  setscantimestart();
1834 #endif
1835 }
int piviews
int opslicecnt
Definition: KSFoundation_tgt.c:1827
float pitslice

◆ 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]
4827  {
4828  unsigned int phase = 117 * ( counter * (counter + 1) + 2) / 2;
4829 
4830  /* fmod() does not seem to work on TGT on hardware (MR scanner) */
4831 
4832  return ((float) (phase%360));
4833 }

◆ ks_scan_gettherightshot()

int ks_scan_gettherightshot ( const KS_DYNAMIC_STATE *  dynamic)

shot vs inner shot logic for ksepi.cc

Parameters
dynamic
Returns
int
4849  {
4850  int shot = 0;
4851 
4852  if (dynamic) {
4853  shot = (dynamic->shot == 0) ? dynamic->inner_shot : dynamic->shot;
4854  }
4855 
4856  return shot;
4857 
4858 }

◆ ks_get_trapamp_instance()

float ks_get_trapamp_instance ( const KS_TRAP trap,
int  instance 
)

Get the amplitude in G/cm for an instance of a KS_TRAP (excluding realtime changes)

Parameters
[in]trapPointer to KS_TRAP
[in]instance0-based instance (up to .base.ninst-1)
Returns
float Gradient amplitude in G/cm
4862  {
4863  float trapamp = -999.0;
4864 
4865 #ifdef IPG
4866  /* getiamp(&readiamp, trap->wfi[i].wf, trap->wfi[i].boardinstance); */
4867  trapamp = trap->wfi[instance].loc.ampscale * trap->amp;
4868 #else
4869  trapamp = trap->locs[instance].ampscale * trap->amp;
4870 #endif
4871 
4872  return trapamp;
4873 }
KS_SEQLOC * locs
Definition: KSFoundation.h:678
float ampscale
Definition: KSFoundation.h:464
KS_WFINSTANCE * wfi
Definition: KSFoundation.h:680
KS_SEQLOC loc
Definition: KSFoundation.h:486
float amp
Definition: KSFoundation.h:669

◆ 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]
1797  {
1798  int i = 0;
1799  for (i = 0; i < gradrfctrl->numrf; ++i) {
1800  gradrfctrl->rfptr[i] = NULL;
1801  }
1802  for (i = 0; i < gradrfctrl->numtrap; ++i) {
1803  gradrfctrl->trapptr[i] = NULL;
1804  }
1805  for (i = 0; i < gradrfctrl->numwave; ++i) {
1806  gradrfctrl->waveptr[i] = NULL;
1807  }
1808  for (i = 0; i < gradrfctrl->numacq; ++i) {
1809  gradrfctrl->readptr[i] = NULL;
1810  }
1811  for (i = 0; i < gradrfctrl->numwait; ++i) {
1812  gradrfctrl->waitptr[i] = NULL;
1813  }
1814  gradrfctrl->numrf = 0;
1815  gradrfctrl->numtrap = 0;
1816  gradrfctrl->numwave = 0;
1817  gradrfctrl->numacq = 0;
1818  gradrfctrl->numwait = 0;
1819  gradrfctrl->is_cleared_on_tgt = TRUE;
1820 }
KS_RF * rfptr[KS_MAXUNIQUE_RF]
Definition: KSFoundation.h:1055
int numtrap
Definition: KSFoundation.h:1058
int32_t i
Definition: KSFoundation_tgt.c:1694
int numrf
Definition: KSFoundation.h:1056
int numwait
Definition: KSFoundation.h:1064
KS_WAVE * waveptr[KS_MAXUNIQUE_WAVE]
Definition: KSFoundation.h:1059
int is_cleared_on_tgt
Definition: KSFoundation.h:1065
KS_WAIT * waitptr[KS_MAXUNIQUE_WAIT]
Definition: KSFoundation.h:1063
KS_TRAP * trapptr[KS_MAXUNIQUE_TRAP]
Definition: KSFoundation.h:1057
int numwave
Definition: KSFoundation.h:1060
KS_READ * readptr[KS_MAXUNIQUE_READ]
Definition: KSFoundation.h:1061
int numacq
Definition: KSFoundation.h:1062

◆ ks_eval_clear_readwave()

int ks_eval_clear_readwave ( KS_READWAVE readwave)

ADDTITLEHERE

7118  {
7119  KS_READWAVE def_readwave;
7120  ks_init_readwave(&def_readwave);
7121  def_readwave.freqoffHz = readwave->freqoffHz;
7122  def_readwave.fov = readwave->fov;
7123  def_readwave.res = readwave->res;
7124  *readwave = def_readwave;
7125  return SUCCESS;
7126 }
float fov
Definition: KSFoundation.h:1610
float freqoffHz
Definition: KSFoundation.h:1609
void ks_init_readwave(KS_READWAVE *readwave)
Resets a KS_READWAVE sequence object to its default value (KS_INIT_READWAVE)
Definition: KSFoundation_host.c:134
Composite sequence object for data readout during a wave (1-D resampling)
Definition: KSFoundation.h:1603
int res
Definition: KSFoundation.h:1611

◆ ks_polyder()

void ks_polyder ( const double *  coeffs,
const int  order,
double *  der_coeffs 
)

get derivative coeffs of polynomial of order

388  {
389  int ido;
390  for (ido = 0; ido < order; ido++) {
391  der_coeffs[ido] = coeffs[ido] * (order - ido);
392  }
393 }

◆ ks_polyval()

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

eval polynomial

398  {
399  int idx, ido;
400  for (idx = 0; idx < numx; idx++) {
401  values[idx] = 0.0;
402  for (ido = 0; ido <= order; ido++) {
403  values[idx] = values[idx] * x[idx] + coeffs[ido];
404  }
405  }
406 }

◆ ks_polyval_f()

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

eval polynomial with float values

411  {
412  double xd[numx];
413  double vald[numx];
414  int i;
415 
416  for (i = 0; i < numx; i++) {
417  xd[i] = (double) x[i];
418  }
419 
420  ks_polyval(coeffs, order, xd, numx, vald);
421 
422  for (i = 0; i < numx; i++) {
423  values[i] = (float) vald[i];
424  }
425 }
void ks_polyval(const double *coeffs, const int order, const double *x, const int numx, double *values)
eval polynomial
Definition: KSFoundation_common.c:398
int32_t i
Definition: KSFoundation_tgt.c:1694

◆ ks_Newtons_method()

double ks_Newtons_method ( const double *  coeffs,
const int  order,
const double  x0,
const int  num_iter 
)

find real polynomial root near x0 using Newton's method

430  {
431  double x = x0;
432  double der_coeffs[order];
433  ks_polyder(coeffs, order, der_coeffs); /* get derivative */
434 
435  int iter;
436  for (iter = 0; iter < num_iter; iter++) {
437  double f, fp; /* polynomial and its derivative at x */
438  ks_polyval(coeffs, order, &x, 1, &f);
439  ks_polyval(der_coeffs, order-1, &x, 1, &fp);
440  x -= f / fp;
441  }
442  return x;
443 }
void ks_polyval(const double *coeffs, const int order, const double *x, const int numx, double *values)
eval polynomial
Definition: KSFoundation_common.c:398
void ks_polyder(const double *coeffs, const int order, double *der_coeffs)
get derivative coeffs of polynomial of order
Definition: KSFoundation_common.c:388

◆ ks_pg_readwave()

int ks_pg_readwave ( KS_READWAVE readwave,
KS_SEQLOC  loc,
KS_SEQ_CONTROL ctrl 
)

ADDTITLEHERE

2232  {
2233  STATUS status;
2234 
2235  int startpos = loc.pos;
2236  KS_SEQLOC omegaloc = KS_INIT_SEQLOC;
2237  omegaloc.board = OMEGA;
2238  omegaloc.pos = startpos + readwave->acqdelay;
2239  omegaloc.ampscale = 1.0f;
2240 
2241  /* Readout waveforms */
2242  /* readwave->delay should be 0 */
2243  loc.pos = startpos + readwave->delay;
2244 
2245 
2246 
2247  status = ks_pg_wave(&readwave->grad, loc, ctrl);
2248  KS_RAISE(status);
2249 
2250  /* Omega */
2251  status = ks_pg_wave(&readwave->omega, omegaloc, ctrl);
2252  KS_RAISE(status);
2253 
2254  int state;
2255  for (state = 0; state < KS_READCONTROL_MAXSTATE; state++) {
2256  if (readwave->grad.p_waveformstates[state] != NULL ) {
2257  ks_pg_addwaveformstate(&readwave->grad, readwave->grad_states[state], state);
2258  }
2259  if (readwave->omega.p_waveformstates[state] != NULL ) {
2260  ks_pg_addwaveformstate(&readwave->omega, readwave->omega_states[state], state);
2261  }
2262  }
2263 
2264  /* Acquisition */
2265  status = ks_pg_read(&readwave->acq, omegaloc.pos, ctrl);
2266  KS_RAISE(status);
2267 
2268 #ifdef IPG
2269  short * iwave = (short *) AllocNode(readwave->omega.res * sizeof(short));
2270  movewaveimm(iwave, &readwave->omega.wfpulse[OMEGA][0], 0, readwave->omega.res, FROMHARDWARE);
2271 
2272  /* phase is accumulated at 10 MHz */
2273  int k;
2274  readwave->omega_iwave_phase_at_echo = 0;
2275  for(k = 0; k < (readwave->time2center - readwave->acqdelay); k++) {
2276  readwave->omega_iwave_phase_at_echo += 10 * iwave[k / GRAD_UPDATE_TIME];
2277  }
2278  FreeNode(iwave);
2279 #endif
2280 
2281  return SUCCESS;
2282 }
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:2051
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:2289
KS_WAVEFORM omega_states[KS_READCONTROL_MAXSTATE]
Definition: KSFoundation.h:1619
int pos
Definition: KSFoundation.h:463
KS_WAVE omega
Definition: KSFoundation.h:1605
int time2center
Definition: KSFoundation.h:1615
int board
Definition: KSFoundation.h:462
KS_WAVE grad
Definition: KSFoundation.h:1604
float ampscale
Definition: KSFoundation.h:464
WF_PULSE ** wfpulse
Definition: KSFoundation.h:753
int delay
Definition: KSFoundation.h:1607
#define KS_RAISE(status)
Definition: KSFoundation.h:190
typedef struct used as argument to ks_pg_*** functions to control where and when to place a sequence ...
Definition: KSFoundation.h:461
KS_WAVEFORM grad_states[KS_READCONTROL_MAXSTATE]
Definition: KSFoundation.h:1618
int omega_iwave_phase_at_echo
Definition: KSFoundation.h:1617
float * p_waveformstates[KS_WAVE_MAXNSTATES]
Definition: KSFoundation.h:749
STATUS ks_pg_addwaveformstate(KS_WAVE *wave, KS_WAVEFORM waveform, int state_index)
Allocates an additional waveformstate in sequencer memory
Definition: KSFoundation_common.c:2544
#define KS_INIT_SEQLOC
Definition: KSFoundation.h:287
int acqdelay
Definition: KSFoundation.h:1608
KS_READ acq
Definition: KSFoundation.h:1606
int res
Definition: KSFoundation.h:746
#define KS_READCONTROL_MAXSTATE
Definition: KSFoundation.h:259

◆ ks_pg_echotrain_readout()

STATUS ks_pg_echotrain_readout ( KS_ECHOTRAIN echotrain,
int  readout_index,
int  echo_time,
KS_ECHOTRAIN_READOUT_TIMING timing,
KS_SEQ_CONTROL ctrl 
)

ADDTITLEHERE

1204  {
1205 
1206  STATUS status;
1207 
1208  KS_READCONTROL *control = &echotrain->controls[readout_index];
1209  const int object_index = control->pg.object_index;
1210 
1211  KS_SEQLOC loc;
1212  loc.ampscale = control->pg.ampscale;
1213  loc.board = XGRAD;
1214 
1215  switch (control->pg.read_type) {
1216  case KS_READ_TRAP: {
1217  KS_READTRAP* trap = &echotrain->readtraps[object_index];
1218  control->pg.instance_index = ks_numplaced(&trap->acq.base);
1219  /* center acq over echo */
1220  loc.pos = RDN_GRD(echo_time - trap->acq.duration / 2 - trap->acqdelay + control->pg.shift);
1221  status = ks_pg_readtrap(trap, loc, ctrl);
1222  KS_RAISE(status);
1223  timing->start = loc.pos;
1224  timing->start_acq = loc.pos + trap->acqdelay;
1225  timing->end_acq = timing->start_acq + trap->acq.duration;
1226  timing->end = loc.pos + trap->grad.duration;
1227  break;
1228  }
1229  case KS_READ_WAVE: {
1230  KS_READWAVE* wave = &echotrain->readwaves[object_index];
1231  control->pg.instance_index = ks_numplaced(&wave->acq.base);
1232  /* center acq over echo */
1233  loc.pos = RDN_GRD(echo_time - wave->acq.duration / 2 - wave->acqdelay + control->pg.shift);
1234  status = ks_pg_readwave(wave, loc, ctrl);
1235  KS_RAISE(status);
1236  timing->start = loc.pos;
1237  timing->start_acq = loc.pos + wave->acqdelay;
1238  timing->end_acq = timing->start_acq + wave->acq.duration;
1239  timing->end = loc.pos + wave->grad.duration;
1240  break;
1241  }
1242  default: {
1243  return KS_THROW("echo train cannot place undefined read type");
1244  }
1245  }
1246 
1247  return SUCCESS;
1248 }
KS_READTRAP readtraps[KS_ECHOTRAIN_MAX_TRAPS]
Definition: KSFoundation.h:2016
KS_READWAVE readwaves[KS_ECHOTRAIN_MAX_WAVES]
Definition: KSFoundation.h:2017
Wraps pulsegen information and state of each readout in an echotrain
Definition: KSFoundation.h:1998
int object_index
Definition: KSFoundation.h:1985
int pos
Definition: KSFoundation.h:463
int instance_index
Definition: KSFoundation.h:1988
float ampscale
Definition: KSFoundation.h:1987
int end
Definition: KSFoundation.h:2038
int board
Definition: KSFoundation.h:462
int end_acq
Definition: KSFoundation.h:2037
int ks_numplaced(KS_BASE *base)
ADDTITLEHERE
Definition: KSFoundation_common.c:4838
KS_READCONTROL_PULSEGEN pg
Definition: KSFoundation.h:2001
KS_TRAP grad
Definition: KSFoundation.h:1561
int start
Definition: KSFoundation.h:2035
KS_WAVE grad
Definition: KSFoundation.h:1604
float ampscale
Definition: KSFoundation.h:464
KS_BASE base
Definition: KSFoundation.h:839
STATUS ks_pg_readwave(KS_READWAVE *readwave, KS_SEQLOC loc, KS_SEQ_CONTROL *ctrl)
ADDTITLEHERE
Definition: KSFoundation_common.c:2232
KS_READ_TYPE read_type
Definition: KSFoundation.h:1984
Definition: KSFoundation.h:1960
Definition: KSFoundation.h:1959
int duration
Definition: KSFoundation.h:841
#define KS_RAISE(status)
Definition: KSFoundation.h:190
typedef struct used as argument to ks_pg_*** functions to control where and when to place a sequence ...
Definition: KSFoundation.h:461
Composite sequence object for data readout using a trapezoid gradient
Definition: KSFoundation.h:1548
Composite sequence object for data readout during a wave (1-D resampling)
Definition: KSFoundation.h:1603
int start_acq
Definition: KSFoundation.h:2036
int acqdelay
Definition: KSFoundation.h:1608
KS_READ acq
Definition: KSFoundation.h:1549
int duration
Definition: KSFoundation.h:673
KS_READ acq
Definition: KSFoundation.h:1606
#define KS_THROW(format,...)
Definition: KSFoundation.h:181
KS_READCONTROL controls[KS_MAXUNIQUE_READ]
Definition: KSFoundation.h:2015
int shift
Definition: KSFoundation.h:1986
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:2161
int acqdelay
Definition: KSFoundation.h:1555
int duration
Definition: KSFoundation.h:747

◆ ks_numplaced()

int ks_numplaced ( KS_BASE base)

ADDTITLEHERE

4838  {
4839 #ifdef IPG
4840  return base->ngenerated;
4841 #else
4842  return base->ninst;
4843 #endif
4844 }
int ninst
Definition: KSFoundation.h:494
int ngenerated
Definition: KSFoundation.h:496

◆ ks_set_opcode()

void ks_set_opcode ( WF_PULSE_ADDR  echo,
short  opcode 
)

ADDTITLEHERE

1487  {
1488  sspload(&opcode, echo, 1, 1, (HW_DIRECTION)TOHARDWARE, (SSP_S_ATTRIB)SSPS1);
1489 }

◆ ks_write_vector_bf()

int ks_write_vector_bf ( float *  vec,
uint32_t  numel,
const char *  fname 
)

ADDTITLEHERE

6172  {
6173  char embedfile_uid[512];
6174  #ifdef PSD_HW /* on MR-scanner */
6175  char outputdir_uid[512];
6176  char cmd[512];
6177  sprintf(outputdir_uid, "/usr/g/mrraw/kstmp/%010d/embed/", rhkacq_uid);
6178  sprintf(embedfile_uid, "%s/%s", outputdir_uid, fname);
6179  sprintf(cmd, "mkdir -p %s > /dev/null", outputdir_uid);
6180  system(cmd);
6181  #else
6182  sprintf(embedfile_uid, "./%s", fname);
6183  #endif
6184  FILE* fp = fopen(embedfile_uid, "wb");
6185  const uint32_t rank = 1;
6186  fwrite(&rank, sizeof(rank), 1, fp);
6187  fwrite(&numel, sizeof(numel), 1, fp);
6188  fwrite(vec, sizeof(float), numel, fp);
6189  fflush(fp);
6190  fclose(fp);
6191  return SUCCESS;
6192 }
int rhkacq_uid

◆ ks_open_file_in_embed()

FILE* ks_open_file_in_embed ( const char *  fname,
const char *  write_mode 
)

ADDTITLEHERE

4715  {
4716  char embedfile_uid[512];
4717  #ifdef PSD_HW /* on MR-scanner */
4718  char outputdir_uid[512];
4719  char cmd[512];
4720  sprintf(outputdir_uid, "/usr/g/mrraw/kstmp/%010d/embed/", rhkacq_uid);
4721  sprintf(embedfile_uid, "%s/%s", outputdir_uid, fname);
4722  sprintf(cmd, "mkdir -p %s > /dev/null", outputdir_uid);
4723  system(cmd);
4724  #else
4725  sprintf(embedfile_uid, "./%s", fname);
4726  #endif
4727 
4728  return fopen(embedfile_uid, write_mode);
4729  /* to do add error handling */
4730 }
int rhkacq_uid

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