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

Data Structures

struct  _grad_heat_handle
 
struct  _grad_list
 
struct  ks_rampparams_s
 

Macros

#define MSS_FLAGS_T   void
 
#define KS_INIT_RAMPPARAMS   {0, 0, 0, 0}
 

Typedefs

typedef int(* ks_comparator) (const void *, const void *)
 

Functions

int ftruncate (int fd, off_t length)
 
STATUS inittargets (LOG_GRAD *lgrad, PHYS_GRAD *pgrad)
 
STATUS obloptimize_epi (LOG_GRAD *lgrad, PHYS_GRAD *pgrad, SCAN_INFO *scaninfotab, INT slquant, INT plane_type, INT coaxial, INT method, INT debug, INT *newgeo, INT srmode)
 
STATUS minseqrfamp (INT *Minseqrfamp, const INT numPulses, const RF_PULSE *rfpulse, const INT entry)
 
STATUS maxsar (INT *Maxseqsar, INT *Maxslicesar, DOUBLE *Avesar, DOUBLE *Cavesar, DOUBLE *Pksar, DOUBLE *B1rms, const INT numPulses, const RF_PULSE *rfpulse, const INT entry, const INT tr_val)
 
STATUS minseq (INT *p_minseqgrad, GRAD_PULSE *gradx, const INT gx_free, GRAD_PULSE *grady, const INT gy_free, GRAD_PULSE *gradz, const INT gz_free, const LOG_GRAD *log_grad, const INT seq_entry_index, const INT samp_rate, const INT min_tr, const INT e_flag, const INT debug_flag)
 
void ks_init_read (KS_READ *read)
 
void ks_init_trap (KS_TRAP *trap)
 
void ks_init_wait (KS_WAIT *wait)
 
void ks_init_wave (KS_WAVE *wave)
 
void ks_init_rf (KS_RF *rf)
 
void ks_init_sms_info (KS_SMS_INFO *sms_info)
 
void ks_init_selrf (KS_SELRF *selrf)
 
void ks_init_readtrap (KS_READTRAP *readtrap)
 
void ks_init_phaser (KS_PHASER *phaser)
 
void ks_init_epi (KS_EPI *epi)
 
void ks_init_readwave (KS_READWAVE *readwave)
 
void ks_init_gradrfctrl (KS_GRADRFCTRL *gradrfctrl)
 
void ks_init_seqcontrol (KS_SEQ_CONTROL *seqcontrol)
 
void ks_init_seqcollection (KS_SEQ_COLLECTION *seqcollection)
 
STATUS ks_init_slewratecontrol (LOG_GRAD *loggrd, PHYS_GRAD *phygrd, float srfact)
 
void ks_init_echotrain (KS_ECHOTRAIN *const echotrain)
 
STATUS ks_eval_addtoseqcollection (KS_SEQ_COLLECTION *seqcollection, KS_SEQ_CONTROL *seqctrl)
 
STATUS ks_eval_seqcollection_isadded (KS_SEQ_COLLECTION *seqcollection, KS_SEQ_CONTROL *seqctrl)
 
STATUS ks_eval_wait (KS_WAIT *wait, const char *const desc, int pg_duration, int max_duration)
 
STATUS ks_eval_isirot (KS_ISIROT *isirot, const char *const desc, int isinumber)
 
STATUS ks_eval_read (KS_READ *read, const char *const desc)
 
STATUS ks_eval_trap_constrained (KS_TRAP *trap, const char *const desc, float ampmax, float slewrate, int minduration)
 
STATUS ks_eval_trap (KS_TRAP *trap, const char *const desc)
 
STATUS ks_eval_trap2 (KS_TRAP *trap, const char *const desc)
 
STATUS ks_eval_trap1 (KS_TRAP *trap, const char *const desc)
 
STATUS ks_eval_trap_rotation_invariant_75_max_slewrate (KS_TRAP *trap, const char *const desc)
 
STATUS ks_eval_trap_constrained_time_maxarea (KS_TRAP *trap, const char *const desc, float ampmax, float slewrate, float maxarea, int derate_slewrate)
 
STATUS ks_eval_set_asym_padding (struct _readtrap_s *readtrap, float wanted_paddingarea_pre, float wanted_paddingarea_post)
 
STATUS ks_eval_trap1p (KS_TRAP *trap, const char *const desc)
 
STATUS ks_eval_ramp (KS_WAVE *ramp, float slew, float end_amp, int dwell)
 
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)
 
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)
 
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)
 
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)
 
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)
 
STATUS ks_eval_readtrap (KS_READTRAP *readtrap, const char *const desc)
 
STATUS ks_eval_readtrap2 (KS_READTRAP *readtrap, const char *const desc)
 
STATUS ks_eval_readtrap1 (KS_READTRAP *readtrap, const char *const desc)
 
STATUS ks_eval_readtrap_constrained_sample_duration (struct _readtrap_s *readtrap, const char *const desc, const float ampmax, const float slewrate, const int t_A, const int sample_duration, const float area_post, const float area_total)
 
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)
 
void ks_eval_readwave_negatestate (KS_READWAVE *readwave, int state)
 
STATUS ks_eval_readwave_multistated (KS_READWAVE *readwave, KS_WAVE *acq_waves, const int numstates, 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 (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)
 
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)
 
STATUS ks_eval_phaser_setaccel (KS_PHASER *phaser, int min_acslines, float R)
 
STATUS ks_eval_phaser_constrained (KS_PHASER *phaser, const char *const phasername, float ampmax, float slewrate, int minduration)
 
STATUS ks_eval_phaser (KS_PHASER *phaser, const char *const phasername)
 
STATUS ks_eval_phaser2 (KS_PHASER *phaser, const char *const phasername)
 
STATUS ks_eval_phaser1 (KS_PHASER *phaser, const char *const phasername)
 
STATUS ks_eval_phaser1p (KS_PHASER *phaser, const char *const phasername)
 
STATUS ks_eval_wave (KS_WAVE *wave, const char *const desc, const int res, const int duration, const KS_WAVEFORM waveform)
 
STATUS ks_eval_wave_file (KS_WAVE *wave, const char *const desc, int res, int duration, const char *const filename, const char *const format)
 
STATUS ks_eval_mirrorwave (KS_WAVE *wave)
 
STATUS ks_eval_mirrorwaveform (KS_WAVEFORM waveform, int res)
 
STATUS ks_eval_rf_sinc (KS_RF *rf, const char *const desc, double bw, double tbw, float flip, int wintype)
 
STATUS ks_eval_rf_secant (KS_RF *rf, const char *const desc, float A0, float tbw, float bw)
 
STATUS ks_eval_rf_hard (KS_RF *rf, const char *const desc, int duration, float flip)
 
STATUS ks_eval_rf_hard_optimal_duration (KS_RF *rf, const char *const desc, int order, float flip, float offsetFreq)
 
STATUS ks_eval_rf_binomial (KS_RF *rf, const char *const desc, int offResExc, int nPulses, float flip, float offsetFreq, int MTreduction)
 
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)
 
STATUS ks_eval_rf (KS_RF *rf, const char *const desc)
 
void ks_eval_rf_relink (KS_RF *rf)
 
STATUS ks_eval_seltrap (KS_TRAP *trap, const char *const desc, float slewrate, float slthick, float bw, int rfduration)
 
STATUS ks_eval_selwave (KS_WAVE *gradwave, const char *const desc, float slthick, float bw, int rfduration)
 
STATUS ks_eval_selrf_constrained (KS_SELRF *selrf, const char *const desc, float ampmax, float slewrate)
 
STATUS ks_eval_selrf (KS_SELRF *selrf, const char *const desc)
 
STATUS ks_eval_selrf2 (KS_SELRF *selrf, const char *const desc)
 
STATUS ks_eval_selrf1 (KS_SELRF *selrf, const char *const desc)
 
STATUS ks_eval_selrf1p (KS_SELRF *selrf, const char *const desc)
 
float ks_eval_findb1 (KS_SELRF *selrf, float max_b1, double scaleFactor, int sms_multiband_factor, int sms_phase_modulation_mode, float sms_slice_gap)
 
void ks_eval_transient_SPGR_FA_train_recursive (float *FA_train, float *MZ_train, int N, float E1, float target_MT, int n)
 
void ks_eval_transient_SPGR_FA_train_binary_search (float *FA_train, float *MZ_train, int N, float E1, float MTlo, float MThi, float total_FA)
 
STATUS ks_eval_check_FA_train (int N, float *FA_train, float *MZ_train, float total_FA)
 
STATUS ks_eval_transient_SPGR_FA_train (float *FA_train, int N, float TR, float T1, float total_FA)
 
STATUS ks_eval_sms_make_multiband (KS_SELRF *selrfMB, const KS_SELRF *selrf, const int sms_multiband_factor, const int sms_phase_modulation_mode, const float sms_slice_gap, int debug)
 
STATUS ks_eval_sms_get_phase_modulation (float *sms_phase_modulation, const int sms_multiband_factor, const int sms_phase_modulation_mode)
 
float ks_eval_sms_calc_slice_gap (int sms_multiband_factor, 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_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_factor, int nslices)
 
STATUS ks_eval_stretch_rf (KS_RF *rf, float stretch_factor)
 
STATUS ks_eval_sms_make_pins (KS_SELRF *selrfPINS, const KS_SELRF *selrf, float sms_slice_gap)
 
STATUS ks_eval_sms_make_pins_dante (KS_SELRF *selrfPINS, const KS_SELRF *selrf, float sms_slice_gap)
 
int ks_eval_findNearestNeighbourIndex (float value, const float *x, int length)
 
void ks_eval_linear_interp1 (const float *x, int x_length, const float *y, const float *xx, int xx_length, float *yy)
 
STATUS ks_eval_readtrap2readwave (KS_READTRAP *readtrap, KS_READWAVE *readwave)
 
STATUS ks_eval_trap2wave (KS_WAVE *wave, const KS_TRAP *trap)
 
STATUS ks_eval_coords2wave (KS_WAVE *wave, float *t, float *G, int num_coords, int dwell, const char *desc)
 
STATUS ks_eval_append_two_waves (KS_WAVE *first_wave, KS_WAVE *second_wave)
 
STATUS ks_eval_append_two_waveforms (KS_WAVEFORM first_waveform, KS_WAVEFORM second_waveform, int res1, int res2)
 
STATUS ks_eval_concatenate_waves (int num_waves, KS_WAVE *target_wave, KS_WAVE **waves_to_append)
 
STATUS ks_eval_epi_constrained (KS_EPI *epi, const char *const desc, float ampmax, float slewrate)
 
STATUS ks_eval_epi_setinfo (KS_EPI *epi)
 
STATUS ks_eval_epi_maxamp_slewrate (float *ampmax, float *slewrate, int xres, float quietnessfactor)
 
STATUS ks_eval_epi (KS_EPI *epi, const char *const desc, float quietnessfactor)
 
STATUS ks_eval_echotrain (KS_ECHOTRAIN *const echotrain)
 
GRAD_PULSE ks_eval_makegradpulse (KS_TRAP *trp, int gradchoice)
 
STATUS ks_eval_seqctrl_setminduration (KS_SEQ_CONTROL *seqctrl, int mindur)
 
STATUS ks_eval_seqctrl_setduration (KS_SEQ_CONTROL *seqctrl, int dur)
 
STATUS ks_eval_seqcollection_durations_setminimum (KS_SEQ_COLLECTION *seqcollection)
 
STATUS ks_eval_seqcollection_durations_atleastminimum (KS_SEQ_COLLECTION *seqcollection)
 
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)
 
void ks_grad_heat_reset ()
 
STATUS _grad_list_init (struct _grad_list *grads)
 
STATUS _grad_list_append (struct _grad_list *grads, t_list *grad, int board)
 
int _grad_compare (const void *_a, const void *_b)
 
STATUS _grad_list_remove_duplicates (struct _grad_list *grads)
 
STATUS _grad_list_add_sequence (struct _grad_list *grads, KS_SEQ_CONTROL *ctrl)
 
STATUS __wrap_getCornerPoints (FLOAT **p_time, FLOAT *ampl[3], FLOAT *pul_type[3], INT *num_totpoints, const LOG_GRAD *log_grad, const INT seq_entry_index, const INT samp_rate, const INT min_tr, const FLOAT dbdtinf, const FLOAT dbdtfactor, const FLOAT efflength, const INT max_encode_mode, const dbLevel_t debug)
 
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)
 
int ks_eval_rflimits (KS_SAR *sar, KS_SEQ_COLLECTION *seqcollection)
 
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)
 
void ks_eval_seqcollection_resetninst (KS_SEQ_COLLECTION *seqcollection)
 
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)
 
int ks_isIceHardware ()
 
int ks_default_ssitime ()
 
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)
 
_cvfloat * opuser_from_number (_cvfloat *cv)
 
unsigned int ks_calc_nextpow2 (unsigned int n)
 
int ks_calc_roundupms (int val)
 
STATUS ks_calc_filter (FILTER_INFO *echortf, int tsp, int duration)
 
STATUS ks_write_vector_bf (float *vec, uint32_t numel, const char *fname)
 
int ks_calc_bw2tsp (float bw)
 
float ks_calc_tsp2bw (int tsp)
 
float ks_calc_nearestbw (float bw)
 
float ks_calc_lower_rbw (float rbw)
 
float ks_calc_higher_rbw (float rbw)
 
float ks_calc_max_rbw (float ampmax, float fov)
 
int ks_calc_trap_time2area (KS_TRAP *trap, float area)
 
STATUS ks_calc_sliceplan (KS_SLICE_PLAN *slice_plan, int nslices, int slperpass)
 
STATUS ks_calc_sliceplan_interleaved (KS_SLICE_PLAN *slice_plan, int nslices, int slperpass, int ninterleaves)
 
STATUS ks_calc_sliceplan_sms (KS_SLICE_PLAN *slice_plan, int nslices, int slperpass, int multiband_factor)
 
STATUS ks_sms_check_divisibility (int *inpass_interleaves, const int sms_factor, const int nslices_per_pass, const int nslices)
 
int ks_calc_sms_min_gap (DATA_ACQ_ORDER *dacq, int nslices)
 
int ks_calc_slice_acquisition_order_smssingleacq_impl (DATA_ACQ_ORDER *dacq, int nslices, int interleave)
 
int ks_calc_slice_acquisition_order_smssingleacq (DATA_ACQ_ORDER *dacq, int nslices)
 
float ks_calc_integrate_coords (float *t, float *G, int num_coords)
 
int ks_get_center_encode (const KS_PHASEENCODING_PLAN *const peplan, const int shot, const int yres, const int zres)
 
int ks_get_center_encode_linearsweep (const KS_KSPACE_ACQ *kacq, const int etl, const KS_PF_EARLYLATE pf_earlylate_te)
 
void ks_print_seqcollection (KS_SEQ_COLLECTION *seqcollection, FILE *fp)
 
void ks_print_sliceplan (const KS_SLICE_PLAN slice_plan, FILE *fp)
 
void ks_print_scaninfo (const SCAN_INFO *scan_info, int nslices, const char *desc, FILE *fp)
 
void ks_print_waveform (const KS_WAVEFORM waveform, const char *filename, int res)
 
void ks_print_wave_part (const KS_WAVE *const wave, const char *filename, int startidx, int numel)
 
void ks_print_readwave (const KS_READWAVE *const readwave, const char *filename)
 
void ks_print_wave (const KS_WAVE *const wave, const char *filename)
 
void ks_print_read (KS_READ a, FILE *fp)
 
void ks_print_trap (KS_TRAP t, FILE *fp)
 
void ks_print_readtrap (KS_READTRAP r, FILE *fp)
 
void ks_print_phaser (KS_PHASER p, FILE *fp)
 
void ks_print_gradrfctrl (KS_GRADRFCTRL gradrfctrl, FILE *fp)
 
void ks_print_epi (KS_EPI s, FILE *fp)
 
void ks_print_rfpulse (RF_PULSE rfpulse, FILE *fp)
 
void ks_print_rf (KS_RF r, FILE *fp)
 
void ks_print_selrf (KS_SELRF r, FILE *fp)
 
STATUS ks_print_acqwaves (KS_SEQ_COLLECTION *seqcollection)
 
void ks_print_readwaves (KS_ECHOTRAIN *const echotrain, const char *suffix, int rhkacq_uid)
 
int ks_eval_clear_readwave (KS_READWAVE *readwave)
 
float get_t1 (float A, float s, float c)
 
float get_T (float A, float s, float c, float t1)
 
float get_b (float A, float c, float T, float t1)
 
void get_ramp (ks_rampparams_s *out, float A, float s, float c)
 
STATUS ks_area_to_amp (KS_WAVE *out, float area, float max_slew, float amp)
 
int ks_eval_append_blip (KS_WAVE *wave, const float max_s, const int max_points, const float area)
 
int ks_file_exist (char *filename)
 
void ks_plot_host_slicetime_path (char *path)
 
void ks_plot_host_slicetime_fullfile (char *fullfile)
 
void ks_plot_host_slicetime_delete ()
 
void ks_plot_host_slicetime_begin ()
 
void ks_plot_host_slicetime_endofslicegroup (const char *desc, const KS_PLOT_SLICEGROUP_MODE mode)
 
void ks_plot_host_slicetime_endofpass (KS_PLOT_PASS_MODE pass_mode)
 
void ks_plot_host_slicetime (const KS_SEQ_CONTROL *ctrl, int nslices, float *slicepos_mm, float slthick_mm, KS_PLOT_EXCITATION_MODE excmode)
 
void ks_plot_host_slicetime_end ()
 
void ks_plot_host (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_rtscales_amps (const KS_RT_SCALE_LOG *rtscalelog, FILE *fp, const int instance)
 
void ks_plot_host_rtscales_states (const KS_RT_SCALE_LOG *rtscalelog, FILE *fp, const int instance)
 
void ks_plot_host_wavestates (const KS_WAVE *waveptr, FILE *fp)
 
void ks_plot_write_peplans (const KS_PHASEENCODING_PLAN *const *plans, const int num_plans, FILE *fp)
 
void ks_plot_host_seqctrl_manyplans (const KS_SEQ_CONTROL *ctrl, const KS_PHASEENCODING_PLAN **plans, const int num_plans)
 
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)
 
STATUS ks_repeat_peplan (KS_PHASEENCODING_PLAN *peplan, const KS_PHASEENCODING_REPEAT_DESIGN *const repeat)
 
int ks_comp_kcoord_r_t (const void *a, const void *b)
 
int ks_comp_kcoord_dist_z_y (const void *a, const void *b)
 
int ks_comp_kview_dist_z_y (const void *a, const void *b)
 
int ks_comp_kcoord_dist_y_z (const void *a, const void *b)
 
int ks_comp_kview_dist_y_z (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_t_r (const void *a, const void *b)
 
int ks_comp_kview_y_z (const void *a, const void *b)
 
int ks_comp_kview_z_y (const void *a, const void *b)
 
int ks_comp_kview_y_r (const void *a, const void *b)
 
int ks_comp_kview_z_r (const void *a, const void *b)
 
int ks_comp_kview_e_tr (const void *a, const void *b)
 
int ks_comp_kview_e_rt (const void *a, const void *b)
 
int ks_comp_kview_e_zy (const void *a, const void *b)
 
int ks_comp_kview_e_yz (const void *a, const void *b)
 
int ks_comp_kview_e_yr (const void *a, const void *b)
 
int ks_comp_kview_e_zr (const void *a, const void *b)
 
int ks_comp_kview_e_sec_t (const void *a, const void *b)
 
int ks_comp_kview_y_z_descend (const void *a, const void *b)
 
int ks_comp_kview_z_y_descend (const void *a, const void *b)
 
ks_comparator ks_get_comp_kview_y_z (int ascend)
 
ks_comparator ks_get_comp_kview_z_y (int ascend)
 
void ks_get_matrix_from_kcoords (KS_KCOORD *K, const int num_coords, int *y, int *z)
 
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)
 
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)
 
int ks_readout_start_index (int center, int etl, int num_encodes_per_train)
 
KS_PEPLAN_SHOT_DISTRIBUTION ks_peplan_distribute_shots (const int etl, const int num_coords, const int center)
 
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)
 
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)
 
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)
 
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)
 
STATUS ks_assign_encodes (KS_VIEW *const views, const int num_views, const int start_encode, const int num_encodes, const int sign)
 
STATUS ks_pe_permute_pclo_rad (int *view_order, const int etl, const int center)
 
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)
 
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 *coord_types)
 
STATUS ks_phaseencoding_generate_2d_singleshot_cal (KS_PHASEENCODING_PLAN *phaseenc_plan_ptr, int yres)
 
int ks_cal_from_nacslines (int R, int nacslines)
 
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)
 
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)
 
STATUS ks_generate_2d_coords_cal (KS_KSPACE_ACQ *kacq, int num_desired_coords, int yres)
 
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)
 
void set_grid (int *grid, int Nz, int y, int z)
 
int get_grid (int *grid, int Nz, int y, int z)
 
float rand_float_in_range (float min, float max)
 
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)
 
int check_conflicts (int *grid, int Ny, int Nz, KS_KCOORD cand, float cand_radius)
 
void remove_element (KS_KCOORD *coord_array, int idx, int size)
 
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)
 
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)
 
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)
 

Variables

int cfsrmode
 
int cfgcoiltype
 
int rhkacq_uid
 
int ks_plot_filefmt
 
int ks_plot_kstmp
 
char ks_psdname [256]
 
struct _grad_heat_handle grad_heat_handle = {NULL, NULL, 0, NULL}
 
int gradHeatMethod
 
int enforce_minseqseg
 
_cvint _optr
 
int minseqcable_t
 
int minseqbusbar_t
 
int ks_plot_enable = 0
 

Detailed Description

This file contains functions only accessible on HOST

Macro Definition Documentation

◆ MSS_FLAGS_T

#define MSS_FLAGS_T   void

◆ KS_INIT_RAMPPARAMS

#define KS_INIT_RAMPPARAMS   {0, 0, 0, 0}

Typedef Documentation

◆ ks_comparator

typedef int(* ks_comparator) (const void *, const void *)

Function Documentation

◆ ftruncate()

int ftruncate ( int  fd,
off_t  length 
)

◆ inittargets()

STATUS inittargets ( LOG_GRAD *  lgrad,
PHYS_GRAD *  pgrad 
)

◆ obloptimize_epi()

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

◆ minseqrfamp()

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

◆ maxsar()

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

◆ minseq()

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

◆ ks_init_read()

void ks_init_read ( KS_READ read)

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

Parameters
[out]readPointer to KS_READ
Returns
void
54  {
55  KS_READ defacq = KS_INIT_READ;
56  *read = defacq;
57 }
Core sequence object that handles a data acquisition window
Definition: KSFoundation.h: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_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_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_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_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_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_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_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
int32_t i
Definition: KSFoundation_tgt.c:1694
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 }
int32_t i
Definition: KSFoundation_tgt.c:1694
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_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_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_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 ( struct _readtrap_s *  readtrap,
float  wanted_paddingarea_pre,
float  wanted_paddingarea_post 
)
596  {
597  if (readtrap->rampsampling) {
598  return ks_error("%s: No support for rampsampled readouts with asymmetric padding", __FUNCTION__);
599  }
600  float previous_paddingarea = readtrap->paddingarea;
601  float amp = readtrap->grad.amp;
602  float ramp_area = readtrap->grad.ramptime * amp/ 2.0;
603 
604  float actual_pre_area = isNotSet(wanted_paddingarea_pre) ? previous_paddingarea : FMax(2, ramp_area, wanted_paddingarea_pre);
605  float actual_post_area = isNotSet(wanted_paddingarea_post) ? previous_paddingarea : FMax(2, ramp_area, wanted_paddingarea_post);
606 
607  int plateau_delay_pre = (int)((actual_pre_area - ramp_area)/ amp);
608  int plateau_delay_post = (int)((actual_post_area - ramp_area)/ amp);
609 
610  readtrap->grad.plateautime = RUP_GRD(readtrap->acq.duration + plateau_delay_pre + plateau_delay_post);
611  readtrap->acqdelay = RUP_FACTOR(readtrap->grad.ramptime + plateau_delay_pre, 2);
612 
613  /* Update convinient fields */
614  readtrap->area2center += actual_pre_area - previous_paddingarea - ramp_area;
615  readtrap->paddingarea = actual_pre_area - ramp_area;
616  readtrap->grad.duration = RUP_GRD(2*readtrap->grad.ramptime + readtrap->grad.plateautime);
617  readtrap->time2center = RUP_GRD((int) ((readtrap->area2center - ramp_area) / amp + readtrap->grad.ramptime));
618  return SUCCESS;
619 }
STATUS ks_error(const char *format,...) __attribute__((format(printf
Common error message function for HOST and TGT
#define isNotSet(a)
Definition: KSFoundation.h:164

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

STATUS ks_eval_ramp ( KS_WAVE ramp,
float  slew,
float  end_amp,
int  dwell 
)
633  {
634  float ramp_dur = KS_RUP_GRD_FLOAT(fabs(end_amp) / slew);
635 
636  float t[3] = {0.0};
637  float G[3] = {0.0};
638 
639  /* simple ramp case */
640  int act_dur = RUP_FACTOR((int)(ramp_dur), (int)(2 * dwell));
641  G[2] = end_amp;
642  t[1] = act_dur - ramp_dur;
643  t[2] = act_dur;
644 
645  return ks_eval_coords2wave(ramp, t, G, 3, dwell, "ramp");
646 }
STATUS ks_eval_coords2wave(KS_WAVE *wave, float *t, float *G, int num_coords, int dwell, const char *desc)
Definition: KSFoundation_host.c:4383
#define KS_RUP_GRD_FLOAT(X)
Definition: KSFoundation.h:170

◆ ks_calc_unconstrained_transition()

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 
)
654  {
655 
656  if (end_amp < 0 || ampmax <= 0 || slew <= 0) {
657  return KS_THROW("Negative entries are not supported( end_amp = %.2f, ampmax = %.2f, slew = %.2f)", end_amp, ampmax, slew);
658  }
659  if (end_amp > ampmax) {
660  return KS_THROW("ampmax > end_amp (%f > %f)", ampmax, end_amp);
661  }
662  if (!G || !t || !num_points) {
663  return KS_THROW("NULL inputs");
664  }
665 
666  const float fast_ramp_dur = KS_RUP_GRD_FLOAT(end_amp / slew);
667  const float fast_ramp_area = end_amp * fast_ramp_dur / 2.0;
668 
669  if (req_area <= fast_ramp_area) {
670  G[0] = 0.0f;
671  t[0] = 0.0f;
672  G[1] = end_amp;
673  t[1] = fast_ramp_dur;
674  *num_points = 2;
675  return SUCCESS;
676  }
677 
678  /* Assume an extra plateau strategy and calculate the extra time */
679  const float extra_plateau_time = (req_area - fast_ramp_area) / end_amp;
680  if (extra_plateau_time <= min_plateau) {
681  t[3] = KS_RUP_GRD_FLOAT(fast_ramp_dur + extra_plateau_time);
682  t[2] = t[3] - extra_plateau_time;
683  t[1] = t[2] - fast_ramp_dur;
684  t[0] = 0;
685 
686  G[3] = end_amp;
687  G[2] = end_amp;
688  G[1] = 0.0f;
689  G[0] = 0.0f;
690 
691  *num_points = 4;
692  return SUCCESS;
693  }
694 
695  /* first calculate area for maxamp and minimum plateau */
696  float plat_dur = min_plateau;
697  float plat_amp = ampmax;
698  float rampdown_dur = (plat_amp - end_amp) / slew;
699  const float simple_ramp_area = 0.5 * end_amp * end_amp / slew; /* Not raster aligned */
700  float rampup_dur = plat_amp / slew;
701  const float area = plat_dur * plat_amp
702  + rampup_dur * plat_amp
703  - simple_ramp_area;
704 
705  if (area < req_area) {
706  /* prolong plateau */
707  plat_dur += (req_area - area) / plat_amp;
708  } else {
709  /* Use minimum plat_dur and keep max slew */
710  plat_amp = (-plat_dur * slew + sqrt( plat_dur*plat_dur*slew*slew + 2*end_amp*end_amp + 4*req_area*slew )) / 2 ;
711  rampdown_dur = (plat_amp - end_amp) / slew;
712  rampup_dur = plat_amp / slew;
713 
714  }
715  if (rampdown_dur < 0) {
716  return KS_THROW("Ops");
717  }
718 
719  t[4] = KS_RUP_GRD_FLOAT(rampup_dur + plat_dur + rampdown_dur);
720  t[3] = t[4] - rampdown_dur;
721  t[2] = t[3] - plat_dur;
722  t[1] = t[2] - rampup_dur;
723  t[0] = 0.0f;
724 
725  G[4] = end_amp;
726  G[3] = plat_amp;
727  G[2] = plat_amp;
728  G[1] = 0.0f;
729  G[0] = 0.0f;
730 
731  *num_points = 5;
732 
733  return SUCCESS;
734 
735 } /* ks_calc_unconstrained_transition() */
#define KS_RUP_GRD_FLOAT(X)
Definition: KSFoundation.h:170
#define KS_THROW(format,...)
Definition: KSFoundation.h:181

◆ ks_calc_exact_transition()

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 
)
743  {
744 
745  STATUS status;
746 
747  if (end_amp < 0 || ampmax <= 0 || slew <= 0) {
748  return KS_THROW("Negative entries are not supported( end_amp = %.2f, ampmax = %.2f, slew = %.2f)", end_amp, ampmax, slew);
749  }
750  if (!G || !t || !num_points) {
751  return KS_THROW("NULL inputs");
752  }
753 
754  min_duration = KS_RUP_GRD_FLOAT(min_duration);
755 
756  const float fast_ramp_dur = KS_RUP_GRD_FLOAT(end_amp / slew);
757  const float fast_ramp_area = end_amp * fast_ramp_dur / 2.0;
758 
759  /* Fast ramp + plateau with duration at least equal to min_duration */
760  const float ramp_plat_dur = min_duration > fast_ramp_dur ? min_duration : fast_ramp_dur;
761  const float ramp_plat_area = fast_ramp_area + (ramp_plat_dur - fast_ramp_dur) * end_amp;
762 
763  if (req_area >= ramp_plat_area) {
764 
765  /* Batwing is needed */
766  status = ks_calc_unconstrained_transition(G, t, num_points,
767  req_area, end_amp,
768  slew, ampmax, min_plateau);
769  KS_RAISE(status);
770 
771  return SUCCESS;
772  }
773 
774  const float plateau_dur = 2*req_area/end_amp - min_duration;
775  if (plateau_dur > min_plateau) {
776 
777  /* ramp + plateau */
778  t[0] = 0.0f;
779  t[1] = min_duration - plateau_dur;
780  t[2] = min_duration;
781 
782  G[0] = 0.0f;
783  G[1] = end_amp;
784  G[2] = end_amp;
785 
786  *num_points = 3;
787 
788  return SUCCESS;
789  }
790 
791  if (req_area >= fast_ramp_area) {
792 
793  /* Shorter ramp */
794  const float ramp_dur = 2*req_area/end_amp;
795  t[2] = KS_RUP_GRD_FLOAT(ramp_dur);
796  t[1] = t[2] - ramp_dur;
797  t[0] = 0;
798 
799  G[2] = end_amp;
800  G[1] = 0;
801  G[0] = 0;
802 
803  *num_points = 3;
804 
805  return SUCCESS;
806  }
807 
808  /* A negative blip is needed */
809  const float blip_area = fast_ramp_area - req_area;
810  float ramp_dur = ampmax/slew;
811  float plat_dur = min_plateau;
812  const float maxamp_minplat_area = (plat_dur + ramp_dur) * ampmax;
813  if (blip_area < maxamp_minplat_area) {
814  ramp_dur = sqrt(plat_dur*plat_dur/4.0 + blip_area/slew) - plat_dur / 2.0;
815  } else {
816  plat_dur += (blip_area - maxamp_minplat_area) / ampmax;
817  }
818 
819  t[5] = KS_RUP_GRD_FLOAT(fast_ramp_dur + plat_dur + 2*ramp_dur);
820  t[4] = t[5] - fast_ramp_dur;
821  t[3] = t[4] - ramp_dur;
822  t[2] = t[3] - plat_dur;
823  t[1] = t[2] - ramp_dur;
824  t[0] = 0;
825 
826  G[5] = end_amp;
827  G[4] = 0;
828  G[3] = -blip_area / (plat_dur + ramp_dur);
829  G[2] = G[3];
830  G[1] = 0;
831  G[0] = 0;
832 
833  /* TODO: dilate the ramp and the blip is there is extra time? */
834 
835  *num_points = 6;
836 
837  return SUCCESS;
838 }
#define KS_RUP_GRD_FLOAT(X)
Definition: KSFoundation.h:170
#define KS_RAISE(status)
Definition: KSFoundation.h:190
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
#define KS_THROW(format,...)
Definition: KSFoundation.h:181

◆ ks_calc_minslew_transition()

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 
)
846  {
847 
848  STATUS status;
849 
850  if (end_amp < 0 || ampmax <= 0 || slew <= 0) {
851  return KS_THROW("Negative entries are not supported( end_amp = %.2f, ampmax = %.2f, slew = %.2f)", end_amp, ampmax, slew);
852  }
853  if (!G || !t || !num_points) {
854  return KS_THROW("NULL inputs");
855  }
856 
857  min_duration = KS_RUP_GRD_FLOAT(min_duration);
858  const float fast_ramp_dur = KS_RUP_GRD_FLOAT(end_amp / slew);
859  if (min_duration < fast_ramp_dur) {
860  /* There is no available time to reduce slew */
861  min_duration = fast_ramp_dur;
862  }
863 
864  const float slow_ramp_area = end_amp * min_duration / 2.0;
865 
866  if (req_area <= slow_ramp_area) {
867  G[0] = 0.0f;
868  t[0] = 0.0f;
869  G[1] = end_amp;
870  t[1] = min_duration;
871  *num_points = 2;
872  return SUCCESS;
873  }
874 
875  status = ks_calc_exact_transition(G, t, num_points,
876  req_area, end_amp, min_duration,
877  slew, ampmax, min_plateau);
878  KS_RAISE(status);
879 
880  return SUCCESS;
881 }
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
#define KS_RUP_GRD_FLOAT(X)
Definition: KSFoundation.h:170
#define KS_RAISE(status)
Definition: KSFoundation.h:190
#define KS_THROW(format,...)
Definition: KSFoundation.h:181

◆ ks_calc_maxarea_transition()

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 
)
889  {
890 
891  STATUS status;
892 
893  /* Generate the fastest strategy first to see if there is any extra time */
894  status = ks_calc_unconstrained_transition(G, t, num_points,
895  req_area, end_amp,
896  slew, ampmax, min_plateau);
897  KS_RAISE(status);
898 
899  if (t[(*num_points) -1] >= max_duration) {
900  return SUCCESS;
901  }
902 
903  max_duration = KS_RUP_GRD_FLOAT(max_duration);
904 
905  const float fast_ramp_dur = KS_RUP_GRD_FLOAT(end_amp / slew);
906 
907  if (max_duration < fast_ramp_dur + min_plateau) {
908 
909  /* ramp + plateau */
910  t[0] = 0;
911  t[1] = fast_ramp_dur;
912  t[2] = max_duration;
913 
914  G[0] = 0;
915  G[1] = end_amp;
916  G[2] = end_amp;
917 
918  *num_points = 3;
919 
920 
921  return SUCCESS;
922  }
923 
924  /* Batwing */
925  const float max_rampup_dur = ampmax / slew;
926  const float ramp_dur = (max_duration - fast_ramp_dur - min_plateau)/2;
927  if ( (fast_ramp_dur + ramp_dur) > max_rampup_dur) {
928  /* Reached ampmax, add as much plateau time as possible */
929  float rampdown_dur = (ampmax - end_amp) /slew;
930  t[0] = 0;
931  t[1] = max_rampup_dur;
932  t[2] = max_duration - rampdown_dur;
933  t[3] = max_duration;
934 
935  G[0] = 0;
936  G[1] = ampmax;
937  G[2] = ampmax;
938  G[3] = end_amp;
939  *num_points = 4;
940 
941  return SUCCESS;
942  }
943 
944  /* Batwing with min plateau */
945  t[0] = 0;
946  t[1] = fast_ramp_dur + ramp_dur;
947  t[2] = t[1] + min_plateau;
948  t[3] = max_duration;
949 
950  G[0] = 0;
951  G[1] = t[1] * slew;
952  G[2] = G[1];
953  G[3] = end_amp;
954 
955  *num_points = 4;
956 
957 
958  return SUCCESS;
959 }
#define KS_RUP_GRD_FLOAT(X)
Definition: KSFoundation.h:170
#define KS_RAISE(status)
Definition: KSFoundation.h:190
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

◆ 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
int32_t i
Definition: KSFoundation_tgt.c:1694
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 ( struct _readtrap_s *  readtrap,
const char *const  desc,
const float  ampmax,
const float  slewrate,
const int  t_A,
const int  sample_duration,
const float  area_post,
const float  area_total 
)
1267  {
1268  int nreadpixels;
1269  float amp;
1270  int t_Sr_pre = 0;
1271  int t_Sp = 0;
1272  int t_Sr_post = 0;
1273  int t_Ep = 0;
1274  int t_Er = 0;
1275  int time_to_center = 0;
1276  /* reset KS_TRAP's */
1277  ks_init_trap(&readtrap->grad);
1278  ks_init_wave(&readtrap->omega);
1279 
1280  /* Description */
1281  strncpy(readtrap->grad.description, desc, KS_DESCRIPTION_LENGTH - 1);
1282 
1283  /* Programmer's error */
1284  if (readtrap->res % 2 || readtrap->res <= 0 || readtrap->res > 2048) {
1285  return ks_error("ks_eval_readtrap(%s): field 'res' must be even and in the range 2-2048", desc);
1286  }
1287  if (readtrap->fov < 10 || readtrap->fov > 600) {
1288  return ks_error("ks_eval_readtrap(%s): field 'fov' must be in the range 10-600 mm", desc);
1289  }
1290  if (abs(readtrap->nover) > readtrap->res / 2) {
1291  return ks_error("ks_eval_readtrap(%s): field 'nover' may not exceed res/2", desc);
1292  }
1293  if (abs(readtrap->nover)) {
1294  nreadpixels = readtrap->res / 2 + abs(readtrap->nover);
1295  } else {
1296  nreadpixels = readtrap->res;
1297  }
1298  if (nreadpixels % 2) {
1299  return ks_error("ks_eval_readtrap(%s): number of sampling points must be even (%d)", desc, nreadpixels);
1300  }
1301 
1302  float readpixelarea = ks_calc_fov2gradareapixel(readtrap->fov);
1303 
1304  /* First assume that both ramps can be used for data sampling. */
1305  /* delay of ACQ start relative to the beginning of the attack ramp.
1306  For rampsampled cases, this value should be at least one dwell time (tsp) */
1307 
1308  /* A is the ramp area where no sampling occurs */
1309  float A = 0.5 * slewrate * t_A * t_A;
1310 
1311  /* Calculate the required trail time, i.e. the time from end of sampling until end of gradient */
1312  int t_ErA = sqrt(2*area_post/slewrate);
1313 
1314  /* tE_r is the time from end of sampling until start of A */
1315  t_Er = t_ErA - t_A;
1316 
1317  /* Total duration */
1318  int t_total = sample_duration + t_ErA;
1319 
1320  /* Sample area = B + C */
1321  /* Sample area to center is B plus the part of C up to k-space center */
1322  float sample_area = readpixelarea * nreadpixels; /* G/cm * us*/
1323  float area_to_center = A + readpixelarea * abs(readtrap->nover); /* Assumes center-out sampling */
1324 
1325  /* The total area is known, as well as the duration and slewrate. Calculate the ramp time */
1326  int t_ramp = (t_total - sqrtf(t_total*t_total - 4.0 * area_total/slewrate)) / 2.0;
1327  t_ramp = RUP_FACTOR(t_ramp, 4);
1328  /* Check the dual ramp sampling assumption */
1329  if (t_ErA > t_ramp) {
1330  /* There is not enough ramp area to cover the required area_post.
1331  Recalculate t_ramp as there will only be one ramp sampled side: */
1332  t_Sr_pre = sample_duration - t_A - sqrtf( (slewrate*(sample_duration*sample_duration - t_A*t_A) -2*sample_area)/ slewrate);
1333  if (t_Sr_pre < 0) { return ks_error("Ops"); }
1334  t_ramp = RUP_FACTOR(t_A + t_Sr_pre, 4);
1335  t_Sr_pre = t_ramp - t_A;
1336  amp = slewrate * t_ramp;
1337  float area_ramp = t_ramp * amp / 2;
1338  float area_plateau = area_total - 2*area_ramp;
1339  int plateau_time = RUP_FACTOR( (int) (area_plateau / amp), 4);
1340  t_Sp = sample_duration - t_ramp;
1341  t_Ep = (area_post - area_ramp) / amp;
1342 
1343  readtrap->grad.ramptime = t_ramp;
1344  readtrap->grad.plateautime = plateau_time;
1345 
1346 
1347  /* Doesn't matter */
1348  t_Sr_post = 0;
1349  t_Er = t_Sr_pre;
1350  t_ErA = t_ramp; /* Doesn't matter */
1351  t_total = 2*t_ramp + t_Sp + t_Ep; /* Doesn't matter */
1352 
1353  ks_error("%s: Single ramp sample case", __FUNCTION__);
1354  /* Calculate time to center */
1355  if (area_ramp > area_to_center) { /* Echo occurs on ramp up */
1356  time_to_center = sqrtf(2*area_to_center/slewrate);
1357  if (time_to_center < t_A) {
1358  return ks_error("%s: Echo occurs before sampling (ramptime: %.2f, time_to_center %.2f)", __FUNCTION__, t_A/1000.0f, time_to_center/1000.0f);
1359  }
1360  } else { /* Echo occurs on plateau */
1361  float plateau_time_to_center = (area_to_center - area_ramp) / amp;
1362  time_to_center = t_ramp + plateau_time_to_center;
1363  }
1364  } else { /* Dual ramp sampling holds */
1365  /* Double ramp sampled case - No extra area on plateau */
1366  ks_error("%s: Double ramp sample case", __FUNCTION__);
1367  amp = slewrate * t_ramp;
1368  float area_ramp = t_ramp * amp / 2;
1369  float area_plateau = area_total - 2*area_ramp;
1370  t_Sp = RUP_FACTOR( (int) (area_plateau / amp), 4);
1371  t_Sr_pre = t_ramp - t_A;
1372  t_Sr_post = t_ramp - t_A - t_Er;
1373  t_Ep = 0;
1374  readtrap->grad.ramptime = RUP_FACTOR(t_ramp, 4);
1375  readtrap->grad.plateautime = RUP_FACTOR(t_Sp + t_Ep, 4);
1376  /* t_Er already set */
1377 
1378  area_plateau = t_Sp * amp;
1379  if (area_ramp > area_to_center) { /* Echo occurs on ramp up */
1380  time_to_center = sqrtf(2*area_to_center/slewrate);
1381  if (time_to_center < t_A) {
1382  return ks_error("%s: Echo occurs before sampling (ramptime: %.2f, time_to_center %.2f)", __FUNCTION__, t_A/1000.0f, time_to_center/1000.0f);
1383  }
1384  } else if (area_to_center <= (area_ramp + area_plateau)) { /* Echo occurs on plateau */
1385  float plateau_time_to_center = (area_to_center - area_ramp) / amp;
1386  time_to_center = t_ramp + plateau_time_to_center;
1387  } else if (area_to_center <= (2*area_ramp + area_plateau)) { /* Echo occurs on ramp down */
1388  float rem_area = area_to_center - area_plateau - area_ramp;
1389  float time_to_center_from_ramp_down = amp/slewrate - sqrtf( powf(amp/slewrate,2) - 2*rem_area/slewrate);
1390  time_to_center = t_ramp + t_Sp + time_to_center_from_ramp_down;
1391  } else {
1392  return ks_error("%s: There is no echo occurring on this waveform", __FUNCTION__);
1393  }
1394  }
1395 
1396  if (amp > ampmax) {
1397  return ks_error("%s: Amp limit exceeded (%.2f > %.2f)", __FUNCTION__, amp, ampmax);
1398  }
1399 
1400  /* Setup object properties */
1401  readtrap->acqdelay = t_A;
1402  readtrap->grad.amp = amp;
1403  readtrap->grad.duration = 2*readtrap->grad.ramptime + readtrap->grad.plateautime;
1404  readtrap->grad.area = (readtrap->grad.ramptime + readtrap->grad.plateautime) * amp;
1405 
1406  float dwelltime = 1.0/(4257.59 * readtrap->fov * 0.1 * amp * 0.000001);
1407  dwelltime = 2.0;
1408  readtrap->acq.rbw = ks_calc_tsp2bw((int)dwelltime);
1409  dwelltime = ks_calc_bw2tsp(readtrap->acq.rbw);
1410  readtrap->area2center = area_to_center;
1411  readtrap->time2center = time_to_center;
1412  readtrap->acq.duration = RUP_FACTOR(t_Sr_pre + t_Sp + t_Sr_post, (int) (2 * dwelltime));
1413  if (ks_eval_read(&readtrap->acq, "read") == SUCCESS) ;
1414  /* Acquisition window */
1415  /* if (ks_eval_read(&readtrap->acq, "echo") == FAILURE) { return FAILURE; } */
1416 
1417  /* Omega board*/
1418  float ramp_sample_period = readtrap->grad.ramptime - readtrap->acqdelay;
1419  float t[4] = {0.0f, ramp_sample_period, (float)readtrap->acq.duration - ramp_sample_period, (float)readtrap->acq.duration};
1420  float start_amp = readtrap->grad.amp / readtrap->grad.ramptime * readtrap->acqdelay;
1421  float G[4] = {start_amp, readtrap->grad.amp, readtrap->grad.amp, start_amp};
1422  int k;
1423  /*ensure 150 mm fov offset is possible*/
1424  for (k = 0; k < 4; k++) {
1425  G[k] *= GAM / 10.0;
1426  }
1427  KS_DESCRIPTION omega_description;
1428  ks_create_suffixed_description(omega_description, readtrap->grad.description, ".omega");
1429  STATUS status;
1430  status = ks_eval_coords2wave(&readtrap->omega, t, G, 4, GRAD_UPDATE_TIME, omega_description);
1431  KS_RAISE(status);
1432  readtrap->omega.fs_factor = 0.1; /* so 1 bit of iamp = 0.1 mm */
1433 
1434  return SUCCESS;
1435 }
float ks_calc_tsp2bw(int tsp)
Convert dwell time to receiver bandwidth
Definition: KSFoundation_host.c:6212
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_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
#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
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
#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
char KS_DESCRIPTION[KS_DESCRIPTION_LENGTH]
Definition: KSFoundation.h:351

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

STATUS ks_eval_selwave ( KS_WAVE gradwave,
const char *const  desc,
float  slthick,
float  bw,
int  rfduration 
)
2928  {
2929 
2930  /* Reset all fields and waveforms */
2931  ks_init_wave(gradwave);
2932 
2933  if ((rfduration < GRAD_UPDATE_TIME) || (rfduration % GRAD_UPDATE_TIME)) {
2934  return ks_error("%s: RF duration must be positive and divisible by 4", __FUNCTION__);
2935  }
2936 
2937  if (slthick < 0 || bw < 0) {
2938  /* N.B.: slthick may be exactly 0, in which case a zero amp gradient is created */
2939  return ks_error("%s: slice thickness & bandwidth cannot be negative", __FUNCTION__);
2940  }
2941 
2942  if (desc == NULL || desc[0] == ' ') {
2943  return ks_error("ks_eval_selwave: wave name (2nd arg) cannot be NULL or begin with a space");
2944  }
2945 
2946  strncpy(gradwave->description, desc, KS_DESCRIPTION_LENGTH - 1);
2947 
2948  float amp = ks_calc_selgradamp(bw, slthick); /* amp [G/cm] */
2949  gradwave->duration = rfduration; /* total duration [us] */
2950  gradwave->res = rfduration / GRAD_UPDATE_TIME;
2951 
2952  int i;
2953  for (i=0; i<gradwave->res; i++) {
2954  gradwave->waveform[i] = amp;
2955  }
2956 
2957  gradwave->area = ks_wave_sum(gradwave);
2959 
2960  return SUCCESS;
2961 
2962 }
Definition: KSFoundation.h:2359
STATUS ks_error(const char *format,...) __attribute__((format(printf
Common error message function for HOST and TGT
int gradwave_units
Definition: KSFoundation.h:751
float area
Definition: KSFoundation.h:757
#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
float ks_wave_sum(const KS_WAVE *wave)
Returns the sum of a KS_WAVE sequence object
Definition: KSFoundation_common.c:1536
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 res
Definition: KSFoundation.h:746
KS_WAVEFORM waveform
Definition: KSFoundation.h:748
int duration
Definition: KSFoundation.h:747

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

STATUS ks_eval_readtrap2readwave ( KS_READTRAP readtrap,
KS_READWAVE readwave 
)
4312  {
4313  STATUS status;
4314 
4315  float t[4] = {0};
4316  float G[4] = {0};
4317 
4318  t[0] = 0; G[0] = 0;
4319  t[1] = readtrap->grad.ramptime; G[1] = readtrap->grad.amp;
4320  t[2] = t[1] + readtrap->grad.plateautime; G[2] = G[1];
4321  t[3] = t[2] + t[1]; G[3] = 0;
4322 
4323  KS_WAVE gradwave = KS_INIT_WAVE;
4324  status = ks_eval_coords2wave(&gradwave, t, G, 4, GRAD_UPDATE_TIME, "monkeypoo");
4325 
4326  return status;
4327 
4328 
4329 }
int plateautime
Definition: KSFoundation.h:672
Core sequence object making arbitrary waveforms on any board (using float data format)
Definition: KSFoundation.h:743
STATUS ks_eval_coords2wave(KS_WAVE *wave, float *t, float *G, int num_coords, int dwell, const char *desc)
Definition: KSFoundation_host.c:4383
#define KS_INIT_WAVE
Definition: KSFoundation.h:295
KS_TRAP grad
Definition: KSFoundation.h:1561
float amp
Definition: KSFoundation.h:669
int ramptime
Definition: KSFoundation.h:671

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

◆ _grad_list_init()

STATUS _grad_list_init ( struct _grad_list grads)
5055  {
5056  int i;
5057 
5058  grads->max_time = 0;
5059 
5060  for (i=0; i<3; ++i) {
5061  grads->buffer_sizes[i] = 1024;
5062  grads->num_grads[i] = 1;
5063 
5064  t_list *p = (t_list*)malloc(grads->buffer_sizes[i]*sizeof(t_list));
5065  if (!p) {
5066  return KS_THROW("failed allocation");
5067  }
5068  grads->data[i] = p;
5069  grads->data[i][0].time = 0;
5070  grads->data[i][0].ampl = 0.0f;
5071  grads->data[i][0].ptype = G_USER;
5072  }
5073  return SUCCESS;
5074 }
int num_grads[3]
Definition: KSFoundation_host.c:5051
int max_time
Definition: KSFoundation_host.c:5052
int buffer_sizes[3]
Definition: KSFoundation_host.c:5050
t_list * data[3]
Definition: KSFoundation_host.c:5049
#define KS_THROW(format,...)
Definition: KSFoundation.h:181

◆ _grad_list_append()

STATUS _grad_list_append ( struct _grad_list grads,
t_list *  grad,
int  board 
)
5079  {
5080  if (board < 0 || board > 2) {
5081  /* Board index out of bounds. Skipping... */
5082  return SUCCESS;
5083  }
5084 
5085  if (grads->num_grads[board] >= grads->buffer_sizes[board]) {
5086  /* resize the buffer by 50%
5087  add 4 on top just in case the size starts at zero or close to it */
5088  grads->buffer_sizes[board] = (1.5*grads->num_grads[board]) + 4;
5089  t_list *p = (t_list*)realloc(grads->data[board], grads->buffer_sizes[board]*sizeof(t_list));
5090  if (!p) {
5091  return KS_THROW("failed reallocation");
5092  }
5093  grads->data[board] = p;
5094  }
5095 
5096  grads->data[board][grads->num_grads[board]] = *grad;
5097  grads->num_grads[board]++;
5098 
5099  return SUCCESS;
5100 }
int num_grads[3]
Definition: KSFoundation_host.c:5051
int buffer_sizes[3]
Definition: KSFoundation_host.c:5050
t_list * data[3]
Definition: KSFoundation_host.c:5049
#define KS_THROW(format,...)
Definition: KSFoundation.h:181

◆ _grad_compare()

int _grad_compare ( const void *  _a,
const void *  _b 
)
5105  {
5106 
5107  t_list *a = (t_list*)_a;
5108  t_list *b = (t_list*)_b;
5109 
5110  return a->time - b->time;
5111 }

◆ _grad_list_remove_duplicates()

STATUS _grad_list_remove_duplicates ( struct _grad_list grads)
5116  {
5117  int b, i, j;
5118  const char board[] = "XYZ";
5119 
5120  for (b=0; b<3; ++b) {
5121 
5122  if (grads->num_grads[b] <= 1) {
5123  continue;
5124  }
5125 
5126  /* sort */
5127  qsort(grads->data[b], grads->num_grads[b], sizeof(t_list), _grad_compare);
5128 
5129 
5130  for (i=0, j=1; j<grads->num_grads[b]; ++j) {
5131  if (grads->data[b][i].time > grads->data[b][j].time) {
5132  return KS_THROW("entries on board %c need to be time sorted", board[b]);
5133  }
5134 
5135  if (grads->data[b][i].time < grads->data[b][j].time) {
5136  /* the entry at j needs to be kept, update i to the new position for this entry */
5137  ++i;
5138  /* copy the entry if necessary */
5139  if (i != j) {
5140  grads->data[b][i] = grads->data[b][j];
5141  }
5142  continue;
5143  }
5144 
5145  /* equal time */
5146  /* corner points marked with ptype G_CONSTANTS are extra zeros and
5147  may be dropped even if the amplitude does not match */
5148  if (grads->data[b][j].ptype != G_CONSTANT &&
5149  fabs(grads->data[b][i].ampl - grads->data[b][j].ampl) > /* tolerance */ 0.01 *
5150  (fabs(grads->data[b][i].ampl) + fabs(grads->data[b][i].ampl))) {
5151 
5152  if (grads->data[b][i].ptype == G_CONSTANT) {
5153  /* Swap out the i-th entry for the j-th */
5154  grads->data[b][i] = grads->data[b][j];
5155  } else {
5156  /* Neither of the entries has ptype G_CONSTANT */
5157  return KS_THROW("amplitude differ (%.4f != %.4f) for the same time point (%d) on board %c", grads->data[b][i].ampl, grads->data[b][j].ampl, grads->data[b][i].time, board[b]);
5158  }
5159  }
5160 
5161  /* nothing to do... the entry at j will be "discarded" */
5162  }
5163 
5164  grads->num_grads[b] = i+1;
5165  }
5166 
5167  return SUCCESS;
5168 }
int num_grads[3]
Definition: KSFoundation_host.c:5051
t_list * data[3]
Definition: KSFoundation_host.c:5049
#define KS_THROW(format,...)
Definition: KSFoundation.h:181
int _grad_compare(const void *_a, const void *_b)
Definition: KSFoundation_host.c:5105

◆ _grad_list_add_sequence()

STATUS _grad_list_add_sequence ( struct _grad_list grads,
KS_SEQ_CONTROL ctrl 
)
5173  {
5174  int i, j, k;
5175  STATUS s;
5176 
5177  if (!ctrl || !grads) {
5178  return KS_THROW("NULL inputs");
5179  }
5180 
5181  /* Add trapezoids */
5182  for (i = 0; i < ctrl->gradrf.numtrap; ++i) { /* each unique trap object */
5183  const KS_TRAP* trap = ctrl->gradrf.trapptr[i];
5184 
5185  for (j = 0; j < trap->base.ninst; ++j) { /* each instance of the trap */
5186  KS_SEQLOC loc = trap->locs[j];
5187  const float rtscale = ks_rt_scale_log_get(&trap->rtscaling, j, ctrl->current_playout);
5188 
5189  /* add the starting corner */
5190  t_list grad = {grads->max_time + loc.pos, 0.0, G_USER};
5191  s = _grad_list_append(grads, &grad, loc.board);
5192  KS_RAISE(s);
5193 
5194  /* add the end of the ramp */
5195  grad.time += trap->ramptime;
5196  grad.ampl = trap->amp * loc.ampscale * rtscale;
5197  s = _grad_list_append(grads, &grad, loc.board);
5198  KS_RAISE(s);
5199 
5200  /* add the end of the plateau */
5201  grad.time += trap->plateautime;
5202  s = _grad_list_append(grads, &grad, loc.board);
5203  KS_RAISE(s);
5204 
5205  /* add the end of the trapezoid */
5206  grad.time += trap->ramptime;
5207  grad.ampl = 0.0;
5208  s = _grad_list_append(grads, &grad, loc.board);
5209  KS_RAISE(s);
5210  }
5211  }
5212 
5213  /* Add waves */
5214  for (i = 0; i < ctrl->gradrf.numwave; ++i) { /* each unique wave object */
5215  const KS_WAVE* waveptr = ctrl->gradrf.waveptr[i];
5216 
5217  const int dwell = waveptr->duration / waveptr->res;
5218  if (dwell % GRAD_UPDATE_TIME) {
5219  return KS_THROW("dwell time must be a multiple of the gradient update time");
5220  }
5221 
5222  /* Extra corner points with zero amplitude are marked with ptype G_CONSTANT */
5223  for (j = 0; j < waveptr->base.ninst; ++j) { /* each instance of the wave */
5224  KS_SEQLOC loc = waveptr->locs[j];
5225  const float rtscale = ks_rt_scale_log_get(&waveptr->rtscaling, j, ctrl->current_playout);
5226 
5227  t_list grad = {grads->max_time + loc.pos, 0.0, G_CONSTANT};
5228  s = _grad_list_append(grads, &grad, loc.board);
5229  KS_RAISE(s);
5230 
5231  grad.time = grads->max_time + loc.pos + dwell/2;
5232  grad.ptype = G_USER;
5233  for (k = 0; k < waveptr->res; ++k) {
5234  grad.ampl = waveptr->waveform[k] * loc.ampscale * rtscale;
5235  s = _grad_list_append(grads, &grad, loc.board);
5236  KS_RAISE(s);
5237  grad.time += dwell;
5238  }
5239 
5240  grad.time -= dwell/2;
5241  grad.ampl = 0.0;
5242  grad.ptype = G_CONSTANT;
5243  s = _grad_list_append(grads, &grad, loc.board);
5244  KS_RAISE(s);
5245  }
5246  }
5247 
5248  /* add a point for each board at the end of the module */
5249  t_list grad = {grads->max_time + ctrl->duration, 0.0, G_USER};
5250  for (i=0; i<3; ++i) {
5251  s = _grad_list_append(grads, &grad, i);
5252  KS_RAISE(s);
5253  }
5254 
5255  /* update the max_time */
5256  grads->max_time = grad.time;
5257 
5258  ctrl->current_playout++;
5259 
5260  return SUCCESS;
5261 }
int plateautime
Definition: KSFoundation.h:672
Core sequence object for making trapezoids on X,Y,Z, and OMEGA boards
Definition: KSFoundation.h:666
KS_BASE base
Definition: KSFoundation.h:667
KS_SEQLOC * locs
Definition: KSFoundation.h:752
int pos
Definition: KSFoundation.h:463
Core sequence object making arbitrary waveforms on any board (using float data format)
Definition: KSFoundation.h:743
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 ninst
Definition: KSFoundation.h:494
int current_playout
Definition: KSFoundation.h:1238
KS_SEQLOC * locs
Definition: KSFoundation.h:678
int board
Definition: KSFoundation.h:462
STATUS _grad_list_append(struct _grad_list *grads, t_list *grad, int board)
Definition: KSFoundation_host.c:5079
int duration
Definition: KSFoundation.h:1227
KS_RT_SCALE_LOG rtscaling
Definition: KSFoundation.h:681
float ampscale
Definition: KSFoundation.h:464
KS_GRADRFCTRL gradrf
Definition: KSFoundation.h:1236
KS_RT_SCALE_LOG rtscaling
Definition: KSFoundation.h:755
KS_WAVE * waveptr[KS_MAXUNIQUE_WAVE]
Definition: KSFoundation.h:1059
KS_BASE base
Definition: KSFoundation.h:744
int max_time
Definition: KSFoundation_host.c:5052
#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
KS_TRAP * trapptr[KS_MAXUNIQUE_TRAP]
Definition: KSFoundation.h:1057
int numwave
Definition: KSFoundation.h:1060
#define KS_THROW(format,...)
Definition: KSFoundation.h:181
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

◆ __wrap_getCornerPoints()

STATUS __wrap_getCornerPoints ( FLOAT **  p_time,
FLOAT *  ampl[3],
FLOAT *  pul_type[3],
INT *  num_totpoints,
const LOG_GRAD *  log_grad,
const INT  seq_entry_index,
const INT  samp_rate,
const INT  min_tr,
const FLOAT  dbdtinf,
const FLOAT  dbdtfactor,
const FLOAT  efflength,
const INT  max_encode_mode,
const dbLevel_t  debug 
)
5316 {
5317  STATUS s;
5318  int i;
5319 
5320  /*ks_dbg("getCornerPoints mode: %s", max_encode_mode == MAXIMUM_POWER ? "MAXIMUM" : "AVERAGE");*/
5321 
5322  /* validate global */
5324  return KS_THROW("the function ks_grad_heat_init must be called with valid inputs prior to running the gradient heating model");
5325  }
5326 
5327  /* using samp rate that is different from */
5328  if (samp_rate != GRAD_UPDATE_TIME){
5329  return KS_THROW("requested sample rate (%d) is different from the default (%d). not supported yet (or ever?)",
5330  samp_rate, GRAD_UPDATE_TIME);
5331  }
5332 
5333  /* collect the list of modules to model */
5336  KS_RAISE(s);
5337 
5338  /* reset current_playout field */
5339  for (i=0; i<grad_heat_handle.seqcollection_p->numseq; ++i) {
5340 
5342  return KS_THROW("NULL pointer to the %d-th sequence entry", i);
5343  }
5344 
5346  }
5347 
5348  struct _grad_list grads;
5349  s = _grad_list_init(&grads);
5350  if (s != SUCCESS) { goto fail; }
5351 
5352  /* iterate over the sequences of modules */
5353  for (i=0; i<grad_heat_handle.seqcollection_p->numplayouts; ++i) {
5354 
5356  s = KS_THROW("NULL pointer to the %d-th sequence entry in playout order", i);
5357  goto fail;
5358  }
5359 
5360  /* log the sequence of module */
5361  /*ks_dbg("playout %d: %s, duration %d", i,
5362  grad_heat_handle.seqcollection_p->seqplayouts[i]->description,
5363  grad_heat_handle.seqcollection_p->seqplayouts[i]->duration);*/
5364 
5366  if (s != SUCCESS) { goto fail; }
5367  }
5368 
5369  s = _grad_list_remove_duplicates(&grads);
5370  if (s != SUCCESS) { goto fail; }
5371 
5372  /*
5373  char boards[] = "XYZ";
5374  for (i=0; i<3; ++i) {
5375  ks_dbg("board %c has %d unique points", boards[i], grads.num_grads[i]);
5376  }
5377  ks_dbg("tot_time = %d", grads.max_time);*/
5378 
5379  /* allocate output arrays */
5380  {
5381  const int max_nout = grads.num_grads[X] + grads.num_grads[Y] + grads.num_grads[Z];
5382  FLOAT *p;
5383  FLOAT **ps[] = {p_time, ampl, ampl+1, ampl+2, pul_type, pul_type+1, pul_type+2};
5384  for (i=0; i<7; ++i) {
5385  /* free'ed by GE's code */
5386  p = (FLOAT*)malloc(max_nout * sizeof(FLOAT));
5387  if (!p) {
5388  s = KS_THROW("Allocation failed");
5389  goto fail;
5390  }
5391  *ps[i] = p;
5392  }
5393  }
5394 
5395 #if EPIC_RELEASE > 26
5396  *num_iters = 1; /* TODO: we could use also numplayouts from seq collection */
5397 #endif
5398 
5399  /* join by interpolation the corner points */
5400  s = _fillcp(grads.data, grads.num_grads, *p_time, ampl, pul_type, num_totpoints);
5401  if (s != SUCCESS) {
5402  KS_THROW("fillcp failed");
5403  goto fail;
5404  }
5405 
5406  /* common outro */
5407  fail:
5408  for (i=0; i<3; ++i) {
5409  free(grads.data[i]);
5410  }
5411  KS_RAISE(s);
5412 
5413  return SUCCESS;
5414 }
Definition: KSFoundation_host.c:5048
int current_playout
Definition: KSFoundation.h:1238
KS_SEQ_CONTROL * seqplayouts[KS_MAX_SEQUENCE_PLAYOUTS]
Definition: KSFoundation.h:1271
STATUS(* play)(const INT max_encode_mode, int nargs, void **args)
Definition: KSFoundation_host.c:5017
KS_SEQ_CONTROL * seqctrlptr[KS_MAXUNIQUE_SEQUENCES]
Definition: KSFoundation.h:1268
KS_SEQ_COLLECTION * seqcollection_p
Definition: KSFoundation_host.c:5016
int nargs
Definition: KSFoundation_host.c:5018
void ** args
Definition: KSFoundation_host.c:5019
#define KS_RAISE(status)
Definition: KSFoundation.h:190
STATUS _grad_list_init(struct _grad_list *grads)
Definition: KSFoundation_host.c:5055
int numplayouts
Definition: KSFoundation.h:1270
STATUS _grad_list_add_sequence(struct _grad_list *grads, KS_SEQ_CONTROL *ctrl)
Definition: KSFoundation_host.c:5173
#define KS_THROW(format,...)
Definition: KSFoundation.h:181
STATUS _grad_list_remove_duplicates(struct _grad_list *grads)
Definition: KSFoundation_host.c:5116
struct _grad_heat_handle grad_heat_handle
Definition: KSFoundation_host.c:5023
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
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_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_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_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_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_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_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 
)

Extract a specified amount of bits from the input with an offset

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

◆ opuser_from_number()

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

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

STATUS 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_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_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_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_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_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_calc_slice_acquisition_order_smssingleacq_impl()

int ks_calc_slice_acquisition_order_smssingleacq_impl ( DATA_ACQ_ORDER *  dacq,
int  nslices,
int  interleave 
)
6493  {
6494 
6495  const int ngroups = (nslices + interleave -1)/interleave;
6496  const int ngroups_full = nslices%ngroups ? nslices%ngroups : ngroups;
6497 
6498  int g, s, t=0;
6499  for (s=0; s<interleave; ++s) {
6500  for (g=0; g<ngroups_full; ++g) {
6501  int i = g*interleave + s;
6502  dacq[i].slloc = i;
6503  dacq[i].slpass = 0;
6504  dacq[i].sltime = t++;
6505  }
6506 
6507  if (s+1 == interleave) { continue; }
6508 
6509  for (g=ngroups_full; g<ngroups; ++g) {
6510  int i = ngroups_full + g*(interleave-1) + s;
6511  dacq[i].slloc = i;
6512  dacq[i].slpass = 0;
6513  dacq[i].sltime = t++;
6514  }
6515  }
6516 
6517  const int sl_in_last_group = nslices%interleave ? interleave-1 : interleave;
6518  const int sl_pairs_to_switch = (sl_in_last_group-1)/2;
6519  int i;
6520  for(i=1; i<=sl_pairs_to_switch; ++i) {
6521  int tmp;
6522  tmp = dacq[nslices-i].sltime;
6523  dacq[nslices-i].sltime = dacq[nslices-sl_in_last_group+i].sltime;
6524  dacq[nslices-sl_in_last_group+i].sltime = tmp;
6525  }
6526 
6527  return 1;
6528 }

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

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

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

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 }
float rbw
Definition: KSFoundation.h:842
KS_DESCRIPTION description
Definition: KSFoundation.h:840
int duration
Definition: KSFoundation.h:841
FILTER_INFO filt
Definition: KSFoundation.h:843

◆ 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 }
int plateautime
Definition: KSFoundation.h:672
float area
Definition: KSFoundation.h:670
float amp
Definition: KSFoundation.h:669
KS_DESCRIPTION description
Definition: KSFoundation.h:668
int duration
Definition: KSFoundation.h:673
int ramptime
Definition: KSFoundation.h:671

◆ 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
float fov
Definition: KSFoundation.h:1551
KS_WAVE omega
Definition: KSFoundation.h:1562
KS_TRAP grad
Definition: KSFoundation.h:1561
int res
Definition: KSFoundation.h:1552
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_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
int time2center
Definition: KSFoundation.h:1560
float area2center
Definition: KSFoundation.h:1559
int res
Definition: KSFoundation.h:746
int acqdelay
Definition: KSFoundation.h:1555
KS_WAVEFORM waveform
Definition: KSFoundation.h:748
int duration
Definition: KSFoundation.h:747

◆ 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 }
int R
Definition: KSFoundation.h:1723
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
int res
Definition: KSFoundation.h:1721
KS_TRAP grad
Definition: KSFoundation.h:1719
int nacslines
Definition: KSFoundation.h:1724
int numlinestoacq
Definition: KSFoundation.h:1727
float areaoffset
Definition: KSFoundation.h:1726
int nover
Definition: KSFoundation.h:1722
KS_DESCRIPTION description
Definition: KSFoundation.h:668
int duration
Definition: KSFoundation.h:673
float fov
Definition: KSFoundation.h:1720

◆ 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_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 */
KS_TRAP blip
Definition: KSFoundation.h:1935
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_PHASER blipphaser
Definition: KSFoundation.h:1937
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
KS_DESCRIPTION description
Definition: KSFoundation.h:668
int time2center
Definition: KSFoundation.h:1944
KS_TRAP readphaser
Definition: KSFoundation.h:1934
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
int read_spacing
Definition: KSFoundation.h:1942

◆ 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_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 */
float cf_offset
Definition: KSFoundation.h:1030
int start2iso
Definition: KSFoundation.h:1032
KS_DESCRIPTION designinfo
Definition: KSFoundation.h:1035
int role
Definition: KSFoundation.h:1027
RF_PULSE rfpulse
Definition: KSFoundation.h:1036
Definition: KSFoundation.h:2340
Definition: KSFoundation.h:2340
Definition: KSFoundation.h:2340
float flip
Definition: KSFoundation.h:1028
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
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
int res
Definition: KSFoundation.h:746
Definition: KSFoundation.h:2340
int duration
Definition: KSFoundation.h:747
float bw
Definition: KSFoundation.h:1029

◆ 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 */
KS_TRAP grad
Definition: KSFoundation.h:1463
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
float pregrad_area_offset
Definition: KSFoundation.h:1459
int role
Definition: KSFoundation.h:1027
KS_WAVE gradwave
Definition: KSFoundation.h:1465
float crusher_dephasing
Definition: KSFoundation.h:1457
RF_PULSE rfpulse
Definition: KSFoundation.h:1036
Definition: KSFoundation.h:2340
int bridge_crushers
Definition: KSFoundation.h:1458
KS_TRAP pregrad
Definition: KSFoundation.h:1462
float postgrad_area_offset
Definition: KSFoundation.h:1460
int grad2rf_start
Definition: KSFoundation.h:1466
KS_RF rf
Definition: KSFoundation.h:1454
KS_DESCRIPTION description
Definition: KSFoundation.h:745
KS_WAVE rfwave
Definition: KSFoundation.h:1037
KS_TRAP postgrad
Definition: KSFoundation.h:1464
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
int rf2grad_end
Definition: KSFoundation.h:1467
int res
Definition: KSFoundation.h:746
int duration
Definition: KSFoundation.h:747
float slthick
Definition: KSFoundation.h:1456

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

◆ get_t1()

float get_t1 ( float  A,
float  s,
float  c 
)
7131  {
7132  /*# ____________
7133  # ╱ 2
7134  # 2⋅╲╱ 2⋅A⋅s - c
7135  # ─────────────────
7136  # s */
7137  return (2 * sqrtf(2 * A * s - c*c)) / s;
7138 }

◆ get_T()

float get_T ( float  A,
float  s,
float  c,
float  t1 
)
7143  {
7144 /*# ________________________
7145  # ╱ 2 2 2
7146  # 2⋅c + s⋅t₁ + ╲╱ -8⋅A⋅s + 4⋅c + s ⋅t₁
7147  # ────────────────────────────────────────
7148  # 2⋅s */
7149  return (2 * c + s * t1 + sqrtf(-8.0f * A * s + 4 * c * c + s * s * t1 * t1)) / (2.0f * s);
7150 }

◆ get_b()

float get_b ( float  A,
float  c,
float  T,
float  t1 
)
7155  {
7156 /*# 2⋅A - c⋅t2
7157  # ───────────
7158  # T */
7159  float t2 = T - t1;
7160  return (2 * A - c * t2) / T;
7161 }

◆ get_ramp()

void get_ramp ( ks_rampparams_s out,
float  A,
float  s,
float  c 
)
7174  {
7175  float t1rnd = KS_RUP_GRD_FLOAT(get_t1(A, s, c));
7176  float Trnd = KS_RUP_GRD_FLOAT(get_T(A, s, c, t1rnd));
7177  float b = -1.0f;
7178  while (b < 0.0f) {
7179  b = get_b(A, c, Trnd, t1rnd);
7180  /* risk of violating maximum slew */
7181  Trnd -= GRAD_UPDATE_TIME;
7182  }
7183  out->t1 = t1rnd;
7184  out->slew1 = b / out->t1;
7185  out->t2 = Trnd - out->t1;
7186  out->slew2 = (c - b) / out->t2;
7187 
7188 }
#define KS_RUP_GRD_FLOAT(X)
Definition: KSFoundation.h:170
float slew2
Definition: KSFoundation_host.c:7168
float get_b(float A, float c, float T, float t1)
Definition: KSFoundation_host.c:7155
float get_t1(float A, float s, float c)
Definition: KSFoundation_host.c:7131
float get_T(float A, float s, float c, float t1)
Definition: KSFoundation_host.c:7143
float t2
Definition: KSFoundation_host.c:7170
float slew1
Definition: KSFoundation_host.c:7167
float t1
Definition: KSFoundation_host.c:7169

◆ ks_area_to_amp()

STATUS ks_area_to_amp ( KS_WAVE out,
float  area,
float  max_slew,
float  amp 
)
7193  {
7195 
7196  float minA = amp*amp / (2.0f*max_slew);
7197  if (area < minA) {
7198  return ks_error("%s: Minimum area is: %.2f", __FUNCTION__, minA);
7199  }
7200 
7201  /* gradient raster rounding can result in slew violations */
7202  float slew = max_slew;
7203  do {
7204  get_ramp(&params, area, slew, amp);
7205  /* Derate the max_slew and try again. */
7206  slew *= 0.999;
7207  } while(params.slew2 > max_slew);
7208  /* Create the wave object */
7209  {
7210  int t1grt = (int)(params.t1 / GRAD_UPDATE_TIME);
7211  int t2grt = (int)(params.t2 / GRAD_UPDATE_TIME);
7212  float s1grt = params.slew1 * GRAD_UPDATE_TIME;
7213  float s2grt = params.slew2 * GRAD_UPDATE_TIME;
7214 
7215  int i = 0;
7216  for(; i < t1grt; i++) out->waveform[i] = i * s1grt;
7217  for(; i < t2grt; i++) out->waveform[i] = i * s2grt;
7218  out->res = t1grt + t2grt;
7219  out->duration = out->res * GRAD_UPDATE_TIME;
7220  }
7221 
7222  return SUCCESS;
7223 
7224 }
Definition: KSFoundation_host.c:7166
STATUS ks_error(const char *format,...) __attribute__((format(printf
Common error message function for HOST and TGT
float slew2
Definition: KSFoundation_host.c:7168
void get_ramp(ks_rampparams_s *out, float A, float s, float c)
Definition: KSFoundation_host.c:7174
#define KS_INIT_RAMPPARAMS
Definition: KSFoundation_host.c:7172
float t2
Definition: KSFoundation_host.c:7170
float slew1
Definition: KSFoundation_host.c:7167
int res
Definition: KSFoundation.h:746
KS_WAVEFORM waveform
Definition: KSFoundation.h:748
int duration
Definition: KSFoundation.h:747
float t1
Definition: KSFoundation_host.c:7169

◆ ks_eval_append_blip()

int ks_eval_append_blip ( KS_WAVE wave,
const float  max_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_file_exist()

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

◆ ks_plot_host_slicetime_path()

void ks_plot_host_slicetime_path ( char *  path)
7288  {
7289 #ifdef PSD_HW /* on MR-scanner */
7290  sprintf(path, "/usr/g/mrraw/plot/%s/slicetime/", ks_psdname);
7291 #else /* in Simulation */
7292  sprintf(path, "./plot/slicetime/");
7293 #endif
7294 }
char ks_psdname[256]
Definition: GERequired.e:245

◆ ks_plot_host_slicetime_fullfile()

void ks_plot_host_slicetime_fullfile ( char *  fullfile)
7299  {
7300  char path[250];
7302  sprintf(fullfile, "%s%s_slicetime.json", path, ks_psdname);
7303 }
void ks_plot_host_slicetime_path(char *path)
Definition: KSFoundation_host.c:7288
char ks_psdname[256]
Definition: GERequired.e:245

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

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

void ks_plot_host_rtscales_amps ( const KS_RT_SCALE_LOG rtscalelog,
FILE *  fp,
const int  instance 
)
7603  {
7604  int shot;
7605  fputs("[", fp);
7606  if (rtscalelog->ampscales && (rtscalelog->num_completed_playouts * rtscalelog->num_instances) > 0) {
7607  int shots = rtscalelog->num_completed_playouts;
7608  for (shot = 0; shot < shots; shot++) {
7609  fprintf(fp, "%0.6f", ks_rt_scale_log_get(rtscalelog, instance, shot) );
7610  if (shot < (shots - 1)) {
7611  fputs(", ", fp);
7612  } else {
7613  fputs("],\n", fp);
7614  }
7615  }
7616  } else {
7617  fputs("1.0],\n", fp);
7618  }
7619 }
int num_completed_playouts
Definition: KSFoundation.h:477
float ks_rt_scale_log_get(const KS_RT_SCALE_LOG *log, int instance_idx, int playout_idx)
Definition: KSFoundation_common.c:4981
int num_instances
Definition: KSFoundation.h:476
float * ampscales
Definition: KSFoundation.h:472

◆ ks_plot_host_rtscales_states()

void ks_plot_host_rtscales_states ( const KS_RT_SCALE_LOG rtscalelog,
FILE *  fp,
const int  instance 
)
7624  {
7625  int shot;
7626  fputs("[", fp);
7627  if (rtscalelog->states && (rtscalelog->num_completed_playouts * rtscalelog->num_instances) > 0) {
7628  int shots = rtscalelog->num_completed_playouts;
7629  for (shot = 0; shot < shots; shot++) {
7630  fprintf(fp, "%d", ks_rt_scale_log_get_state(rtscalelog, instance, shot) );
7631  if (shot < (shots - 1)) {
7632  fputs(", ", fp);
7633  } else {
7634  fputs("],\n", fp);
7635  }
7636  }
7637  } else {
7638  fputs("0],\n", fp);
7639  }
7640 }
int num_completed_playouts
Definition: KSFoundation.h:477
int num_instances
Definition: KSFoundation.h:476
int * states
Definition: KSFoundation.h:473
int ks_rt_scale_log_get_state(const KS_RT_SCALE_LOG *log, int instance_idx, int playout_idx)
Definition: KSFoundation_common.c:5013

◆ ks_plot_host_wavestates()

void ks_plot_host_wavestates ( const KS_WAVE waveptr,
FILE *  fp 
)
7645  {
7646  if (waveptr->base.nstates) {
7647  int state = 0;
7648  int w;
7649  fprintf(fp, "\t\t\"states\": [\n");
7650  for (; state < waveptr->base.nstates; state++) {
7651  fprintf(fp, "\t\t\t [0.0,");
7652  for (w = 0; w < waveptr->res; w++) {
7653  fprintf(fp, "%0.6f,", waveptr->p_waveformstates[state][w]);
7654  }
7655 
7656  if (state < (waveptr->base.nstates - 1)) {
7657  fprintf(fp, "0.0],\n");
7658  } else {
7659  fprintf(fp, "0.0]\n");
7660  }
7661  }
7662  fprintf(fp, "\t\t],\n");
7663  }
7664 }
KS_BASE base
Definition: KSFoundation.h:744
int nstates
Definition: KSFoundation.h:495
float * p_waveformstates[KS_WAVE_MAXNSTATES]
Definition: KSFoundation.h:749
int res
Definition: KSFoundation.h:746

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

int ks_comp_kcoord_dist_z_y ( const void *  a,
const void *  b 
)
8236  {
8237  KS_KCOORD* k_a = (KS_KCOORD*)a;
8238  KS_KCOORD* k_b = (KS_KCOORD*)b;
8239 
8240  const float z_distance = fabs(k_a->z + 0.5) - fabs(k_b->z + 0.5);
8241  if (!areWithinTol(z_distance, 0.0, 0.1)) {
8242  return (z_distance > 0.0 ? 1 : 0);
8243  }
8244 
8245  const float y_distance = fabs(k_a->y + 0.5) - fabs(k_b->y + 0.5);
8246  if (!areWithinTol(y_distance, 0.0, 0.1)) {
8247  return (y_distance > 0.0 ? 1 : 0);
8248  }
8249 
8250  return 0;
8251 }
int z
Definition: KSFoundation.h:2202
#define areWithinTol(a, b, tol)
Definition: KSFoundation.h:151
ADDTITLEHERE
Definition: KSFoundation.h:2200
int y
Definition: KSFoundation.h:2201

◆ ks_comp_kview_dist_z_y()

int ks_comp_kview_dist_z_y ( const void *  a,
const void *  b 
)
8256  {
8257  KS_VIEW* view_a =(KS_VIEW*)a;
8258  KS_VIEW* view_b =(KS_VIEW*)b;
8259  return ks_comp_kcoord_dist_z_y(view_a->coord, view_b->coord);
8260 }
int ks_comp_kcoord_dist_z_y(const void *a, const void *b)
Definition: KSFoundation_host.c:8236
KS_KCOORD * coord
Definition: KSFoundation.h:2215
ADDTITLEHERE
Definition: KSFoundation.h:2214

◆ ks_comp_kcoord_dist_y_z()

int ks_comp_kcoord_dist_y_z ( const void *  a,
const void *  b 
)
8265  {
8266  KS_KCOORD* k_a = (KS_KCOORD*)a;
8267  KS_KCOORD* k_b = (KS_KCOORD*)b;
8268  const float y_distance = fabs(k_a->y + 0.5) - fabs(k_b->y + 0.5);
8269  if (!areWithinTol(y_distance, 0.0, 0.1)) {
8270  return (y_distance > 0.0 ? 1 : 0);
8271  }
8272 
8273  const float z_distance = fabs(k_a->z + 0.5) - fabs(k_b->z + 0.5);
8274  if (!areWithinTol(z_distance, 0.0, 0.1)) {
8275  return (z_distance > 0.0 ? 1 : 0);
8276  }
8277 
8278  return 0;
8279 }
int z
Definition: KSFoundation.h:2202
#define areWithinTol(a, b, tol)
Definition: KSFoundation.h:151
ADDTITLEHERE
Definition: KSFoundation.h:2200
int y
Definition: KSFoundation.h:2201

◆ ks_comp_kview_dist_y_z()

int ks_comp_kview_dist_y_z ( const void *  a,
const void *  b 
)
8284  {
8285  KS_VIEW* view_a =(KS_VIEW*)a;
8286  KS_VIEW* view_b =(KS_VIEW*)b;
8287  return ks_comp_kcoord_dist_y_z(view_a->coord, view_b->coord);
8288 }
KS_KCOORD * coord
Definition: KSFoundation.h:2215
ADDTITLEHERE
Definition: KSFoundation.h:2214
int ks_comp_kcoord_dist_y_z(const void *a, const void *b)
Definition: KSFoundation_host.c:8265

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

int ks_comp_kview_t_r ( const void *  a,
const void *  b 
)
8356  {
8357  KS_VIEW* k_a =(KS_VIEW*)a;
8358  KS_VIEW* k_b =(KS_VIEW*)b;
8359 
8360  if (k_a->coord->t > k_b->coord->t) {
8361  return 1;
8362  } else if (k_a->coord->t < k_b->coord->t) {
8363  return -1;
8364  }
8365 
8366  if (k_a->coord->r > k_b->coord->r) {
8367  return 1;
8368  } else if (k_a->coord->r < k_b->coord->r) {
8369  return -1;
8370  }
8371 
8372  return 0;
8373 }
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_z_y()

int ks_comp_kview_z_y ( const void *  a,
const void *  b 
)
8398  {
8399  KS_VIEW* k_a =(KS_VIEW*)a;
8400  KS_VIEW* k_b =(KS_VIEW*)b;
8401  if (k_a->coord->z > k_b->coord->z) {
8402  return 1;
8403  } else if (k_a->coord->z < k_b->coord->z) {
8404  return -1;
8405  }
8406 
8407  if (k_a->coord->y > k_b->coord->y) {
8408  return 1;
8409  } else if (k_a->coord->y < k_b->coord->y) {
8410  return -1;
8411  }
8412  return 0;
8413 }
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_y_r()

int ks_comp_kview_y_r ( const void *  a,
const void *  b 
)
8418  {
8419  KS_VIEW* k_a =(KS_VIEW*)a;
8420  KS_VIEW* k_b =(KS_VIEW*)b;
8421  if (k_a->coord->y > k_b->coord->y) {
8422  return 1;
8423  } else if (k_a->coord->y < k_b->coord->y) {
8424  return -1;
8425  }
8426 
8427  if (k_a->coord->r > k_b->coord->r) {
8428  return 1;
8429  } else if (k_a->coord->r < k_b->coord->r) {
8430  return -1;
8431  }
8432  return 0;
8433 }
KS_KCOORD * coord
Definition: KSFoundation.h:2215
float r
Definition: KSFoundation.h:2203
ADDTITLEHERE
Definition: KSFoundation.h:2214
int y
Definition: KSFoundation.h:2201

◆ ks_comp_kview_z_r()

int ks_comp_kview_z_r ( const void *  a,
const void *  b 
)
8438  {
8439  KS_VIEW* k_a =(KS_VIEW*)a;
8440  KS_VIEW* k_b =(KS_VIEW*)b;
8441  if (k_a->coord->z > k_b->coord->z) {
8442  return 1;
8443  } else if (k_a->coord->z < k_b->coord->z) {
8444  return -1;
8445  }
8446 
8447  if (k_a->coord->r > k_b->coord->r) {
8448  return 1;
8449  } else if (k_a->coord->r < k_b->coord->r) {
8450  return -1;
8451  }
8452  return 0;
8453 }
KS_KCOORD * coord
Definition: KSFoundation.h:2215
float r
Definition: KSFoundation.h:2203
int z
Definition: KSFoundation.h:2202
ADDTITLEHERE
Definition: KSFoundation.h:2214

◆ ks_comp_kview_e_tr()

int ks_comp_kview_e_tr ( const void *  a,
const void *  b 
)
8458  {
8459  KS_VIEW* k_a =(KS_VIEW*)a;
8460  KS_VIEW* k_b =(KS_VIEW*)b;
8461 
8462  if (k_a->encode > k_b->encode) {
8463  return 1;
8464  } else if (k_a->encode < k_b->encode) {
8465  return -1;
8466  }
8467 
8468  return ks_comp_kview_t_r(a, b);
8469 
8470 }
int ks_comp_kview_t_r(const void *a, const void *b)
Definition: KSFoundation_host.c:8356
ADDTITLEHERE
Definition: KSFoundation.h:2214
int encode
Definition: KSFoundation.h:2216

◆ ks_comp_kview_e_rt()

int ks_comp_kview_e_rt ( const void *  a,
const void *  b 
)
8475  {
8476  KS_VIEW* k_a =(KS_VIEW*)a;
8477  KS_VIEW* k_b =(KS_VIEW*)b;
8478 
8479  if (k_a->encode > k_b->encode) {
8480  return 1;
8481  } else if (k_a->encode < k_b->encode) {
8482  return -1;
8483  }
8484 
8485  return ks_comp_kview_r_t(a, b);
8486 
8487 }
int ks_comp_kview_r_t(const void *a, const void *b)
ADDTITLEHERE
Definition: KSFoundation_host.c:8335
ADDTITLEHERE
Definition: KSFoundation.h:2214
int encode
Definition: KSFoundation.h:2216

◆ ks_comp_kview_e_zy()

int ks_comp_kview_e_zy ( const void *  a,
const void *  b 
)
8492  {
8493  KS_VIEW* k_a =(KS_VIEW*)a;
8494  KS_VIEW* k_b =(KS_VIEW*)b;
8495 
8496  if (k_a->encode > k_b->encode) {
8497  return 1;
8498  } else if (k_a->encode < k_b->encode) {
8499  return -1;
8500  }
8501 
8502  return ks_comp_kview_z_y(a, b);
8503 
8504 }
int ks_comp_kview_z_y(const void *a, const void *b)
Definition: KSFoundation_host.c:8398
ADDTITLEHERE
Definition: KSFoundation.h:2214
int encode
Definition: KSFoundation.h:2216

◆ ks_comp_kview_e_yz()

int ks_comp_kview_e_yz ( const void *  a,
const void *  b 
)
8509  {
8510  KS_VIEW* k_a =(KS_VIEW*)a;
8511  KS_VIEW* k_b =(KS_VIEW*)b;
8512 
8513  if (k_a->encode > k_b->encode) {
8514  return 1;
8515  } else if (k_a->encode < k_b->encode) {
8516  return -1;
8517  }
8518 
8519  return ks_comp_kview_y_z(a, b);
8520 
8521 }
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

◆ ks_comp_kview_e_yr()

int ks_comp_kview_e_yr ( const void *  a,
const void *  b 
)
8526  {
8527  KS_VIEW* k_a =(KS_VIEW*)a;
8528  KS_VIEW* k_b =(KS_VIEW*)b;
8529 
8530  if (k_a->encode > k_b->encode) {
8531  return 1;
8532  } else if (k_a->encode < k_b->encode) {
8533  return -1;
8534  }
8535 
8536  return ks_comp_kview_y_r(a, b);
8537 
8538 }
int ks_comp_kview_y_r(const void *a, const void *b)
Definition: KSFoundation_host.c:8418
ADDTITLEHERE
Definition: KSFoundation.h:2214
int encode
Definition: KSFoundation.h:2216

◆ ks_comp_kview_e_zr()

int ks_comp_kview_e_zr ( const void *  a,
const void *  b 
)
8543  {
8544  KS_VIEW* k_a =(KS_VIEW*)a;
8545  KS_VIEW* k_b =(KS_VIEW*)b;
8546 
8547  if (k_a->encode > k_b->encode) {
8548  return 1;
8549  } else if (k_a->encode < k_b->encode) {
8550  return -1;
8551  }
8552 
8553  return ks_comp_kview_z_r(a, b);
8554 
8555 }
int ks_comp_kview_z_r(const void *a, const void *b)
Definition: KSFoundation_host.c:8438
ADDTITLEHERE
Definition: KSFoundation.h:2214
int encode
Definition: KSFoundation.h:2216

◆ ks_comp_kview_e_sec_t()

int ks_comp_kview_e_sec_t ( const void *  a,
const void *  b 
)
8560  {
8561  KS_VIEW* k_a =(KS_VIEW*)a;
8562  KS_VIEW* k_b =(KS_VIEW*)b;
8563 
8564  if (k_a->encode > k_b->encode) {
8565  return 1;
8566  } else if (k_a->encode < k_b->encode) {
8567  return -1;
8568  }
8569 
8570  const int N = 4;
8571  int segments = 128;
8572  float ta = fmod(N * (k_a->coord->t + PI), 2*PI) / (2 * PI);
8573  float tb = fmod(N * (k_b->coord->t + PI), 2*PI) / (2 * PI);
8574  int ca = (int)(ta * segments) - segments / 2;
8575  int cb = (int)(tb * segments) - segments / 2;
8576 
8577  if (ca < 0) {
8578  ca = -2 * ca - 1;
8579  } else {
8580  ca *= 2;
8581  }
8582 
8583  if (cb < 0) {
8584  cb = -2 * cb - 1;
8585  } else {
8586  cb *= 2;
8587  }
8588 
8589  if (ca > cb) {
8590  return 1;
8591  } else if (ca < cb) {
8592  return -1;
8593  }
8594 
8595  if (k_a->coord->t > k_b->coord->t) {
8596  return 1;
8597  } else if (k_a->coord->t < k_b->coord->t) {
8598  return -1;
8599  }
8600 
8601  return 0;
8602 
8603 }
float t
Definition: KSFoundation.h:2204
KS_KCOORD * coord
Definition: KSFoundation.h:2215
ADDTITLEHERE
Definition: KSFoundation.h:2214
int encode
Definition: KSFoundation.h:2216

◆ ks_comp_kview_y_z_descend()

int ks_comp_kview_y_z_descend ( const void *  a,
const void *  b 
)
8608  {
8609  return -ks_comp_kview_y_z(a, b);
8610 }
int ks_comp_kview_y_z(const void *a, const void *b)
ADDTITLEHERE
Definition: KSFoundation_host.c:8378

◆ ks_comp_kview_z_y_descend()

int ks_comp_kview_z_y_descend ( const void *  a,
const void *  b 
)
8615  {
8616  return -ks_comp_kview_z_y(a, b);
8617 }
int ks_comp_kview_z_y(const void *a, const void *b)
Definition: KSFoundation_host.c:8398

◆ ks_get_comp_kview_y_z()

ks_comparator ks_get_comp_kview_y_z ( int  ascend)
8622  {
8623  return (ascend == 1) ? ks_comp_kview_y_z : ks_comp_kview_y_z_descend;
8624 }
int ks_comp_kview_y_z_descend(const void *a, const void *b)
Definition: KSFoundation_host.c:8608
int ks_comp_kview_y_z(const void *a, const void *b)
ADDTITLEHERE
Definition: KSFoundation_host.c:8378

◆ ks_get_comp_kview_z_y()

ks_comparator ks_get_comp_kview_z_y ( int  ascend)
8629  {
8630  return (ascend == 1) ? ks_comp_kview_z_y : ks_comp_kview_z_y_descend;
8631 }
int ks_comp_kview_z_y_descend(const void *a, const void *b)
Definition: KSFoundation_host.c:8615
int ks_comp_kview_z_y(const void *a, const void *b)
Definition: KSFoundation_host.c:8398

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

int ks_readout_start_index ( int  center,
int  etl,
int  num_encodes_per_train 
)
8945  {
8946  int readout_start = center - num_encodes_per_train/2;
8947  readout_start = IMax(2, readout_start, 0);
8948  readout_start = IMin(2, readout_start, etl - num_encodes_per_train);
8949  return readout_start;
8950 }

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

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

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

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 
)
9048  {
9049  const int readout_end = shot_dist->readout_start + shot_dist->encodes_per_shot;
9050  int readout = shot_dist->readout_start;
9051  int assigned = 0;
9052  for (; readout < readout_end; readout++) {
9053  int shot = first_shot_number;
9054  for (; shot < (shot_dist->shots + first_shot_number); shot++) {
9055  int block = (shot - first_shot_number) < shot_dist->first_blocks ? 0 : 1;
9056  if (block == 1 && readout == (readout_end -1)) {
9057  /* Second block - skip one encode */
9058  continue;
9059  } else {
9060  ks_phaseencoding_set(peplan,
9061  views[assigned].encode,
9062  shot,
9063  views[assigned].coord->y + y_offset,
9064  is3D ? views[assigned].coord->z + z_offset : KS_NOTSET);
9065  assigned++;
9066  }
9067  }
9068  }
9069  if (assigned != num_views) {
9070  return KS_THROW("Couldn't assign all requested encodes. There are %d encodes in shot_dist but %d were requested", assigned, num_views);
9071  }
9072  return SUCCESS;
9073 }
int shots
Definition: KSFoundation.h:2166
#define KS_NOTSET
Definition: KSFoundation.h:115
int readout_start
Definition: KSFoundation.h:2169
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:2168
int first_blocks
Definition: KSFoundation.h:2170
#define KS_THROW(format,...)
Definition: KSFoundation.h:181

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

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 
)
9231  {
9232  return ks_generate_peplan_from_kcoords_american(kacq, peplan, mtf_direction, etl, center);
9233 }
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
KS_KSPACE_ACQ kacq
Definition: ksepi_implementation.e:355

◆ ks_assign_encodes()

STATUS ks_assign_encodes ( KS_VIEW *const  views,
const int  num_views,
const int  start_encode,
const int  num_encodes,
const int  sign 
)
9242  {
9243  int idx;
9244  if (num_views < 0) {
9245  /*ks_dbg("Not assigning %d encodes at start %d", num_encodes, start_encode);*/
9246  return SUCCESS;
9247  }
9248  if (num_encodes < 1) {
9249  return KS_THROW("num_encodes (%d) must be > 0 ", num_encodes);
9250  }
9251  const int num_shots = CEIL_DIV(num_views, num_encodes);
9252  /*ks_dbg("Assigning %d views with %d encodes into %d shots at start %d with sign %d", num_views, num_encodes, num_shots, start_encode, sign);*/
9253  for (idx = 0; idx < num_views; idx++) {
9254  if (views[idx].encode != KS_NOTSET) {
9255  /* KS_THROW("view has an encode already (%d, wanted %d) [%d, %d]", views[idx].encode, start_encode + sign * idx / num_shots, views[idx].coord->y, views[idx].coord->z); */
9256  }
9257  views[idx].encode = start_encode + sign * idx / num_shots;
9258  }
9259 
9260  return SUCCESS;
9261 }
#define KS_NOTSET
Definition: KSFoundation.h:115
int encode
Definition: KSFoundation.h:2216
#define KS_THROW(format,...)
Definition: KSFoundation.h:181

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

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 
)
9317  {
9318  KS_PEPLAN_SHOT_DISTRIBUTION shot_dist = ks_peplan_distribute_shots(etl, num_coords, center);
9319  if (center > shot_dist.encodes_per_shot/2) {
9320  ks_lcpo_assign_encodes(views, num_coords, mtf_direction, split_shots, etl, etl - 1 - center);
9321  int idx = 0;
9322  for (; idx < num_coords; idx++) {
9323  views[idx].encode = etl - 1 - views[idx].encode;
9324  }
9325  return SUCCESS;
9326  }
9327  int num_inner_coords;
9328  int num_inner_encodes;
9329  /* Find the linear size that gets the desired center */
9330  /* Sort by distance from center along mtf_direction to ensure we get the central coordinates */
9331  if (mtf_direction == MTF_Y) {
9332  qsort(views, num_coords, sizeof(KS_VIEW), &ks_comp_kview_dist_y_z);
9333  } else {
9334  qsort(views, num_coords, sizeof(KS_VIEW), &ks_comp_kview_dist_z_y);
9335  }
9336  int start_from = 0;
9337 
9338  /* Figure out sweep order (lth or htl) */
9339  num_inner_coords = IMin(2, shot_dist.encodes_per_shot * shot_dist.shots, num_coords);
9340  const int center_lth_linsweep = ks_peplan_find_center_from_linear_sweep(NULL, views, num_inner_coords, mtf_direction, +1 /* lth */, shot_dist.shots, start_from);
9341  const int center_htl_linsweep = ks_peplan_find_center_from_linear_sweep(NULL, views, num_inner_coords, mtf_direction, -1 /* htl */, shot_dist.shots, start_from);
9342  int sweep_sign = 0;
9343  /* ks_dbg("lth/htl %d/%d", center_lth_linsweep, center_htl_linsweep); */
9344  if (center <= center_lth_linsweep && center <= center_htl_linsweep) {
9345  /* both possible */
9346  sweep_sign = center_lth_linsweep < center_htl_linsweep ? 1 : -1; /* Prefer starting from short side */
9347  /* ks_dbg("Two solutions - prefer %d", sweep_sign); */
9348  } else if (center <= center_lth_linsweep) {
9349  /* ks_dbg("Must do low to high"); */
9350  sweep_sign = 1; /* low to high */
9351  } else if (center <= center_htl_linsweep) {
9352  /* ks_dbg("Must do high to low"); */
9353  sweep_sign = -1; /* high to low */
9354  } else {
9355  /* ks_dbg("lth / htl = %d / %d", center_lth_linsweep, center_htl_linsweep); */
9356  if ((center > 0) && (etl % 2 == 0)) { /* Even ETL is a bit tricky with the center definition */
9357  /* is htl == lth a better condition? */
9358  KS_THROW("Center encode %d cannot be achieved - trying %d", center, center-1);
9359  return ks_lcpo_assign_encodes(views, num_coords, mtf_direction, split_shots, etl, center -1);
9360  } else {
9361  return KS_THROW("Center encode %d cannot be achieved", center);
9362  }
9363 
9364  }
9365 
9366  for (num_inner_encodes = shot_dist.encodes_per_shot; num_inner_encodes > 0; num_inner_encodes--) {
9367  num_inner_coords = IMin(2, num_inner_encodes * shot_dist.shots, num_coords);
9368  const int current_center_encode = ks_peplan_find_center_from_linear_sweep(NULL, views, num_inner_coords, mtf_direction, sweep_sign, shot_dist.shots, start_from);
9369 
9370  /* ks_dbg("Start from %d, num_inner_encodes = %d, num_inner_coords = %d, etl = %d, Current center encode = %d, center = %d", start_from, num_inner_encodes, num_inner_coords, shot_dist.encodes_per_shot, current_center_encode, center); */
9371 
9372  if (current_center_encode == center) {
9373  /* ks_dbg("Current center encode %d matches desired center encodes with %d linear encodes out of %d", center, num_inner_encodes, shot_dist.encodes_per_shot); */
9374  /* Prefer odd num_inner_encodes to avoid discontinuity in k-space center, except for the honorable linear sweep */
9375  if (!(num_inner_encodes % 2) &&
9376  (num_inner_encodes > 0) &&
9377  (num_inner_encodes != shot_dist.encodes_per_shot)) {
9379  num_inner_coords - shot_dist.shots,
9380  mtf_direction,
9381  sweep_sign,
9382  shot_dist.shots,
9383  start_from) == current_center_encode) {
9384  num_inner_encodes -= 1;
9385  /* ks_dbg("Found odd solution with %d/%d linear encodes", num_inner_encodes, shot_dist.encodes_per_shot); */
9386  } else {
9387  /* ks_dbg("Odd solution not possible"); */
9388  }
9389  }
9390  break;
9391  }
9392  } /* Done search inner */
9393 
9394 
9395  num_inner_coords = IMin(2, num_inner_encodes * shot_dist.shots, num_coords);
9396  /* ks_dbg("center = %d\tnum_inner_encodes = %d\tnum_inner_coords = %d\tshot_dist.shots = %d", center, num_inner_encodes, num_inner_coords, shot_dist.shots); */
9397  /* Sort the inner coords */
9398  if (mtf_direction == MTF_Y) {
9399  qsort(views, num_inner_coords, sizeof(KS_VIEW), ks_get_comp_kview_y_z(sweep_sign));
9400  } else {
9401  qsort(views, num_inner_coords, sizeof(KS_VIEW), ks_get_comp_kview_z_y(sweep_sign));
9402  }
9403  /* Assign the inner coords */
9404  ks_assign_encodes(views, num_inner_coords, shot_dist.readout_start + start_from, num_inner_encodes, 1);
9405 
9406  /* Sort rest of coords by R and assign them */
9407  /* Assign the outer coords - which are already sorted by abs(mtf_dir) */
9408  const int num_outer_coords = num_coords - num_inner_coords;
9409  if (num_outer_coords > 0) {
9410  /* Single-shot solution - This is still applicable to multi-shot but has different MTF than split-shot mode */
9411  if (split_shots == 0) { /* LCPO */
9412  int is_split_segment[] = {0, 0};
9413  const int num_shots = shot_dist.shots; /* CEIL_DIV(num_inner_coords, num_inner_encodes); */ /* Segment size should match linear inner segments */
9414  const int sign = start_from == 0 ? 1 : -1;
9415  (void)sign;
9416  int rest_start = shot_dist.readout_start + start_from == 0 ? num_inner_encodes
9417  : (start_from - 1);
9418  KS_VIEW* outer_views = views + num_inner_coords;
9419  /* Assign encode segments pair-wise radially until there are no remaining coordinates */
9420  int rem_outer_coords = num_outer_coords;
9421  while (rem_outer_coords > 0) {
9422  KS_VIEW* next_pair = outer_views + num_outer_coords - rem_outer_coords;
9423 
9424  const int next_pair_size = IMin(2, 2 * num_shots, rem_outer_coords);
9425 
9426  /* Sort along MTF (Y / Z) ascendingly */
9427  if (mtf_direction == MTF_Y) {
9428  qsort(next_pair, next_pair_size, sizeof(KS_VIEW), ks_get_comp_kview_y_z(1));
9429  } else {
9430  qsort(next_pair, next_pair_size, sizeof(KS_VIEW), ks_get_comp_kview_z_y(1));
9431  }
9432 
9433 
9434  int num_first = IMin(2, num_shots, rem_outer_coords);
9435  rem_outer_coords -= num_first;
9436  int num_second = IMin(2, num_shots, rem_outer_coords);
9437  rem_outer_coords -= num_second;
9438 
9439 
9440  if (mtf_direction == MTF_Y) {
9441  is_split_segment[0] = next_pair[0].coord->y * next_pair[num_first - 1].coord->y < 0;
9442  if (num_second) {
9443  is_split_segment[1] = next_pair[num_first].coord->y * next_pair[num_first + num_second - 1].coord->y < 0;
9444  }
9445  } else {
9446  is_split_segment[0] = next_pair[0].coord->z * next_pair[num_first - 1].coord->z < 0;
9447  if (num_second) {
9448  is_split_segment[1] = next_pair[num_first].coord->z * next_pair[num_first + num_second - 1].coord->z < 0;
9449  }
9450  }
9451 
9452  if (is_split_segment[0]) {
9453  ks_assign_encodes(next_pair, num_first, rest_start++, 1, 1);
9454  ks_assign_encodes(next_pair + num_first, num_second, rest_start++, 1, 1);
9455  } else if (is_split_segment[1]) {
9456  ks_assign_encodes(next_pair + num_first, num_second, rest_start++, 1, 1);
9457  ks_assign_encodes(next_pair, num_first, rest_start++, 1, 1);
9458  } else { /* Both segments are full */
9459  /* On same side? */
9460  int same_side = 0;
9461  if (mtf_direction == MTF_Y) {
9462  same_side = next_pair[0].coord->y * next_pair[num_first + num_second - 1].coord->y > 0;
9463  } else {
9464  same_side = next_pair[0].coord->z * next_pair[num_first + num_second - 1].coord->z > 0;
9465  }
9466  if (same_side) {
9467  if (mtf_direction == MTF_Y) {
9468  qsort(next_pair, num_first+num_second, sizeof(KS_VIEW), &ks_comp_kview_dist_y_z);
9469  } else {
9470  qsort(next_pair, num_first+num_second, sizeof(KS_VIEW), &ks_comp_kview_dist_z_y);
9471  }
9472  ks_assign_encodes(next_pair , num_first, rest_start++, 1, 1);
9473  ks_assign_encodes(next_pair + num_first, num_second, rest_start++, 1, 1);
9474  } else { /* On both sides */
9475  if (sweep_sign == 1) { /* low-to-high */
9476  ks_assign_encodes(next_pair, num_first, rest_start++, 1, 1);
9477  ks_assign_encodes(next_pair + num_first, num_second, rest_start++, 1, 1);
9478  /* qsort(next_pair, num_first+num_second, sizeof(KS_VIEW), ks_get_comp_kview_y_z(sweep_sign)); */
9479  } else { /* high-to-low */
9480  ks_assign_encodes(next_pair + num_first, num_second, rest_start++, 1, 1);
9481  ks_assign_encodes(next_pair, num_first, rest_start++, 1, 1);
9482  /* qsort(next_pair, num_first+num_second, sizeof(KS_VIEW), ks_get_comp_kview_y_z(sweep_sign)); */
9483  }
9484  }
9485  }
9486 
9487  }
9488  } else { /* Split-shot solution */
9489  if (mtf_direction == MTF_Y) {
9490  qsort(views + num_inner_coords, num_outer_coords, sizeof(KS_VIEW), &ks_comp_kview_dist_y_z);
9491  } else {
9492  qsort(views + num_inner_coords, num_outer_coords, sizeof(KS_VIEW), &ks_comp_kview_dist_z_y);
9493  }
9494 
9495  ks_assign_encodes(views + num_inner_coords,
9496  num_outer_coords,
9497  shot_dist.readout_start + start_from == 0 ? num_inner_encodes
9498  : (start_from - 1),
9499  shot_dist.encodes_per_shot - num_inner_encodes,
9500  start_from == 0 ? 1 : -1);
9501  }
9502  } else {
9503  /* ks_dbg("No pivot coords"); */
9504  }
9505  return SUCCESS;
9506 }
int shots
Definition: KSFoundation.h:2166
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 readout_start
Definition: KSFoundation.h:2169
Definition: KSFoundation.h:2105
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
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
ks_comparator ks_get_comp_kview_z_y(int ascend)
Definition: KSFoundation_host.c:8629
int z
Definition: KSFoundation.h:2202
ADDTITLEHERE
Definition: KSFoundation.h:2165
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
STATUS ks_assign_encodes(KS_VIEW *const views, const int num_views, const int start_encode, const int num_encodes, const int sign)
Definition: KSFoundation_host.c:9238
#define KS_THROW(format,...)
Definition: KSFoundation.h:181
int y
Definition: KSFoundation.h:2201

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

◆ set_grid()

void set_grid ( int *  grid,
int  Nz,
int  y,
int  z 
)
10157  {
10158  grid[y*Nz + z] = 1;
10159 }

◆ get_grid()

int get_grid ( int *  grid,
int  Nz,
int  y,
int  z 
)
10164  {
10165  return grid[y*Nz + z];
10166 }

◆ rand_float_in_range()

float rand_float_in_range ( float  min,
float  max 
)
10171  {
10172  float random = (float)(rand()) / (float)RAND_MAX; /* [0.0, 1.0] */
10173  return (min + (random)*(max-min));
10174 }

◆ ks_poisson_disc_min_spacing()

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 
)
10185  {
10186  const float center_dist = sqrt( (y + 0.5 - center_y)*(y + 0.5 - center_y) +
10187  (z + 0.5 - center_z)*(z + 0.5 - center_z) );
10188  const float max_dist = sqrt(center_z*center_z + center_y*center_y);
10189  if (pattern == EXPONENTIAL) {
10190  return (min_spacing * exp( log(max_spacing/min_spacing) * (center_dist / max_dist) ));
10191  } else if (pattern == LINEAR) {
10192  return (min_spacing + (center_dist / max_dist)*(max_spacing - min_spacing));
10193  } else {
10194  ks_error("%s: radius pattern (%d) not implemented", __FUNCTION__, (int)pattern);
10195  return -1.0f;
10196  }
10197 }
STATUS ks_error(const char *format,...) __attribute__((format(printf
Common error message function for HOST and TGT
Definition: KSFoundation.h:2156
Definition: KSFoundation.h:2155

◆ check_conflicts()

int check_conflicts ( int *  grid,
int  Ny,
int  Nz,
KS_KCOORD  cand,
float  cand_radius 
)
10202  {
10203  const int block_size = 2*(int)ks_round(cand_radius) + 1;
10204  const float center = block_size / 2.0;
10205  int y, z;
10206  int conflict = 0;
10207  for (y = 0; y < block_size; y++) {
10208  int ycoord = cand.y - block_size/2 + y;
10209  if (ycoord < 0 || ycoord >= Ny) {
10210  continue;
10211  }
10212  for (z = 0; z < block_size; z++) {
10213  int zcoord = cand.z - block_size/2 + z;
10214  if (zcoord < 0 || zcoord >= Nz) {
10215  continue;
10216  }
10217 
10218  /* Is within radius? */
10219  if (sqrt((y + 0.5 - center)*(y + 0.5 - center) + (z + 0.5 - center)*(z + 0.5 - center)) < cand_radius) {
10220  conflict |= get_grid(grid, Nz, ycoord, zcoord);
10221  if (conflict == 1) {
10222  return conflict;
10223  }
10224  }
10225  }
10226  }
10227  return conflict;
10228 }
int get_grid(int *grid, int Nz, int y, int z)
Definition: KSFoundation_host.c:10164
#define ks_round(x)
Definition: KSFoundation.h:169
int z
Definition: KSFoundation.h:2202
int y
Definition: KSFoundation.h:2201

◆ remove_element()

void remove_element ( KS_KCOORD coord_array,
int  idx,
int  size 
)
10233  {
10234  int i;
10235  for (i=idx; i < (size-1); i++) {
10236  coord_array[i] = coord_array[i+1];
10237  }
10238 }

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

Variable Documentation

◆ cfsrmode

int cfsrmode

◆ cfgcoiltype

int cfgcoiltype

◆ rhkacq_uid

int rhkacq_uid

◆ ks_plot_filefmt

int ks_plot_filefmt

◆ ks_plot_kstmp

int ks_plot_kstmp

◆ ks_psdname

char ks_psdname[256]

◆ grad_heat_handle

struct _grad_heat_handle grad_heat_handle = {NULL, NULL, 0, NULL}

◆ gradHeatMethod

int gradHeatMethod

◆ enforce_minseqseg

int enforce_minseqseg

◆ _optr

_cvint _optr

◆ minseqcable_t

int minseqcable_t

◆ minseqbusbar_t

int minseqbusbar_t

◆ ks_plot_enable

int ks_plot_enable = 0