KSFoundation  [October2024]
A platform for structured EPIC programming on GE MR systems
Feature module: Dixon (ksdixon.[h,cc])

Data Structures

struct  KSDIXON_DESIGN
 
struct  KSDIXON_READTRAP
 
struct  KSDIXON_DUALREADTRAP
 
struct  KSDIXON_STATE
 

Macros

#define KSDIXON_MAX_POINTS   2 /* Maximum number of chemical shift encodes */
 
#define KSDIXON_UPDATE_UI(dixon_mode, kdesign, ks_op_dixon_mode, ks_op_dixon_tr_mode, ks_op_dixon_shift)   ksdixon_update_UI(dixon_mode, kdesign, KS_EXPAND_OPUSER(ks_op_dixon_mode), KS_EXPAND_OPUSER(ks_op_dixon_tr_mode), KS_EXPAND_OPUSER(ks_op_dixon_shift))
 
#define KSDIXON_INIT_UI(rbw, xres, fov, ks_op_dixon_mode, ks_op_dixon_tr_mode, ks_op_dixon_shift)   ksdixon_init_UI(rbw, xres, fov, KS_EXPAND_OPUSER(ks_op_dixon_mode), KS_EXPAND_OPUSER(ks_op_dixon_tr_mode), KS_EXPAND_OPUSER(ks_op_dixon_shift))
 
#define KSDIXON_SETUP(dixon_settings, ks_op_dixon_mode, ks_op_dixon_tr_mode, ks_op_dixon_shift)   ksdixon_setup(dixon_settings, KS_EXPAND_OPUSER(ks_op_dixon_mode), KS_EXPAND_OPUSER(ks_op_dixon_tr_mode), KS_EXPAND_OPUSER(ks_op_dixon_shift));
 
#define KSDIXON_MODE_DESCR   "Dixon [1=shift, 2=dBW, 3=spl, 4=invrmp, 5=triptr, 6=dBWs}"
 
#define KSDIXON_MODE_NUM_MODES   7
 
#define KSDIXON_TR_MODE_DESCR   "TR [0:single 1:multi]"
 
#define KSDIXON_SHIFT_DEFAULT   (30000.0/(float)cffield * 1000)
 
#define KSDIXON_SHIFT_DESC   "Shift [us]"
 
#define KSDIXON_INIT_DESIGN   {KSDIXON_OFF, 1, KS_INITVALUE(KSDIXON_MAX_POINTS,0), KS_NOTSET, 1.0, KS_NOTSET, KS_NOTSET, KS_NOTSET, KS_NOTSET, KS_NOTSET, KS_NOTSET, KSDIXON_SINGLE_TR}
 
#define KSDIXON_INIT_READTRAP   {KS_INITVALUE(4,0.0), KS_INITVALUE(4,0.0)}
 
#define KSDIXON_INIT_DUALREADTRAP   {KSDIXON_INIT_READTRAP, KSDIXON_INIT_READTRAP, 0.0, 0.0, 0}
 
#define KSDIXON_INIT_STATE   {KS_INITVALUE(3, KS_NOTSET), KS_INITVALUE(KSDIXON_MAX_POINTS,0), 0}
 

Enumerations

enum  KSDIXON_MODE {
  KSDIXON_OFF = 0, KSDIXON_SHIFTED = 1, KSDIXON_DBW = 2, KSDIXON_ASYM_SPLINE = 3,
  KSDIXON_ASYM_INVRAMP = 4, KSDIXON_TRIP_TRAP = 5, KSDIXON_DBW_STAGGERED = 6
}
 
enum  KSDIXON_TR_MODE { KSDIXON_SINGLE_TR = 0, KSDIXON_MULTI_TR = 1 }
 

Functions

float ksdixon_calc_dephasing_period ()
 
float ksdixon_calc_nsa (float t1, float t2, float var1, float var2, float dephasing_period)
 
int ksdixon_max_shift (KSDIXON_MODE mode, int duration, float xres, float fov)
 
STATUS ksdixon_dbw_time2echo_hbw (float *time2echo, const float *const t, const float *const G, const float area2echo)
 
STATUS ksdixon_dbw_time2echo_lbw (float *time2echo, const float *const t, const float *const G, const float area2echo)
 
STATUS ksdixon_time2echo_from_readtrap (float *time2echo, KSDIXON_READTRAP *readtrap, float area2echo)
 
STATUS ksdixon_dualreadtrap_eval_shifts (KSDIXON_DUALREADTRAP *dualreadtrap, float area2echo, float shift_at_acq_start, float deadtime)
 
STATUS ksdixon_eval_acq_trap (KSDIXON_READTRAP *readtrap, int acq_duration, float sample_area, float max_start_amp, float sampling_threshold_amp, float slew, float amp_limit)
 
STATUS ksdixon_eval_dualreadtrap (KSDIXON_DUALREADTRAP *dualreadtrap, int acq_duration1, int acq_duration2, float sample_area, float max_start_end_amp, float sampling_threshold_amp)
 
STATUS ksdixon_eval_gre_dBW_acq_waves (KS_WAVE *wave1, KS_WAVE *wave2, int *shift1, int *shift2, int *nover, int acq_start_time, float sampling_threshold_amp, int min_nover, int fov, int res, KS_DESCRIPTION desc)
 
STATUS ksdixon_eval_gre_dualBW_readwave (KS_READWAVE *readwave1, KS_READWAVE *readwave2, const KS_KSPACE_DESIGN *kdesign, KSDIXON_DESIGN *dixon, const int acq_start_time, KS_DESCRIPTION desc)
 
STATUS ksdixon_eval_asym_spline_acq_wave (KS_WAVE *wave, int shift, int duration, float area, KS_DESCRIPTION desc)
 
STATUS ksdixon_eval_trip_trap_acq_points (float *t, float *G, int shift, int duration, float area, float Mc, float g1, float s)
 
float ksdixon_dbw_ramptime (const float dur, const float sample_area, const float slewrate)
 
STATUS ksdixon_eval_dbw_acq_waves (KS_WAVE *lbw_wave, KS_WAVE *hbw_wave, int *lbw_cse, int *hbw_cse, float *pf, const float min_pf, const int duration, const float area_without_pf, KS_DESCRIPTION desc)
 
STATUS ksdixon_eval_flat_acq_wave (KS_WAVE *wave, int duration, float area, KS_DESCRIPTION desc)
 
STATUS ksdixon_eval_inverted_ramp_acq_wave (KS_WAVE *wave, int shift, int duration, float area, KS_DESCRIPTION desc)
 
STATUS ksdixon_eval_trip_trap_acq_wave (KS_WAVE *wave, int shift, int duration, float area, KS_DESCRIPTION desc)
 
float ksdixon_amp_limit (float board_amp_limit, float fov)
 
STATUS ksdixon_eval_inverted_ramps (KS_WAVE *acq_waves, int *shifts, int points, const KS_KSPACE_DESIGN *kdesign, KS_DESCRIPTION desc)
 
STATUS ksdixon_eval_inverted_ramp_acq_points (float *t, float *G, int shift, int duration, float area, float s, float amp_limit)
 
void ksdixon_init_settings (KSDIXON_DESIGN *const dixon)
 
STATUS ksdixon_setup (KSDIXON_DESIGN *pdixon, const int opnum_dixon_mode, _cvfloat *_opuser_dixon_mode, const int opnum_dixon_tr_mode, _cvfloat *_opuser_dixon_tr_mode, const int opnum_dixon_shift, _cvfloat *_opuser_dixon_shift)
 
STATUS ksdixon_eval_validate_settings (const KSDIXON_DESIGN *dixon)
 
void ksdixon_update_UI (const KSDIXON_MODE dixon_mode, const KS_KSPACE_DESIGN *const kdesign, const int opnum_dixon_mode, _cvfloat *_opuser_dixon_mode, const int opnum_dixon_tr_mode, _cvfloat *_opuser_dixon_tr_mode, const int opnum_dixon_shift, _cvfloat *_opuser_dixon_shift)
 
void ksdixon_init_UI (float rbw, int xres, float fov, const int opnum_dixon_mode, _cvfloat *_opuser_dixon_mode, const int opnum_dixon_tr_mode, _cvfloat *_opuser_dixon_tr_mode, const int opnum_dixon_shift, _cvfloat *_opuser_dixon_shift)
 
STATUS ksdixon_eval_setup_gre_readouts (KS_ECHOTRAIN *echotrain, KSDIXON_DESIGN *dixon, const KS_KSPACE_DESIGN *kdesign, const int acq_start_time, const int TR_index, KS_DESCRIPTION desc)
 
STATUS ksdixon_set_design (KSDIXON_DESIGN *const dixon_design, const KS_KSPACE_DESIGN *const kdesign)
 
STATUS ksdixon_setup_fse_echotrain (KS_ECHOTRAIN_DESIGN *const echotrain_design, KSDIXON_STATE *const dixon_state, const KSDIXON_DESIGN *const dixon_design, const int ETL, float xcrusher_area, const KS_DESCRIPTION description)
 
STATUS ksdixon_eval_trip_traps (KS_WAVE *acq_waves, int *shifts, int points, const KS_KSPACE_DESIGN *kdesign, KS_DESCRIPTION desc)
 

Variables

int piuset
 

Detailed Description

Macro Definition Documentation

◆ KSDIXON_MAX_POINTS

#define KSDIXON_MAX_POINTS   2 /* Maximum number of chemical shift encodes */

◆ KSDIXON_UPDATE_UI

#define KSDIXON_UPDATE_UI (   dixon_mode,
  kdesign,
  ks_op_dixon_mode,
  ks_op_dixon_tr_mode,
  ks_op_dixon_shift 
)    ksdixon_update_UI(dixon_mode, kdesign, KS_EXPAND_OPUSER(ks_op_dixon_mode), KS_EXPAND_OPUSER(ks_op_dixon_tr_mode), KS_EXPAND_OPUSER(ks_op_dixon_shift))

Macro that calls ksdixon_update_UI. Currently not used. The idea is to update dixon opusers according to kdesign and dixon_mode.

◆ KSDIXON_INIT_UI

#define KSDIXON_INIT_UI (   rbw,
  xres,
  fov,
  ks_op_dixon_mode,
  ks_op_dixon_tr_mode,
  ks_op_dixon_shift 
)    ksdixon_init_UI(rbw, xres, fov, KS_EXPAND_OPUSER(ks_op_dixon_mode), KS_EXPAND_OPUSER(ks_op_dixon_tr_mode), KS_EXPAND_OPUSER(ks_op_dixon_shift))

Macro that calls ksdixon_init_UI. The purpose of these macros is to directly pass the opuser number rather than the full cv (the macro does the expansion for you). For instance:

In implementation e file

// Select opuser numbers for dixon options
@global
#define OPUSER_NUM_DIXON_MODE 17
#define OPUSER_NUM_DIXON_TR_MODE 18
#define OPUSER_NUM_DIXON_SHIFT 19
ksfse2_init_UI():
// Setup the opuser description, range, etc.
KSDIXON_INIT_UI(oprbw, opxres, opfov, OPUSER_NUM_DIXON_MODE, OPUSER_NUM_DIXON_TR_MODE, OPUSER_NUM_DIXON_SHIFT);
Parameters
[in]rbwReadout bandwidth (oprbw)
[in]xresNumber of readout samples prescribed (opxres)
[in]fovReadout FOV (opfov) [mm]
[in]ks_op_dixon_modeopuser number for selecting dixon mode (e.g. 17 for opuser17)
[in]ks_op_dixon_tr_modeopuser number for selecting dixon TR mode (multi/single TR)
[in]ks_op_dixon_shiftopuser number for selecting dixon cse (shift in us)

◆ KSDIXON_SETUP

#define KSDIXON_SETUP (   dixon_settings,
  ks_op_dixon_mode,
  ks_op_dixon_tr_mode,
  ks_op_dixon_shift 
)    ksdixon_setup(dixon_settings, KS_EXPAND_OPUSER(ks_op_dixon_mode), KS_EXPAND_OPUSER(ks_op_dixon_tr_mode), KS_EXPAND_OPUSER(ks_op_dixon_shift));

Macro that calls ksdixon_setup. The purpose of these macros is to directly pass the opuser number rather than the full cv (the macro does the expansion for you). For instance:

In implementation e file

// Select opuser numbers for dixon options
@global
#define OPUSER_NUM_DIXON_MODE 17
#define OPUSER_NUM_DIXON_TR_MODE 18
#define OPUSER_NUM_DIXON_SHIFT 19
ksfse2_setup_stretched():
// Fill ksfse2_dixon_settings with dixon related values.
status = KSDIXON_SETUP(&ksfse2_dixon_settings, OPUSER_NUM_DIXON_MODE, OPUSER_NUM_DIXON_TR_MODE, OPUSER_NUM_DIXON_SHIFT);
Parameters
[out]dixon_settings- An instance of KSDIXON_DESIGN that will be filled according to the values in the opusers
[in]ks_op_dixon_mode- opuser number for selecting dixon mode (e.g. 17 for opuser17)
[in]ks_op_dixon_tr_mode- opuser number for selecting dixon TR mode (multi/single TR)
[in]ks_op_dixon_shift- opuser number for selecting dixon cse (shift in us)
Return values
STATUSSUCCESS or FAILURE

◆ KSDIXON_MODE_DESCR

#define KSDIXON_MODE_DESCR   "Dixon [1=shift, 2=dBW, 3=spl, 4=invrmp, 5=triptr, 6=dBWs}"

◆ KSDIXON_MODE_NUM_MODES

#define KSDIXON_MODE_NUM_MODES   7

◆ KSDIXON_TR_MODE_DESCR

#define KSDIXON_TR_MODE_DESCR   "TR [0:single 1:multi]"

◆ KSDIXON_SHIFT_DEFAULT

#define KSDIXON_SHIFT_DEFAULT   (30000.0/(float)cffield * 1000)

◆ KSDIXON_SHIFT_DESC

#define KSDIXON_SHIFT_DESC   "Shift [us]"

◆ KSDIXON_INIT_DESIGN

◆ KSDIXON_INIT_READTRAP

#define KSDIXON_INIT_READTRAP   {KS_INITVALUE(4,0.0), KS_INITVALUE(4,0.0)}

◆ KSDIXON_INIT_DUALREADTRAP

#define KSDIXON_INIT_DUALREADTRAP   {KSDIXON_INIT_READTRAP, KSDIXON_INIT_READTRAP, 0.0, 0.0, 0}

◆ KSDIXON_INIT_STATE

#define KSDIXON_INIT_STATE   {KS_INITVALUE(3, KS_NOTSET), KS_INITVALUE(KSDIXON_MAX_POINTS,0), 0}

Enumeration Type Documentation

◆ KSDIXON_MODE

Enums for selecting a Dixon mode. This typically changes the readout window but is not restricted to only that.

  • KSDIXON_OFF - No Dixon
  • KSDIXON_SHIFTED - Conventional CSE using shifted gradients [Hardy PA, Hinks RS, Tkach JA. Separation of fat and water in fast spin‐echo MR imaging with the three‐point dixon technique. J. Magn. Reson. Imaging 1995;5:181–185.]
  • KSDIXON_DBW - Dual bandwidths [Rydén H, Berglund J, Norbeck O, et al. RARE two-point Dixon with dual bandwidths. Magn. Reson. Med. 2020;84:2456–2468.]
  • KSDIXON_ASYM_SPLINE - Asymmetric spline [Rydén H, Norbeck O, Avventi E, et al. Chemical shift encoding using asymmetric readout waveforms. Magn. Reson. Med. 2020;42:963.]
  • KSDIXON_ASYM_INVRAMP - Maximum CSE
  • KSDIXON_TRIP_TRAP - Asymmetric piece-wise linear waveform with three bandwidths [FSE Dixon with bandwidth-matched asymmetric readouts tailored for real-valued fat/water estimates, ISMRM 2023]
Enumerator
KSDIXON_OFF 
KSDIXON_SHIFTED 
KSDIXON_DBW 
KSDIXON_ASYM_SPLINE 
KSDIXON_ASYM_INVRAMP 
KSDIXON_TRIP_TRAP 
KSDIXON_DBW_STAGGERED 
98  {KSDIXON_OFF = 0,
99  KSDIXON_SHIFTED = 1,
100  KSDIXON_DBW = 2,
103  KSDIXON_TRIP_TRAP = 5,
105 } KSDIXON_MODE;
Definition: ksdixon.h:99
KSDIXON_MODE
Enums for selecting a Dixon mode. This typically changes the readout window but is not restricted to ...
Definition: ksdixon.h:98
Definition: ksdixon.h:98
Definition: ksdixon.h:102
Definition: ksdixon.h:101
Definition: ksdixon.h:103
Definition: ksdixon.h:100
Definition: ksdixon.h:104

◆ KSDIXON_TR_MODE

Enumerator
KSDIXON_SINGLE_TR 
KSDIXON_MULTI_TR 
112  {KSDIXON_SINGLE_TR = 0, /* Echoes in echo train toggle between chemical shift encodes */
113  KSDIXON_MULTI_TR = 1 /* One CSE per shot */
Definition: ksdixon.h:112
KSDIXON_TR_MODE
Definition: ksdixon.h:112
Definition: ksdixon.h:113

Function Documentation

◆ ksdixon_calc_dephasing_period()

float ksdixon_calc_dephasing_period ( )

Period of dephasing between fat and water [usec]

Uses cv cffield to determine field strength.

Returns
[us] float
46  {
47  float delta = 3.4e-6; /* chemical shift is 3.4 ppm */
48  float gyro = 42.5759; /* MHz/T */
49  float B0 = cffield * 1e-4; /* T */
50 
51  return 1.0f / (gyro * B0 * delta); /* usec */
52 
53 } /* ksdixon_calc_dephasing_period() */

◆ ksdixon_calc_nsa()

float ksdixon_calc_nsa ( float  t1,
float  t2,
float  var1,
float  var2,
float  dephasing_period 
)

Calculate mean single-species weighted NSA for 2-point Dixon

Following Eq. 7 in Ryden et al. MRM 84(5):2456-68, 2020 (using noise variance var=1/w^2)

Parameters
t1
t2
var1
var2
dephasing_period
Returns
float
60  {
61  float wsq1 = 1.0 / var1;
62  float wsq2 = 1.0 / var2;
63  const float c1 = cos(2 * PI / dephasing_period * t1);
64  const float c2 = cos(2 * PI / dephasing_period * t2);
65 
66  return (wsq1 * wsq2 * pow(c1 - c2, 2) * (wsq1*c1*c1 + wsq2*c2*c2 + wsq1 + wsq2) / (2 * (wsq1 + wsq2) * (wsq1*c1*c1 + wsq2*c2*c2)) );
67 
68 } /* ksdixon_calc_nsa() */

◆ ksdixon_max_shift()

int ksdixon_max_shift ( KSDIXON_MODE  mode,
int  duration,
float  xres,
float  fov 
)

Find the max shift for for a certain Dixon mode using binary sarch. Used for get ranges in the UI

Parameters
[in]modeThe dixon mode
[in]duration[us] Acq duration
[in]xresNumber of samples along x
[in]fov[mm] FOV
Returns
int [us] max CSE
73  {
74  STATUS status;
75  float sample_area = xres * ks_calc_fov2gradareapixel(fov);
76  float tsp = duration / xres;
77  float trap_amp = ((float) (1.0 / (GAM * tsp * 1e-6) * (10.0 / fov)));
78 
79  KS_WAVE acq_wave;
80  int right_shift = duration / 2; /* Maximum shift, shouldn't be possible */
81  int left_shift = 0;
82  int shift;
83  int iters = 20;
84  int diff = duration;
85  int i = 0;
86  /* Binary search */
87  for (; (i < iters) && (diff >= 8) /* us */; i++) {
88  shift = (right_shift + left_shift) / 2;
89  if (mode == KSDIXON_ASYM_SPLINE) {
90  KS_DESCRIPTION tmpdesc = "ui_maxshift";
91  status = ksdixon_eval_asym_spline_acq_wave(&acq_wave, shift, duration, sample_area, tmpdesc);
92  } else if (mode == KSDIXON_TRIP_TRAP) {
93  float t[6] = {KS_NOTSET};
94  float G[6] = {KS_NOTSET};
95  status = ksdixon_eval_trip_trap_acq_points(t, G, shift, duration, sample_area, .25*sample_area, trap_amp, ks_syslimits_slewrate1(loggrd));
96  } else if (mode == KSDIXON_ASYM_INVRAMP) {
97  float t[4] = {};
98  float G[4] = {};
99  status = ksdixon_eval_inverted_ramp_acq_points(t, G, shift, duration, sample_area, .95 * ks_syslimits_slewrate1(loggrd), ksdixon_amp_limit(ks_syslimits_ampmax1(loggrd), fov));
100  } else if (mode == KSDIXON_DBW || mode == KSDIXON_DBW_STAGGERED) {
101  return 0;
102  } else {
103  return ks_error("%s: Not implemented for the requested dixon method. It is easy and you should do it now.", __FUNCTION__);
104  }
105  if (status == SUCCESS) {
106  diff = shift - left_shift;
107  left_shift = shift;
108  } else {
109  right_shift = shift;
110  }
111  }
112  return left_shift;
113 
114 } /* ksdixon_max_shift() */
float ks_calc_fov2gradareapixel(float fov)
Calculates the gradient area needed to move one pixel in k-space
Definition: KSFoundation_common.c:492
float ksdixon_amp_limit(float board_amp_limit, float fov)
Returns the highest amplitude allowed. This can either be the board max, or restricted by the FOV...
Definition: ksdixon.cc:767
#define KS_NOTSET
Definition: KSFoundation.h:115
Core sequence object making arbitrary waveforms on any board (using float data format)
Definition: KSFoundation.h:743
STATUS ks_error(const char *format,...) __attribute__((format(printf
Common error message function for HOST and TGT
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
Definition: ksdixon.h:102
Definition: ksdixon.h:101
LOG_GRAD loggrd
Definition: ksdixon.h:103
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
Definition: ksdixon.h:100
STATUS ksdixon_eval_asym_spline_acq_wave(KS_WAVE *wave, int shift, int duration, float area, KS_DESCRIPTION desc)
Evaluate an asymmetric spline-based acq wave according to [Rydén H, et al. Chemical shift encoding us...
Definition: ksdixon.cc:384
Definition: ksdixon.h:104
STATUS ksdixon_eval_trip_trap_acq_points(float *t, float *G, int shift, int duration, float area, float Mc, float g1, float s)
Generates support points for an asymmetric piece-wise linear waveform with three bandwidths[FSE Dixon...
Definition: ksdixon.cc:469
char KS_DESCRIPTION[KS_DESCRIPTION_LENGTH]
Definition: KSFoundation.h:351
STATUS ksdixon_eval_inverted_ramp_acq_points(float *t, float *G, int shift, int duration, float area, float s, float amp_limit)
Generates support points for an inverted ramp waveform.
Definition: ksdixon.cc:817

◆ ksdixon_dbw_time2echo_hbw()

STATUS ksdixon_dbw_time2echo_hbw ( float *  time2echo,
const float *const  t,
const float *const  G,
const float  area2echo 
)
119  {
120  const float ramp_up_area = (t[2] - t[0]) * (G[2] + G[0]) / 2.0;
121  const float plateau_area = (t[3] - t[2]) * G[2];
122  if (area2echo < ramp_up_area) { /* echo is on ramp up */
123  const float slew = (G[2] - G[0]) / (t[2] - t[0]);
124  *time2echo = sqrt(2 * area2echo / slew);
125  } else if (area2echo < (ramp_up_area + plateau_area) ) { /* echo is on plateau */
126  *time2echo = t[2] + (area2echo - ramp_up_area) / G[2];
127  } else {
128  return ks_error("%s: hbw cannot reach area2echo (%f)", __FUNCTION__, area2echo);
129  }
130 
131  return SUCCESS;
132 
133 } /* ksdixon_dbw_time2echo_hbw() */
STATUS ks_error(const char *format,...) __attribute__((format(printf
Common error message function for HOST and TGT

◆ ksdixon_dbw_time2echo_lbw()

STATUS ksdixon_dbw_time2echo_lbw ( float *  time2echo,
const float *const  t,
const float *const  G,
const float  area2echo 
)
138  {
139  const float ramp_up_area = (t[1] - t[0]) * (G[1] + G[0]) / 2.0;
140  const float plateau_area = (t[2] - t[1]) * G[1];
141  if (area2echo < (ramp_up_area + plateau_area)) { /* echo is on plateau */
142  *time2echo = t[1] + (area2echo - ramp_up_area) / G[1];
143  } else { /* echo is on ramp down */
144  const float slew = (G[2] - G[3]) / (t[3] - t[2]);
145  const float square = pow((G[2] / slew), 2) - 2.0 * (area2echo - ramp_up_area - plateau_area) / slew;
146  if (square < 0.0) {
147  return ks_error("%s: lbw cannot reach area2echo (%f)", __FUNCTION__, area2echo);
148  }
149  *time2echo = t[2] + G[2] / slew - sqrt(square);
150  }
151 
152  return SUCCESS;
153 
154 } /* ksdixon_dbw_time2echo_lbw() */
STATUS ks_error(const char *format,...) __attribute__((format(printf
Common error message function for HOST and TGT

◆ ksdixon_time2echo_from_readtrap()

STATUS ksdixon_time2echo_from_readtrap ( float *  time2echo,
KSDIXON_READTRAP readtrap,
float  area2echo 
)

Calculate the time until k-space center is reached given a KSDIXON_READTRAP and the area to center

Parameters
[out]time2echo[us]
[in]readtrapA readtrap support point struct
[in]area2echoArea required to reach k-space center
Return values
STATUSSUCCESS or FAILURE
159  {
160  float* t = readtrap->t;
161  float* G = readtrap->G;
162 
163  return ksdixon_dbw_time2echo_lbw(time2echo, t, G, area2echo);
164 
165 } /* ksdixon_time2echo_from_readtrap() */
STATUS ksdixon_dbw_time2echo_lbw(float *time2echo, const float *const t, const float *const G, const float area2echo)
Definition: ksdixon.cc:138
float t[4]
Definition: ksdixon.h:157
float G[4]
Definition: ksdixon.h:156

◆ ksdixon_dualreadtrap_eval_shifts()

STATUS ksdixon_dualreadtrap_eval_shifts ( KSDIXON_DUALREADTRAP dualreadtrap,
float  area2echo,
float  shift_at_acq_start,
float  deadtime 
)

Calculate the shifts in a Dixon dbw readout.

Parameters
[out]dualreadtrapWill set dualreadtrap->shift1 and dualreadtrap->shift2
[in]area2echoArea to k-space center
[in]shift_at_acq_start[us] CSE when the acq starts
[in]deadtime[us] Deadtime corresponds to inner ramps where amp is below sampling threshold
Return values
STATUSSUCCESS or FAILURE
170  {
171  STATUS status;
172  float time2echo;
173 
174  status = ksdixon_time2echo_from_readtrap(&time2echo, &dualreadtrap->trap1, area2echo);
175  KS_RAISE(status);
176  dualreadtrap->shift1 = shift_at_acq_start + time2echo;
177 
178  status = ksdixon_time2echo_from_readtrap(&time2echo, &dualreadtrap->trap2, area2echo);
179  KS_RAISE(status);
180  /* note that trap2 is "mirrored" */
181  dualreadtrap->shift2 = shift_at_acq_start + dualreadtrap->trap1.t[3] + deadtime + dualreadtrap->trap2.t[3] - time2echo;
182 
183  return SUCCESS;
184 
185 } /* ksdixon_dualreadtrap_eval_shifts() */
STATUS ksdixon_time2echo_from_readtrap(float *time2echo, KSDIXON_READTRAP *readtrap, float area2echo)
Calculate the time until k-space center is reached given a KSDIXON_READTRAP and the area to center...
Definition: ksdixon.cc:159
float shift2
Definition: ksdixon.h:173
float t[4]
Definition: ksdixon.h:157
#define KS_RAISE(status)
Definition: KSFoundation.h:190
KSDIXON_READTRAP trap2
Definition: ksdixon.h:171
float shift1
Definition: ksdixon.h:172
KSDIXON_READTRAP trap1
Definition: ksdixon.h:170

◆ ksdixon_eval_acq_trap()

STATUS ksdixon_eval_acq_trap ( KSDIXON_READTRAP readtrap,
int  acq_duration,
float  sample_area,
float  max_start_amp,
float  sampling_threshold_amp,
float  slew,
float  amp_limit 
)

Evaluates readout support points for a trapezoid

Parameters
[out]readtrapStruct with support points
[in]acq_duration[us] Duration of acquisition window
[in]sample_areaSample area
[in]max_start_ampMaximum amp at the start of acquisition
[in]sampling_threshold_ampAmplitude threshold where sampling is toggled
[in]slewSystem slew rate
[in]amp_limitSystem gradient amplitude limit
Return values
STATUSSUCCESS or FAILURE
190  {
191  float amp; /* plateau amplitude */
192  float rem_area = sample_area; /* remaining sample area */
193  rem_area -= acq_duration * sampling_threshold_amp; /* base area up to sampling threshold amp */
194  if (rem_area < 0.0) {
195  /* ks_dbg("%s: Trapezoid did not reach the sampling threshold", __FUNCTION__); */
196  return FAILURE;
197  }
198  /* "middle area" above base area up to max start amp includes a ramp down */
199  float middle_area = (max_start_amp - sampling_threshold_amp) * (acq_duration - (max_start_amp - sampling_threshold_amp) / (2.0 * slew));
200  if (rem_area < middle_area) { /* Need to lower the amplitude */
201  float square = pow(acq_duration * slew, 2) - 2.0 * rem_area * slew;
202  if (square < 0.0) {
203  /* ks_dbg("%s: Trapezoid did not reach max_start_amp", __FUNCTION__); */
204  return FAILURE;
205  }
206  amp = acq_duration * slew - sqrt(square) + sampling_threshold_amp;
207  } else {
208  rem_area -= middle_area; /* remaining "top area" above base and middle areas includes a ramp up and a ramp down */
209  float term = (acq_duration * slew - max_start_amp + sampling_threshold_amp) / 2.0;
210  float square = pow(term, 2) - rem_area * slew;
211  if (square < 0.0) {
212  /* ks_dbg("%s: Trapezoid did not reach plateau", __FUNCTION__); */
213  return FAILURE;
214  }
215  amp = term - sqrt(square) + max_start_amp;
216  }
217 
218  if (amp > amp_limit) {
219  /* ks_dbg("%s: Trapezoid amplitude (%f) exceeds amp_limit (%f)", __FUNCTION__, amp, amp_limit); */
220  return FAILURE;
221  }
222 
223  readtrap->t[0] = 0.0;
224  readtrap->G[0] = FMin(2, max_start_amp, amp);
225 
226  readtrap->t[1] = (amp - readtrap->G[0]) / slew;
227  readtrap->G[1] = amp;
228 
229  readtrap->t[2] = acq_duration - (amp - sampling_threshold_amp) / slew;
230  readtrap->G[2] = amp;
231 
232  readtrap->t[3] = acq_duration;
233  readtrap->G[3] = sampling_threshold_amp;
234 
235  return SUCCESS;
236 
237 } /* ksdixon_eval_acq_trap() */
float t[4]
Definition: ksdixon.h:157
float G[4]
Definition: ksdixon.h:156

◆ ksdixon_eval_dualreadtrap()

STATUS ksdixon_eval_dualreadtrap ( KSDIXON_DUALREADTRAP dualreadtrap,
int  acq_duration1,
int  acq_duration2,
float  sample_area,
float  max_start_end_amp,
float  sampling_threshold_amp 
)

Evaluates a dbw readout (Only evaluation, this should be wrapped around some optimization)

Parameters
[out]dualreadtrap
[in]acq_duration1[us] Duration of first acquisition
[in]acq_duration2[us] Duration of second acquisition
[in]sample_areaSample area for one readout. This value must take partial Fourier into account
[in]max_start_end_ampMaximum amp at the start and end of acquisition
[in]sampling_threshold_ampAmplitude threshold where sampling is toggled on/off
Return values
STATUSSUCCESS or FAILURE
242  {
243  STATUS status;
244 
245  float slew_limit = 0.98 * ks_syslimits_slewrate1(loggrd);
246  float amp_limit = ks_syslimits_ampmax1(loggrd);
247 
248  if (max_start_end_amp > amp_limit) {
249  return ks_error("%s: max_start_end_amp (%f) exceeds amp_limit (%f)", __FUNCTION__, max_start_end_amp, amp_limit);
250  }
251  if (sampling_threshold_amp > max_start_end_amp) {
252  return ks_error("%s: max_start_end_amp (%f) is below sampling_threshold_amp (%f)", __FUNCTION__, max_start_end_amp, sampling_threshold_amp);
253  }
254 
255  status = ksdixon_eval_acq_trap(&dualreadtrap->trap1, acq_duration1, sample_area, max_start_end_amp, sampling_threshold_amp, slew_limit, amp_limit);
256  if (status != SUCCESS) return status;
257 
258  status = ksdixon_eval_acq_trap(&dualreadtrap->trap2, acq_duration2, sample_area, sampling_threshold_amp, sampling_threshold_amp, slew_limit, amp_limit);
259  if (status != SUCCESS) return status;
260 
261  return SUCCESS;
262 
263 } /* ksdixon_eval_dualreadtrap() */
STATUS ksdixon_eval_acq_trap(KSDIXON_READTRAP *readtrap, int acq_duration, float sample_area, float max_start_amp, float sampling_threshold_amp, float slew, float amp_limit)
Evaluates readout support points for a trapezoid
Definition: ksdixon.cc:190
STATUS ks_error(const char *format,...) __attribute__((format(printf
Common error message function for HOST and TGT
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
KSDIXON_READTRAP trap2
Definition: ksdixon.h:171
KSDIXON_READTRAP trap1
Definition: ksdixon.h:170

◆ ksdixon_eval_gre_dBW_acq_waves()

STATUS ksdixon_eval_gre_dBW_acq_waves ( KS_WAVE wave1,
KS_WAVE wave2,
int *  shift1,
int *  shift2,
int *  nover,
int  acq_start_time,
float  sampling_threshold_amp,
int  min_nover,
int  fov,
int  res,
KS_DESCRIPTION  desc 
)

Generates two acq waves for GRE dBW using a brute-force search

Parameters
[out]wave1Output wave 1
[out]wave2Output wave 2
[out]shift1CSE for wave 1
[out]shift2CSE for wave 2
[out]noverPartial Fourier
[in]acq_start_timeTime when first acquisition is turned on
[in]sampling_threshold_ampAmplitude threshold where sampling is toggled on/off
[in]min_noverMinimum number of samples after k-space center ( partial Fourier )
[in]fov[mm]
[in]resNumber of samples in readout direction (as if no partial Fourier)
[in]descDescription
Return values
STATUSSUCCESS or FAILURE
268  {
269  STATUS status = FAILURE;
270 
273 
274  /* Brute-force optimize with free parameters nover (partial Fourier in read dir) and acq_duration of first trap */
275  float max_nsa = 0.0f; /* target for optimization */
276 
277  float dephasing_period = ksdixon_calc_dephasing_period(); /* usec */
278 
279  /* minimum acq_duration of first trap corresponds to in-phase echo for min_nover */
280  int min_dur = RUP_FACTOR((int) ks_round(dephasing_period) / 4, 16 * GRAD_UPDATE_TIME);
281  int max_dur = RUP_GRD((int) ks_round(dephasing_period));
282  int acq_duration1, acq_duration2;
283  for (acq_duration1 = min_dur; acq_duration1 < max_dur; acq_duration1 += 16 * GRAD_UPDATE_TIME) {
284 
285  for (acq_duration2 = min_dur; acq_duration2 < max_dur; acq_duration2 += 16 * GRAD_UPDATE_TIME) {
286 
287  for (dualreadtrap.nover = min_nover; dualreadtrap.nover <= (res/2); dualreadtrap.nover += 4) {
288 
289  float sample_area = (res/2 + dualreadtrap.nover) * ks_calc_fov2gradareapixel(fov);
290 
291  if (SUCCESS != ksdixon_eval_dualreadtrap(&dualreadtrap, acq_duration1, acq_duration2, sample_area, sampling_threshold_amp, sampling_threshold_amp)) {
292  continue;
293  }
294 
295  float area2echo = (res/2) * ks_calc_fov2gradareapixel(fov);
296  /* deadtime corresponds to inner ramps where amp is below sampling threshold */
297  int deadtime = 2 * RUP_GRD((int) (sampling_threshold_amp / ks_syslimits_slewrate1(loggrd)));
298  if (SUCCESS != ksdixon_dualreadtrap_eval_shifts(&dualreadtrap, area2echo, (float) acq_start_time, (float) deadtime)) {
299  return ks_error("%s: failed to eval shifts of dualreadtrap", __FUNCTION__);
300  }
301 
302  if (fabs(dualreadtrap.shift1 - dualreadtrap.shift2) > dephasing_period) {
303  break; /* if this failed, higher nover will also fail */
304  }
305 
306  float nsa = ksdixon_calc_nsa(dualreadtrap.shift1, dualreadtrap.shift2, 1.0 / (float) acq_duration1, 1.0 / (float) acq_duration2, dephasing_period);
307 
308  if (nsa > max_nsa) {
309  max_nsa = nsa;
310  best_dualreadtrap = dualreadtrap;
311  status = SUCCESS;
312  }
313  }
314  }
315  }
316 
317  if (status != SUCCESS) {
318  return ks_error("%s: unable to find dualreadtrap", __FUNCTION__);
319  }
320 
321  *shift1 = ks_round(best_dualreadtrap.shift1);
322  *shift2 = ks_round(best_dualreadtrap.shift2);
323  *nover = best_dualreadtrap.nover;
324 
325  KS_DESCRIPTION tmpdesc;
326  sprintf(tmpdesc, "%s_readtrap_1", desc);
327  status = ks_eval_coords2wave(wave1, best_dualreadtrap.trap1.t, best_dualreadtrap.trap1.G, 4, GRAD_UPDATE_TIME, tmpdesc);
328  KS_RAISE(status);
329 
330  sprintf(tmpdesc, "%s_readtrap_2", desc);
331  status = ks_eval_coords2wave(wave2, best_dualreadtrap.trap2.t, best_dualreadtrap.trap2.G, 4, GRAD_UPDATE_TIME, tmpdesc);
332  KS_RAISE(status);
333 
334  status = ks_eval_mirrorwave(wave2);
335  KS_RAISE(status);
336 
337  return SUCCESS;
338 
339 } /* ksdixon_eval_gre_dBW_acq_waves() */
#define KSDIXON_INIT_DUALREADTRAP
Definition: ksdixon.h:177
STATUS ks_eval_coords2wave(KS_WAVE *wave, float *t, float *G, int num_coords, int dwell, const char *desc) WARN_UNUSED_RESULT
Definition: KSFoundation_host.c:4383
float ks_calc_fov2gradareapixel(float fov)
Calculates the gradient area needed to move one pixel in k-space
Definition: KSFoundation_common.c:492
STATUS ks_error(const char *format,...) __attribute__((format(printf
Common error message function for HOST and TGT
float shift2
Definition: ksdixon.h:173
STATUS ks_eval_mirrorwave(KS_WAVE *wave)
Flips the contents of the KS_WAVEFORM in a KS_WAVE object
Definition: KSFoundation_host.c:2140
float t[4]
Definition: ksdixon.h:157
STATUS ksdixon_eval_dualreadtrap(KSDIXON_DUALREADTRAP *dualreadtrap, int acq_duration1, int acq_duration2, float sample_area, float max_start_end_amp, float sampling_threshold_amp)
Evaluates a dbw readout (Only evaluation, this should be wrapped around some optimization)
Definition: ksdixon.cc:242
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
int nover
Definition: ksdixon.h:174
#define ks_round(x)
Definition: KSFoundation.h:169
STATUS ksdixon_dualreadtrap_eval_shifts(KSDIXON_DUALREADTRAP *dualreadtrap, float area2echo, float shift_at_acq_start, float deadtime)
Calculate the shifts in a Dixon dbw readout.
Definition: ksdixon.cc:170
LOG_GRAD loggrd
#define KS_RAISE(status)
Definition: KSFoundation.h:190
KSDIXON_READTRAP trap2
Definition: ksdixon.h:171
char KS_DESCRIPTION[KS_DESCRIPTION_LENGTH]
Definition: KSFoundation.h:351
float shift1
Definition: ksdixon.h:172
Struct related to dual bandwidth readouts.Contains the support points as well as the shifts and parti...
Definition: ksdixon.h:169
float ksdixon_calc_nsa(float t1, float t2, float var1, float var2, float dephasing_period)
Calculate mean single-species weighted NSA for 2-point Dixon
Definition: ksdixon.cc:60
float G[4]
Definition: ksdixon.h:156
KSDIXON_READTRAP trap1
Definition: ksdixon.h:170
float ksdixon_calc_dephasing_period()
Period of dephasing between fat and water [usec]Uses cv cffield to determine field strength...
Definition: ksdixon.cc:46

◆ ksdixon_eval_gre_dualBW_readwave()

STATUS ksdixon_eval_gre_dualBW_readwave ( KS_READWAVE readwave1,
KS_READWAVE readwave2,
const KS_KSPACE_DESIGN kdesign,
KSDIXON_DESIGN dixon,
const int  acq_start_time,
KS_DESCRIPTION  desc 
)

Convenience wrapper that calls ksdixon_eval_gre_dBW_acq_waves and generates readwaves

Parameters
[out]readwave1Output readwave 1
[out]readwave2Output readwave 2
[in]kdesignA KS_KSPACE_DESIGN, required to calculate readout areas and nover
[out]dixonA KSDIXON_DESIGN object that gets filled with shifts TODO
acq_start_timeTime when first acquisition is turned on
[in]descDescription
Return values
STATUSSUCCESS or FAILURE
344  {
345  STATUS status;
346 
347  int nover;
348  KS_WAVE acq_wave1, acq_wave2;
349 
350  /* use amplitude of the default trap as sampling threshold (i.e. correspondning to the kdesign.rbw) */
351  /* this preserves the acq_delay, as not to corrupt calculations of shifts */
352  KS_READTRAP default_trap;
353  status = ks_eval_design_readtrap(&default_trap, kdesign, 0.0, "default");
354  KS_RAISE(status);
355  float sampling_threshold_amp = default_trap.grad.amp;
356 
357  int min_nover = (kdesign->nover[XGRAD] > 0) ? kdesign->nover[XGRAD]
358  : 0;
359 
360  status = ksdixon_eval_gre_dBW_acq_waves(&acq_wave1, &acq_wave2, &dixon->shifts[0], &dixon->shifts[1], &nover, acq_start_time, sampling_threshold_amp, min_nover, kdesign->fov[XGRAD], kdesign->res[XGRAD], desc);
361  KS_RAISE(status);
362 
363  readwave1->fov = kdesign->fov[XGRAD];
364  readwave1->res = kdesign->res[XGRAD];
365  readwave1->nover = -nover; /* first trap is a "late echo" */
366 
367  status = ks_eval_readwave(readwave1, &acq_wave1, 0.0, 0.0, 0, KS_CRUSHER_STRATEGY_FASTEST, KS_CRUSHER_STRATEGY_FASTEST);
368  KS_RAISE(status);
369 
370  readwave2->fov = kdesign->fov[XGRAD];
371  readwave2->res = kdesign->res[XGRAD];
372  readwave2->nover = nover; /* second trap is an "early echo" */
373 
374  status = ks_eval_readwave(readwave2, &acq_wave2, 0.0, 0.0, 0, KS_CRUSHER_STRATEGY_FASTEST, KS_CRUSHER_STRATEGY_FASTEST);
375  KS_RAISE(status);
376 
377  return SUCCESS;
378 
379 } /* ksdixon_eval_gre_dualBW_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
Definition: KSFoundation_host.c:1725
float fov
Definition: KSFoundation.h:1610
STATUS ks_eval_design_readtrap(KS_READTRAP *trap, const KS_KSPACE_DESIGN *kdesign, const float crusher_area, const char *desc)
Function that sets up a KS_READTRAP struct based on an KS_KSPACE_DESIGN
Definition: ksdesign.cc:119
STATUS ksdixon_eval_gre_dBW_acq_waves(KS_WAVE *wave1, KS_WAVE *wave2, int *shift1, int *shift2, int *nover, int acq_start_time, float sampling_threshold_amp, int min_nover, int fov, int res, KS_DESCRIPTION desc)
Generates two acq waves for GRE dBW using a brute-force search
Definition: ksdixon.cc:268
int nover[3]
Definition: ksdesign.h:82
Core sequence object making arbitrary waveforms on any board (using float data format)
Definition: KSFoundation.h:743
KS_TRAP grad
Definition: KSFoundation.h:1561
#define KS_RAISE(status)
Definition: KSFoundation.h:190
float amp
Definition: KSFoundation.h:669
Composite sequence object for data readout using a trapezoid gradient
Definition: KSFoundation.h:1548
Definition: KSFoundation.h:2362
int shifts[KSDIXON_MAX_POINTS]
Definition: ksdixon.h:135
int res
Definition: KSFoundation.h:1611
int res[3]
Definition: ksdesign.h:80
float fov[3]
Definition: ksdesign.h:79
int nover
Definition: KSFoundation.h:1612

◆ ksdixon_eval_asym_spline_acq_wave()

STATUS ksdixon_eval_asym_spline_acq_wave ( KS_WAVE wave,
int  shift,
int  duration,
float  area,
KS_DESCRIPTION  desc 
)

Evaluate an asymmetric spline-based acq wave according to [Rydén H, et al. Chemical shift encoding using asymmetric readout waveforms. Magn. Reson. Med. 2020;42:963]

Assumes there is not other waveform played (calls ks_syslimits_ampmax1(loggrd))

Parameters
[out]waveOutput wave
[in]shift[us] Desired CSE
[in]durationDuration of the acq wave (i.e. sampling duration)
[in]areaSample area
[in]descDescription
Return values
STATUSSUCCESS or FAILURE
384  {
385  STATUS status;
386 
387  if (shift > 0) {
388  status = ksdixon_eval_asym_spline_acq_wave(wave, -shift, duration, area, desc);
389  KS_RAISE(status);
390  return ks_eval_mirrorwave(wave);
391  } else if (shift == 0) { /* Make a flat acq wave if in-phase echo is desired */
392  float t[2] = {0, (float)duration};
393  float G[2] = {area/duration, area/duration};
394  status = ks_eval_coords2wave(wave, t, G, 2, GRAD_UPDATE_TIME, desc);
395  return status;
396  }
397 
398  if (duration % GRAD_UPDATE_TIME) {
399  return ks_error("%s: duration (%d) must fit on the gradient raster (%d)", __FUNCTION__, duration, GRAD_UPDATE_TIME);
400  }
401 
402  float base_area = area / 2; /* put half the sample area in a base rectangle */
403 
404  const int ttc = RDN_GRD(duration / 2 + shift); /* time to center (i.e. gradient echo) */
405 
406  const double g0 = base_area / duration;
407  const double T1 = ttc;
408  const double T2 = duration - T1;
409  const double M_b1 = T1/(T1+T2) * base_area;
410  const double M_b2 = base_area - M_b1;
411  const double M1 = area / 2 - M_b1;
412  const double M2 = area / 2 - M_b2;
413 
414  const double coeffs_a[4] = { /* a3 */ 4*( M1*T2*T2*T2*T2 - 2*M2*T1*T1*T1*T1 + 6*M1*T1*T1*T2*T2 - 4*M2*T1*T1*T2*T2 + 5*M1*T1*T2*T2*T2 - 8*M2*T1*T1*T1*T2) / ( T1*T1*T1*T1*T2 * (T1 + T2) * (3*T2*T2 + 2*T1*T2)),
415  /* a2 */ 12*( M2*T1*T1*T1 + 4*M2*T1*T1*T2 - 4*M1*T1*T2*T2 - M1*T2*T2*T2) / (2*T1*T1*T1*T1*T2*T2 + 3*T1*T1*T1*T2*T2*T2),
416  /* a1 */ 4*(3*M1*T2*T2*T2*T2 - M2*T1*T1*T1*T1 + 6*M1*T1*T1*T2*T2 - 6*M2*T1*T1*T2*T2 + 10*M1*T1*T2*T2*T2 - 6*M2*T1*T1*T1*T2) / ( T1*T1*T2*T2 * (2*T1 + 3*T2) * (T1+T2) ),
417  /* a0 */ g0
418  };
419  const double coeffs_b[4] = { -(3*coeffs_a[0]*T1 + coeffs_a[1]) / (3*T2), /* b3 in paper */
420  (3*coeffs_a[0]*T1 + coeffs_a[1]), /* b2 */
421  3*coeffs_a[0]*T1*T1 + 2*coeffs_a[1]*T1 + coeffs_a[2], /* b1 */
422  coeffs_a[0]*T1*T1*T1 + coeffs_a[1]*T1*T1 + coeffs_a[2]*T1 + coeffs_a[3] }; /* b0 */
423 
424  const int num_points_a = ttc / GRAD_UPDATE_TIME;
425  const int num_points_b = (duration - ttc) / GRAD_UPDATE_TIME;
426  float ta[num_points_a];
427  float tb[num_points_b];
428  int idx;
429  for (idx = 0; idx < num_points_a; idx++) {
430  ta[idx] = (idx + 0.5) * GRAD_UPDATE_TIME;
431  }
432  for (idx = 0; idx < num_points_b; idx++) {
433  tb[idx] = (idx + 0.5) * GRAD_UPDATE_TIME;
434  }
435 
436  KS_WAVEFORM waveform = KS_INIT_WAVEFORM;
437 
438  ks_polyval_f(coeffs_a, 3, ta, num_points_a, &waveform[0]);
439  ks_polyval_f(coeffs_b, 3, tb, num_points_b, &waveform[num_points_a]);
440 
441  KS_DESCRIPTION tmpdesc;
442  ks_create_suffixed_description(tmpdesc, desc, "_cs%d", shift);
443 
444  status = ks_eval_wave(wave, tmpdesc, num_points_a + num_points_b, duration, waveform);
445  KS_RAISE(status);
446 
447  if (wave->min_amp < 0) {
448  return ks_error("%s: wave undershoots", __FUNCTION__);
449  }
450 
451  ks_wave_multiplyval(wave, area / wave->area); /* normalize to account for round-off errors */
453 
454  if (wave->abs_max_amp > ks_syslimits_ampmax1(loggrd)) {
455  return ks_error("%s: wave exceeds amplitude limit", __FUNCTION__);
456  }
458  return ks_error("%s: wave exceeds slew limit", __FUNCTION__);
459  }
460 
461 
462  return SUCCESS;
463 
464 } /* ksdixon_eval_asym_spline_acq_wave() */
void ks_wave_compute_params(KS_WAVE *const wave)
Fills the members of the KS_WAVE sequence object
Definition: KSFoundation_common.c:1123
STATUS ks_eval_coords2wave(KS_WAVE *wave, float *t, float *G, int num_coords, int dwell, const char *desc) WARN_UNUSED_RESULT
Definition: KSFoundation_host.c:4383
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
STATUS ks_eval_mirrorwave(KS_WAVE *wave)
Flips the contents of the KS_WAVEFORM in a KS_WAVE object
Definition: KSFoundation_host.c:2140
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
float area
Definition: KSFoundation.h:757
void ks_polyval_f(const double *coeffs, const int order, const float *x, const int numx, float *values)
eval polynomial with float values
Definition: KSFoundation_common.c:411
#define KS_INIT_WAVEFORM
Definition: KSFoundation.h:294
float abs_max_amp
Definition: KSFoundation.h:761
LOG_GRAD loggrd
void ks_create_suffixed_description(char *const out, const char *const prefix, const char *suffix,...) __attribute__((format(printf
float ks_syslimits_ampmax1(LOG_GRAD loggrd)
Returns the maximum gradient amplitude that can be used on one gradient board at a time...
Definition: KSFoundation_common.c:249
float KS_WAVEFORM[KS_MAXWAVELEN]
Definition: KSFoundation.h:352
#define KS_RAISE(status)
Definition: KSFoundation.h:190
STATUS ksdixon_eval_asym_spline_acq_wave(KS_WAVE *wave, int shift, int duration, float area, KS_DESCRIPTION desc)
Evaluate an asymmetric spline-based acq wave according to [Rydén H, et al. Chemical shift encoding us...
Definition: ksdixon.cc:384
STATUS ks_eval_wave(KS_WAVE *wave, const char *const desc, const int res, const int duration, const KS_WAVEFORM waveform) WARN_UNUSED_RESULT
Sets up a KS_WAVE object based on a waveform (KS_WAVEFORM) in memory
Definition: KSFoundation_host.c:2029
char KS_DESCRIPTION[KS_DESCRIPTION_LENGTH]
Definition: KSFoundation.h:351
void ks_wave_multiplyval(KS_WAVE *wave, float val)
In-place scalar multiplication of a KS_WAVE
Definition: KSFoundation_common.c:1653

◆ ksdixon_eval_trip_trap_acq_points()

STATUS ksdixon_eval_trip_trap_acq_points ( float *  t,
float *  G,
int  shift,
int  duration,
float  area,
float  Mc,
float  g1,
float  s 
)

Generates support points for an asymmetric piece-wise linear waveform with three bandwidths

[FSE Dixon with bandwidth-matched asymmetric readouts tailored for real-valued fat/water estimates, ISMRM 2023]

Parameters
[out]t6 times
[out]G6 amplitudes
[in]shiftDesired CSE
[in]durationDuration (Will equal t[5] - t[0])
[in]areaRequired sample area
[in]McMid plateau area
[in]g1Mid plateau amp
[in]sslewrate
Return values
STATUSSUCCESS or FAILURE
476  {
477 
478  float Ms = (area - Mc) / 2; /* Side area (area of 1st and 3rd trap) */
479 
480  /* Trap durations s.t. duration = T1 + T2 + T3 */
481  float T2 = (Mc / g1);
482  float center = duration/2 - abs(shift); /* Gradient echo (center of 2nd plateau) */
483  float T1 = center - T2/2;
484  float T3 = duration - T2 - T1;
485 
486  float t2_1 = - ( sqrt( s*T1*T1 + 2*g1*T1 - 2*Ms) - T1 * sqrt(s) ) / sqrt(s);
487  float t2_2 = ( sqrt( s*T1*T1 + 2*g1*T1 - 2*Ms) + T1 * sqrt(s) ) / sqrt(s);
488  float t2 = t2_1 > 0 ? t2_1 : t2_2;
489 
490  float t1 = T1 - t2; /* First trap plateau */
491  float g0 = g1 + s * t2; /* First trap amp */
492 
493  float t3_1 = ( sqrt(s*T3*T3 - 2*T3*g1 + 2*Ms) + T3 * sqrt(s) ) / sqrt(s);
494  float t3_2 = - ( sqrt(s*T3*T3 - 2*T3*g1 + 2*Ms) - T3 * sqrt(s) ) / sqrt(s);
495 
496  float t3 = t3_1 > T3 ? t3_2 : t3_1; /* Third trap ramp */
497  float g2 = g1 - s*t3; /* Third trap amp */
498 
499  if (t3 < T3 &&
500  t2 < T1 &&
501  g0 > 0 &&
502  g2 > 0) {
503  if (shift <= 0) {
504  t[0] = 0; G[0] = g0;
505  t[1] = t1; G[1] = g0;
506  t[2] = T1; G[2] = g1;
507  t[3] = T1+T2; G[3] = g1;
508  t[4] = T1+T2+t3; G[4] = g2;
509  t[5] = duration; G[5] = g2;
510  } else {
511  t[5] = duration; G[5] = g0;
512  t[4] = duration - (t1); G[4] = g0;
513  t[3] = duration - (T1); G[3] = g1;
514  t[2] = duration - (T1+T2); G[2] = g1;
515  t[1] = duration - (T1+T2+t3); G[1] = g2;
516  t[0] = duration - (duration); G[0] = g2;
517  }
518  return SUCCESS;
519  } else {
520  return FAILURE;
521  }
522 
523 } /* ksdixon_eval_trip_trap_acq_points() */

◆ ksdixon_dbw_ramptime()

float ksdixon_dbw_ramptime ( const float  dur,
const float  sample_area,
const float  slewrate 
)
528  {
529  const float root = sqrt(dur*dur - 4*sample_area/slewrate);
530  if (dur < root) { /* Negative solution */
531  ks_dbg("%s, negative solution - shouldn't happen.", __FUNCTION__);
532  return ((dur + root)/2);
533  } else {
534  return ((dur - root)/2);
535  }
536 
537 } /* ksdixon_dbw_ramptime() */
STATUS STATUS ks_dbg(const char *format,...) __attribute__((format(printf
Common debug message function for HOST and TGT

◆ ksdixon_eval_dbw_acq_waves()

STATUS ksdixon_eval_dbw_acq_waves ( KS_WAVE lbw_wave,
KS_WAVE hbw_wave,
int *  lbw_cse,
int *  hbw_cse,
float *  pf,
const float  min_pf,
const int  duration,
const float  area_without_pf,
KS_DESCRIPTION  desc 
)
542  {
543  STATUS status;
544 
545  /* Brute-force optimize with free parameters nover (partial Fourier in read dir) and acq_duration of first trap */
546  float max_nsa = -1.0f; /* target for optimization */
547 
548  int min_dur1 = RUP_FACTOR((int) (duration * min_pf), GRAD_UPDATE_TIME*2 );
549  int dur_lbw;
550  int dur_hbw;
551  int opt_ramptime_lbw;
552  float opt_ramptime_lbw_float;
553  float cur_pf;
554  const float slew = 0.97*ks_syslimits_slewrate1(loggrd);
555  const float cse_at_acq_start = -duration / 2.0;
556  const float dephasing_period = ksdixon_calc_dephasing_period(); /* usec */
557  float best_time2echo_lbw = 0;
558  float best_time2echo_hbw = 0;
559  float cse[2] = {0.0, 0.0};
560 
561  float t_opt_lbw[2] = {0, 0};
562  float G_opt_lbw[2] = {0, 0};
563  float t_opt_hbw[4] = {0, 0, 0, 0};
564  float G_opt_hbw[4] = {0, 0, 0, 0};
565 
566  int counter, opt_counter;
567  for (dur_lbw = min_dur1; dur_lbw < duration; dur_lbw += GRAD_UPDATE_TIME ) {
568  for (cur_pf = min_pf; cur_pf < 1; cur_pf += 0.01, counter++) {
569  const float cur_sample_area = cur_pf * area_without_pf;
570  /* Low bandwidth trapezoid. This excludes ramp up as that will be part of the crusher evaluation later */
571  /* Ramptime if there was no raster */
572  const float ramptime_lbw_float = ksdixon_dbw_ramptime(dur_lbw, cur_sample_area, slew);
573  /* Snap ramptime to raster */
574  int ramptime_lbw = RUP_FACTOR((int) ramptime_lbw_float, 2*GRAD_UPDATE_TIME); /* Why 2 times? because it's enforced in ks_eval_crusher */
575 
576  /* This shouldn't be necessary */
577  ramptime_lbw += GRAD_UPDATE_TIME * 2;
578 
579 
580  const float amp_lbw = cur_sample_area / (dur_lbw - ramptime_lbw);
581  /* Since ramptime is now longer, it is necessary to adjust the slew rate to get the desired amp */
582  const float slew_lbw_rastered = amp_lbw / ramptime_lbw;
583  float t_lbw[4] = { 0, 0, (float)(dur_lbw - ramptime_lbw), (float)dur_lbw };
584  float G_lbw[4] = {amp_lbw, amp_lbw, amp_lbw, 0 };
585 
586  /* lbw is a late echo */
587  float time2echo_lbw;
588  status = ksdixon_dbw_time2echo_lbw(&time2echo_lbw, t_lbw, G_lbw, area_without_pf / 2.0);
589  KS_RAISE(status);
590 
591  /* High bandwidth, use the remaining duration */
592 
593  /*
594  rh
595  ____________
596  /| |
597  /<-----TOP--->|
598  /__|___________|
599  /| | |
600  / | | |
601  / | | |
602  /___|__|___________|
603  rl
604  */
605 
606  dur_hbw = duration - dur_lbw;
607  /* Subtract the bottom rectangular area, making the ramptime solution in ksdixon_dbw_ramptime applicable */
608  const float area_top = cur_sample_area - amp_lbw * (dur_hbw - ramptime_lbw);
609  const float ramptime_hbw = ramptime_lbw + ksdixon_dbw_ramptime(dur_hbw, area_top, slew_lbw_rastered);
610  if (isNaN(ramptime_hbw)) {
611  continue;
612  }
613  const float amp_hbw = ramptime_hbw * slew_lbw_rastered;
614 
615  float t_hbw[4] = { 0, (float)ramptime_lbw, (float)ramptime_hbw, (float)dur_hbw };
616  float G_hbw[4] = { 0, amp_lbw, amp_hbw, amp_hbw };
617 
618  float time2echo_hbw;
619  /* hbw is an early echo */
620  status = ksdixon_dbw_time2echo_hbw(&time2echo_hbw, t_hbw, G_hbw, area_without_pf*(cur_pf - 0.5) + ramptime_lbw * amp_lbw / 2 );
621  KS_RAISE(status);
622  if (isNaN(time2echo_hbw)) {
623  continue;
624  }
625  /* Gather chemical shift and evaluate the current gradient pair */
626  cse[0] = cse_at_acq_start + time2echo_lbw;
627  cse[1] = cse_at_acq_start + dur_lbw + time2echo_hbw;
628 
629  if (fabs(cse[0] - cse[1]) > dephasing_period) {
630  /* If this failed, higher partial Fourier fractions will also fail */
631  break;
632  }
633 
634  float nsa = ksdixon_calc_nsa(cse[0], cse[1], 1.0 / (dur_lbw - ramptime_lbw), 1.0 / (dur_hbw - ramptime_lbw), dephasing_period);
635  if (nsa > max_nsa) {
636  max_nsa = nsa;
637  opt_ramptime_lbw = ramptime_lbw;
638  opt_ramptime_lbw_float = ramptime_lbw_float;
639  t_opt_lbw[0] = 0;
640  t_opt_lbw[1] = dur_lbw - ramptime_lbw;
641  G_opt_lbw[0] = amp_lbw;
642  G_opt_lbw[1] = amp_lbw;
643  opt_counter = counter;
644  t_opt_hbw[0] = 0;
645  /* This point is necessary in order to amplitude match the two waves since ks_eval_coords2wave doesn't honor G[0] */
646  t_opt_hbw[1] = GRAD_UPDATE_TIME;
647  /* However, this alters the calculated times and area, so another correction is necessary */
648  const float area_top_adj = area_top - GRAD_UPDATE_TIME * amp_lbw;
649  const int rem_time = dur_hbw - ramptime_lbw - GRAD_UPDATE_TIME; /* On raster */
650  const float ramptime_hbw_top = ksdixon_dbw_ramptime(rem_time, area_top_adj, slew_lbw_rastered);
651  const float amp_top = area_top / (rem_time - ramptime_hbw_top / 2.0);
652  t_opt_hbw[2] = GRAD_UPDATE_TIME + ramptime_hbw_top;
653  t_opt_hbw[3] = dur_hbw - ramptime_lbw;
654  G_opt_hbw[0] = amp_lbw;
655  G_opt_hbw[1] = amp_lbw;
656  G_opt_hbw[2] = amp_lbw + amp_top;
657  G_opt_hbw[3] = amp_lbw + amp_top;
658  best_time2echo_lbw = time2echo_lbw;
659  best_time2echo_hbw = time2echo_hbw;
660  *lbw_cse = (int)cse[0];
661  *hbw_cse = (int)cse[1];
662  *pf = cur_pf;
663  status = SUCCESS;
664  }
665  } /* pf */
666  } /* dur_lbw */
667  ks_dbg("opt_counter = %d", opt_counter);
668  if (status != SUCCESS || max_nsa < 0.0f) {
669  *lbw_cse = 0;
670  *hbw_cse = 0;
671  return status;
672  }
673 
674  KS_DESCRIPTION tmpdesc_lbw;
675  ks_create_suffixed_description(tmpdesc_lbw, desc, "_lo_cs%d", *lbw_cse);
676  status = ks_eval_coords2wave(lbw_wave, t_opt_lbw, G_opt_lbw, 2, GRAD_UPDATE_TIME, tmpdesc_lbw);
677  KS_RAISE(status);
678  KS_DESCRIPTION tmpdesc_hbw;
679  ks_create_suffixed_description(tmpdesc_hbw, desc, "_hi_cs%d", *hbw_cse);
680  status = ks_eval_coords2wave(hbw_wave, t_opt_hbw, G_opt_hbw, 4, GRAD_UPDATE_TIME, tmpdesc_hbw);
681  KS_RAISE(status);
682 
683 
684  ks_dbg("opt_ramptime_lbw = %d", opt_ramptime_lbw);
685  ks_dbg("opt_ramptime_lbw_float = %.2f", opt_ramptime_lbw_float);
686  ks_dbg("inner_ramparea = %.2f", opt_ramptime_lbw * G_opt_lbw[0] /2);
687  ks_dbg("pf = %.2f", *pf);
688  ks_dbg("area mismatch lbw = %.2f", lbw_wave->area - (*pf) * area_without_pf);
689  ks_dbg("area mismatch hbw = %.2f", hbw_wave->area - (*pf) * area_without_pf);
690  ks_dbg("sample_area = %.2f", (*pf) * area_without_pf);
691  ks_dbg("lbw_wave_area = %.2f", lbw_wave->area);
692  ks_dbg("hbw_wave_area = %.2f", hbw_wave->area);
693  ks_dbg("best_time2echo_lbw = %.2f", best_time2echo_lbw);
694  ks_dbg("best_time2echo_hbw = %.2f", best_time2echo_hbw);
695  ks_dbg("cse = %d/%d", *lbw_cse, *hbw_cse);
696  int i;
697  for (i = 0; i < 2; i++) {
698  ks_dbg("t_opt_lbw[%d] = %.2f", i, t_opt_lbw[i]);
699  ks_dbg("G_opt_lbw[%d] = %.4f", i, G_opt_lbw[i]);
700  }
701 
702  for (i = 0; i < 4; i++) {
703  ks_dbg("t_opt_hbw[%d] = %.2f", i, t_opt_hbw[i]);
704  ks_dbg("G_opt_hbw[%d] = %.4f", i, G_opt_hbw[i]);
705  }
706  return SUCCESS;
707 
708 } /* ksdixon_eval_dbw_acq_waves() */
STATUS ksdixon_dbw_time2echo_lbw(float *time2echo, const float *const t, const float *const G, const float area2echo)
Definition: ksdixon.cc:138
STATUS ks_eval_coords2wave(KS_WAVE *wave, float *t, float *G, int num_coords, int dwell, const char *desc) WARN_UNUSED_RESULT
Definition: KSFoundation_host.c:4383
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
float ksdixon_dbw_ramptime(const float dur, const float sample_area, const float slewrate)
Definition: ksdixon.cc:528
float area
Definition: KSFoundation.h:757
LOG_GRAD loggrd
void ks_create_suffixed_description(char *const out, const char *const prefix, const char *suffix,...) __attribute__((format(printf
#define KS_RAISE(status)
Definition: KSFoundation.h:190
STATUS ksdixon_dbw_time2echo_hbw(float *time2echo, const float *const t, const float *const G, const float area2echo)
Definition: ksdixon.cc:119
STATUS STATUS ks_dbg(const char *format,...) __attribute__((format(printf
Common debug message function for HOST and TGT
int isNaN(float a)
Definition: KSFoundation_common.c:58
char KS_DESCRIPTION[KS_DESCRIPTION_LENGTH]
Definition: KSFoundation.h:351
float ksdixon_calc_nsa(float t1, float t2, float var1, float var2, float dephasing_period)
Calculate mean single-species weighted NSA for 2-point Dixon
Definition: ksdixon.cc:60
float ksdixon_calc_dephasing_period()
Period of dephasing between fat and water [usec]Uses cv cffield to determine field strength...
Definition: ksdixon.cc:46

◆ ksdixon_eval_flat_acq_wave()

STATUS ksdixon_eval_flat_acq_wave ( KS_WAVE wave,
int  duration,
float  area,
KS_DESCRIPTION  desc 
)

Generates an acq wave without chemical shift encoding (i.e. flat)

Parameters
[out]waveAcquisition wave
[in]durationAcq duration
[in]areaRequired sample area
[in]descDescription
Return values
STATUSSUCCESS or FAILURE
713  {
714  float t[2] = {0, (float)duration};
715  float G[2] = {area/duration, area/duration};
716  STATUS status = ks_eval_coords2wave(wave, t, G, 2, GRAD_UPDATE_TIME, desc);
717  ks_create_suffixed_description(desc, desc, "_flat");
718  return status;
719 
720 } /* ksdixon_eval_flat_acq_wave() */
STATUS ks_eval_coords2wave(KS_WAVE *wave, float *t, float *G, int num_coords, int dwell, const char *desc) WARN_UNUSED_RESULT
Definition: KSFoundation_host.c:4383
void ks_create_suffixed_description(char *const out, const char *const prefix, const char *suffix,...) __attribute__((format(printf

◆ ksdixon_eval_inverted_ramp_acq_wave()

STATUS ksdixon_eval_inverted_ramp_acq_wave ( KS_WAVE wave,
int  shift,
int  duration,
float  area,
KS_DESCRIPTION  desc 
)

ADDTITLEHERE

ADDDESCHERE

Parameters
[out]waveADDTEXTHERE
[in]shiftADDTEXTHERE
[in]durationADDTEXTHERE
[in]areaADDTEXTHERE
[in]descADDTEXTHERE
Return values
STATUSSUCCESS or FAILURE
725  {
726  STATUS status;
727  KS_DESCRIPTION tmpdesc;
728  (void) tmpdesc;
729  if (shift == 0) { /* Make a flat acq wave if in-phase echo is desired */
730  return ksdixon_eval_flat_acq_wave(wave, duration, area, desc);
731  }
732  status = SUCCESS;
733  return status;
734 
735 } /* ksdixon_eval_inverted_ramp_acq_wave() */
STATUS ksdixon_eval_flat_acq_wave(KS_WAVE *wave, int duration, float area, KS_DESCRIPTION desc)
Generates an acq wave without chemical shift encoding (i.e. flat)
Definition: ksdixon.cc:713
char KS_DESCRIPTION[KS_DESCRIPTION_LENGTH]
Definition: KSFoundation.h:351

◆ ksdixon_eval_trip_trap_acq_wave()

STATUS ksdixon_eval_trip_trap_acq_wave ( KS_WAVE wave,
int  shift,
int  duration,
float  area,
KS_DESCRIPTION  desc 
)

Wrapper to ksdixon_eval_trip_trap_acq_points() to get an acq wave (KS_WAVE) given a specific shift, duration, and area.

[FSE Dixon with bandwidth-matched asymmetric readouts tailored for real-valued fat/water estimates, ISMRM 2023]

Parameters
[out]waveAcquisition wave
[in]shiftDesired CSE
[in]durationAcq duration
[in]areaRequired sample area
[in]descDescription
Return values
STATUSSUCCESS or FAILURE
740  {
741  STATUS status;
742  KS_DESCRIPTION tmpdesc;
743  if (shift == 0) { /* Make a flat acq wave if in-phase echo is desired */
744  return ksdixon_eval_flat_acq_wave(wave, duration, area, desc);
745  }
746 
747  if (duration % GRAD_UPDATE_TIME) {
748  return ks_error("%s: duration (%d) must fit on the gradient raster (%d)", __FUNCTION__, duration, GRAD_UPDATE_TIME);
749  }
750 
751  float t[6] = {KS_NOTSET};
752  float G[6] = {KS_NOTSET};
753  float amp = area / duration;
754  float center_area = area * .25;
755  status = ksdixon_eval_trip_trap_acq_points(t, G, shift, duration, area, center_area, amp, ks_syslimits_slewrate1(loggrd));
756  KS_RAISE(status);
757  ks_create_suffixed_description(tmpdesc, desc, "_cs%d", shift);
758  status = ks_eval_coords2wave(wave, t, G, 6, GRAD_UPDATE_TIME, tmpdesc);
759 
760  return status;
761 
762 } /* ksdixon_eval_trip_trap_acq_wave() */
STATUS ks_eval_coords2wave(KS_WAVE *wave, float *t, float *G, int num_coords, int dwell, const char *desc) WARN_UNUSED_RESULT
Definition: KSFoundation_host.c:4383
#define KS_NOTSET
Definition: KSFoundation.h:115
STATUS ksdixon_eval_flat_acq_wave(KS_WAVE *wave, int duration, float area, KS_DESCRIPTION desc)
Generates an acq wave without chemical shift encoding (i.e. flat)
Definition: ksdixon.cc:713
STATUS ks_error(const char *format,...) __attribute__((format(printf
Common error message function for HOST and TGT
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
void ks_create_suffixed_description(char *const out, const char *const prefix, const char *suffix,...) __attribute__((format(printf
#define KS_RAISE(status)
Definition: KSFoundation.h:190
STATUS ksdixon_eval_trip_trap_acq_points(float *t, float *G, int shift, int duration, float area, float Mc, float g1, float s)
Generates support points for an asymmetric piece-wise linear waveform with three bandwidths[FSE Dixon...
Definition: ksdixon.cc:469
char KS_DESCRIPTION[KS_DESCRIPTION_LENGTH]
Definition: KSFoundation.h:351

◆ ksdixon_amp_limit()

float ksdixon_amp_limit ( float  board_amp_limit,
float  fov 
)

Returns the highest amplitude allowed. This can either be the board max, or restricted by the FOV.

Parameters
[in]board_amp_limit[G/cm] Max board amplitude
[in]fov[mm] FOV
Returns
float
767  {
768  float bw_amp_limit = ((float) (1.0 / (GAM * 2 * 1e-6) * (10.0 / fov)));
769  float amp_limit = board_amp_limit < bw_amp_limit ? board_amp_limit : bw_amp_limit;
770 
771  return amp_limit;
772 
773 } /* ksdixon_amp_limit() */

◆ ksdixon_eval_inverted_ramps()

STATUS ksdixon_eval_inverted_ramps ( KS_WAVE acq_waves,
int *  shifts,
int  points,
const KS_KSPACE_DESIGN kdesign,
KS_DESCRIPTION  desc 
)
778  {
779  STATUS status;
780  KS_DESCRIPTION tmpdesc;
781  /* get a resonable acq window and reference amplitude from the default trap */
782  KS_READTRAP default_trap;
783  status = ks_eval_design_readtrap(&default_trap, kdesign, 0, "default");
784  KS_RAISE(status);
785 
786  float duration = default_trap.acq.duration;
787  float sample_area = duration * default_trap.grad.amp;
788 
789  float slew = ks_syslimits_slewrate1(loggrd) * 0.95;
790  float amp_limit = ksdixon_amp_limit(ks_syslimits_ampmax1(loggrd), kdesign->fov[XGRAD]);
791 
792  int i = 0;
793  for (; i < points; i++) {
794  sprintf(tmpdesc, "%s_invr_cs%d", desc, shifts[i]);
795  if (shifts[i] == 0) {
796  /* return a trap */
797  float t[2] = {0, (float)duration};
798  float G[2] = {default_trap.grad.amp, default_trap.grad.amp};
799  status = ks_eval_coords2wave(&acq_waves[i], t, G, 2, GRAD_UPDATE_TIME, tmpdesc);
800  KS_RAISE(status);
801  } else {
802  float t[4] = {0};
803  float G[4] = {0};
804  status = ksdixon_eval_inverted_ramp_acq_points(t, G, shifts[i], duration, sample_area, slew, amp_limit);
805  KS_RAISE(status);
806  status = ks_eval_coords2wave(&acq_waves[i], t, G, 4, GRAD_UPDATE_TIME, tmpdesc);
807  KS_RAISE(status);
808  }
809  }
810  return SUCCESS;
811 
812 } /* ksdixon_eval_inverted_ramps() */
STATUS ks_eval_design_readtrap(KS_READTRAP *trap, const KS_KSPACE_DESIGN *kdesign, const float crusher_area, const char *desc)
Function that sets up a KS_READTRAP struct based on an KS_KSPACE_DESIGN
Definition: ksdesign.cc:119
STATUS ks_eval_coords2wave(KS_WAVE *wave, float *t, float *G, int num_coords, int dwell, const char *desc) WARN_UNUSED_RESULT
Definition: KSFoundation_host.c:4383
float ksdixon_amp_limit(float board_amp_limit, float fov)
Returns the highest amplitude allowed. This can either be the board max, or restricted by the FOV...
Definition: ksdixon.cc:767
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
KS_TRAP grad
Definition: KSFoundation.h:1561
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
int duration
Definition: KSFoundation.h:841
#define KS_RAISE(status)
Definition: KSFoundation.h:190
float amp
Definition: KSFoundation.h:669
Composite sequence object for data readout using a trapezoid gradient
Definition: KSFoundation.h:1548
KS_READ acq
Definition: KSFoundation.h:1549
char KS_DESCRIPTION[KS_DESCRIPTION_LENGTH]
Definition: KSFoundation.h:351
STATUS ksdixon_eval_inverted_ramp_acq_points(float *t, float *G, int shift, int duration, float area, float s, float amp_limit)
Generates support points for an inverted ramp waveform.
Definition: ksdixon.cc:817
float fov[3]
Definition: ksdesign.h:79

◆ ksdixon_eval_inverted_ramp_acq_points()

STATUS ksdixon_eval_inverted_ramp_acq_points ( float *  t,
float *  G,
int  shift,
int  duration,
float  area,
float  s,
float  amp_limit 
)

Generates support points for an inverted ramp waveform.

Parameters
[out]t4 times
[out]G4 amplitudes
[in]shiftDesired CSE
[in]durationDuration (Will equal t[3] - t[0])
[in]areaRequired sample area
[in]sslewrate
[in]amp_limitMaximum amplitude
823  {
824 
825  float plat_amp = area / (2 * fabs(shift) + duration);
826  float peak_amp = plat_amp + 2 * sqrt(plat_amp * fabs(shift) * s);
827  float ramp_amp = (peak_amp - plat_amp);
828 
829  if (peak_amp > amp_limit) {
830  peak_amp = amp_limit;
831  ramp_amp = (peak_amp - plat_amp);
832 
833  float g0 = peak_amp;
834  float g1 = area / (duration + 2*fabs(shift) );
835  float T2 = (g0-g1) / s;
836  float T1 = (area - T2*(g0 + g1)/2 - (duration - T2) * g1) / (g0-g1);
837  if (T1 < 0) {
838  return ks_error("%s: Negative initial plateau. Something is wrong.", __FUNCTION__);
839  }
840  float T3 = duration - T2 - T1;
841  if (shift < 0) {
842  G[0] = g0;
843  G[1] = g0;
844  G[2] = g1;
845  G[3] = g1;
846  t[0] = 0;
847  t[1] = T1;
848  t[2] = T1 + T2;
849  t[3] = duration;
850  } else {
851  G[0] = g1;
852  G[1] = g1;
853  G[2] = g0;
854  G[3] = g0;
855  t[0] = 0;
856  t[1] = T3;
857  t[2] = T3 + T2;
858  t[3] = duration;
859  }
860 
861  return SUCCESS;
862  }
863 
864  if ((int)(ramp_amp / s) > (int)(duration / 2 - fabs(shift))) {
865  return ks_error("Too large a shift for the slew limit!");
866  }
867 
868  if (shift > 0) {
869  G[0] = plat_amp;
870  t[0] = 0.0;
871  G[1] = plat_amp;
872  t[1] = duration - ramp_amp / s;
873  G[2] = peak_amp;
874  t[2] = duration;
875  } else if (shift == 0) {
876  G[0] = plat_amp;
877  t[0] = 0.0;
878  t[2] = plat_amp;
879  t[2] = duration;
880  G[1] = plat_amp;
881  t[1] = duration/2;
882  } else { /* negative shift */
883  G[0] = peak_amp;
884  t[0] = 0.0;
885  G[1] = plat_amp;
886  t[1] = ramp_amp / s;
887  G[2] = plat_amp;
888  t[2] = duration;
889  }
890  t[3] = t[2];
891  G[3] = G[2];
892 
893  return SUCCESS;
894 
895 } /* ksdixon_eval_inverted_ramp_acq_points() */
STATUS ks_error(const char *format,...) __attribute__((format(printf
Common error message function for HOST and TGT

◆ ksdixon_init_settings()

void ksdixon_init_settings ( KSDIXON_DESIGN *const  dixon)

Resets a KSDIXON_DESIGN to KSDIXON_INIT_DESIGN

Parameters
dixonThe KSDIXON_DESIGN to reset
908  {
909  KSDIXON_DESIGN default_settings = KSDIXON_INIT_DESIGN;
910 
911  *dixon = default_settings;
912 
913 } /* ksdixon_init_settings() */
Dixon settings
Definition: ksdixon.h:132
#define KSDIXON_INIT_DESIGN
Definition: ksdixon.h:147

◆ ksdixon_setup()

STATUS ksdixon_setup ( KSDIXON_DESIGN pdixon,
const int  opnum_dixon_mode,
_cvfloat *  _opuser_dixon_mode,
const int  opnum_dixon_tr_mode,
_cvfloat *  _opuser_dixon_tr_mode,
const int  opnum_dixon_shift,
_cvfloat *  _opuser_dixon_shift 
)

ksdixon_setup

Intended to be called using the macro KSDIXON_SETUP:

Implementation e file

// Select opuser numbers for dixon options
@global
#define OPUSER_NUM_DIXON_MODE 17
#define OPUSER_NUM_DIXON_TR_MODE 18
#define OPUSER_NUM_DIXON_SHIFT 19
ksfse2_setup_stretched():
// Fill ksfse2_dixon_settings with dixon related values.
status = KSDIXON_SETUP(&ksfse2_dixon_settings, OPUSER_NUM_DIXON_MODE, OPUSER_NUM_DIXON_TR_MODE, OPUSER_NUM_DIXON_SHIFT);
921  {
922  (void)opnum_dixon_mode;
923  (void)opnum_dixon_tr_mode;
924  (void)opnum_dixon_shift;
925 
926  STATUS status;
927  ksdixon_init_settings(pdixon);
928 
929  pdixon->mode = (KSDIXON_MODE) *_opuser_dixon_mode->addr;
930 
931  pdixon->points = 2;
932 
933  if (pdixon->mode != KSDIXON_DBW) {
934 
935  pdixon->tr_mode = (KSDIXON_TR_MODE) *_opuser_dixon_tr_mode->addr;
936  pdixon->shifts[0] = 0;
937  pdixon->shifts[1] = RUP_GRD((int) *_opuser_dixon_shift->addr);
938 
939  }
940 
941  status = ksdixon_eval_validate_settings(pdixon);
942  KS_RAISE(status);
943 
944  return SUCCESS;
945 
946 } /* ksdixon_setup() */
KSDIXON_MODE
Enums for selecting a Dixon mode. This typically changes the readout window but is not restricted to ...
Definition: ksdixon.h:98
void ksdixon_init_settings(KSDIXON_DESIGN *const dixon)
Resets a KSDIXON_DESIGN to KSDIXON_INIT_DESIGN
Definition: ksdixon.cc:908
KSDIXON_TR_MODE tr_mode
Definition: ksdixon.h:144
KSDIXON_TR_MODE
Definition: ksdixon.h:112
Definition: ksdixon.h:100
#define KS_RAISE(status)
Definition: KSFoundation.h:190
KSDIXON_MODE mode
Definition: ksdixon.h:133
int points
Definition: ksdixon.h:134
int shifts[KSDIXON_MAX_POINTS]
Definition: ksdixon.h:135
STATUS ksdixon_eval_validate_settings(const KSDIXON_DESIGN *dixon)
Validates KSDIXON_DESIGN to make sure settings are compatible. Right now it only checks if DBW is in ...
Definition: ksdixon.cc:951

◆ ksdixon_eval_validate_settings()

STATUS ksdixon_eval_validate_settings ( const KSDIXON_DESIGN dixon)

Validates KSDIXON_DESIGN to make sure settings are compatible. Right now it only checks if DBW is in single TR mode.

Parameters
[in]dixonThe settings
Return values
STATUSSUCCESS or FAILURE
951  {
952  if ((dixon->tr_mode == KSDIXON_MULTI_TR) && (dixon->mode == KSDIXON_DBW)) {
953  return ks_error("%s: Multi-TR not supported for dBW readouts", __FUNCTION__);
954  }
955  return SUCCESS;
956 
957 } /* ksdixon_eval_validate_settings() */
STATUS ks_error(const char *format,...) __attribute__((format(printf
Common error message function for HOST and TGT
KSDIXON_TR_MODE tr_mode
Definition: ksdixon.h:144
Definition: ksdixon.h:100
KSDIXON_MODE mode
Definition: ksdixon.h:133
Definition: ksdixon.h:113

◆ ksdixon_update_UI()

void ksdixon_update_UI ( const KSDIXON_MODE  dixon_mode,
const KS_KSPACE_DESIGN *const  kdesign,
const int  opnum_dixon_mode,
_cvfloat *  _opuser_dixon_mode,
const int  opnum_dixon_tr_mode,
_cvfloat *  _opuser_dixon_tr_mode,
const int  opnum_dixon_shift,
_cvfloat *  _opuser_dixon_shift 
)

ksdixon_update_UI

Intended to be called using the macro KSDIXON_UPDATE_UI:

Currently not used. The idea is to update dixon opusers according to kdesign and dixon_mode.

966  {
967  (void)_opuser_dixon_mode;
968  (void)_opuser_dixon_tr_mode;
969  (void)_opuser_dixon_shift;
970  (void)kdesign;
971 
972  switch (dixon_mode) {
973  case KSDIXON_OFF: {
974  piuset |= (1 << opnum_dixon_mode);
975  piuset &= ~( (1 << opnum_dixon_tr_mode) | (1 << opnum_dixon_shift) );
976  break;
977  }
978  case KSDIXON_SHIFTED:
979  case KSDIXON_ASYM_SPLINE:
981  case KSDIXON_TRIP_TRAP: {
982  piuset |= (1 << opnum_dixon_tr_mode);
983  piuset |= (1 << opnum_dixon_shift);
984  break;
985  }
986  case KSDIXON_DBW:
987  case KSDIXON_DBW_STAGGERED: {
988  piuset &= ~( (1 << opnum_dixon_tr_mode) | (1 << opnum_dixon_shift) );
989  break;
990  }
991  default:
992  break;
993  }
994 
995 } /* ksdixon_update_UI() */
Definition: ksdixon.h:99
int piuset
Definition: ksdixon.h:98
Definition: ksdixon.h:102
Definition: ksdixon.h:101
Definition: ksdixon.h:103
Definition: ksdixon.h:100
Definition: ksdixon.h:104

◆ ksdixon_init_UI()

void ksdixon_init_UI ( float  rbw,
int  xres,
float  fov,
const int  opnum_dixon_mode,
_cvfloat *  _opuser_dixon_mode,
const int  opnum_dixon_tr_mode,
_cvfloat *  _opuser_dixon_tr_mode,
const int  opnum_dixon_shift,
_cvfloat *  _opuser_dixon_shift 
)

See documentation for macro KSDIXON_INIT_UI.

1003  {
1004 
1005  piuset |= (1 << opnum_dixon_mode);
1006  _opuser_dixon_mode->minval = 0;
1007  _opuser_dixon_mode->maxval = KSDIXON_MODE_NUM_MODES-1;
1008  _opuser_dixon_mode->defval = KSDIXON_OFF;
1009  _opuser_dixon_mode->descr = KSDIXON_MODE_DESCR;
1010  KSDIXON_MODE mode = (KSDIXON_MODE) *_opuser_dixon_mode->addr;
1011 
1012  switch (mode) {
1013  case KSDIXON_OFF: {
1014  piuset &= ~( (1 << opnum_dixon_tr_mode) | (1 << opnum_dixon_shift) );
1015  break;
1016  }
1017  case KSDIXON_SHIFTED:
1018  case KSDIXON_ASYM_SPLINE:
1019  case KSDIXON_ASYM_INVRAMP:
1020  case KSDIXON_TRIP_TRAP: {
1021  piuset |= (1 << opnum_dixon_tr_mode);
1022  _opuser_dixon_tr_mode->minval = 0;
1023  _opuser_dixon_tr_mode->maxval = 1;
1024  _opuser_dixon_tr_mode->defval = KSDIXON_SINGLE_TR;
1025  _opuser_dixon_tr_mode->descr = KSDIXON_TR_MODE_DESCR;
1026 
1027  piuset |= (1 << opnum_dixon_shift);
1028  int tsp = ks_calc_bw2tsp(rbw);
1029  int max_shift = ksdixon_max_shift(mode, xres * tsp, xres, fov);
1030  _opuser_dixon_shift->minval = -5000;
1031  _opuser_dixon_shift->maxval = 5000;
1032  _opuser_dixon_shift->defval = 0;
1033  KS_DESCRIPTION tmpdesc;
1034  sprintf(tmpdesc,"%s (max %d)",KSDIXON_SHIFT_DESC, max_shift);
1035  _opuser_dixon_shift->descr = strdup(tmpdesc);
1036  break;
1037  }
1038  case KSDIXON_DBW:
1039  case KSDIXON_DBW_STAGGERED: {
1040  piuset &= ~( (1 << opnum_dixon_tr_mode) | (1 << opnum_dixon_shift) );
1041  break;
1042  }
1043  default:
1044  piuset &= ~(1 << opnum_dixon_tr_mode);
1045  piuset &= ~(1 << opnum_dixon_shift);
1046  break;
1047  }
1048 
1049 } /* ksdixon_init_UI() */
Definition: ksdixon.h:112
Definition: ksdixon.h:99
KSDIXON_MODE
Enums for selecting a Dixon mode. This typically changes the readout window but is not restricted to ...
Definition: ksdixon.h:98
int piuset
#define KSDIXON_MODE_DESCR
Definition: ksdixon.h:107
Definition: ksdixon.h:98
Definition: ksdixon.h:102
int ksdixon_max_shift(KSDIXON_MODE mode, int duration, float xres, float fov)
Find the max shift for for a certain Dixon mode using binary sarch. Used for get ranges in the UI...
Definition: ksdixon.cc:73
#define KSDIXON_SHIFT_DESC
Definition: ksdixon.h:119
Definition: ksdixon.h:101
Definition: ksdixon.h:103
int ks_calc_bw2tsp(float bw)
Convert receiver bandwidth to dwell time
Definition: KSFoundation_host.c:6197
Definition: ksdixon.h:100
Definition: ksdixon.h:104
#define KSDIXON_TR_MODE_DESCR
Definition: ksdixon.h:116
#define KSDIXON_MODE_NUM_MODES
Definition: ksdixon.h:108
char KS_DESCRIPTION[KS_DESCRIPTION_LENGTH]
Definition: KSFoundation.h:351

◆ ksdixon_eval_setup_gre_readouts()

STATUS ksdixon_eval_setup_gre_readouts ( KS_ECHOTRAIN echotrain,
KSDIXON_DESIGN dixon,
const KS_KSPACE_DESIGN kdesign,
const int  acq_start_time,
const int  TR_index,
KS_DESCRIPTION  desc 
)

Sets up GRE readouts. Currently only supports KSDIXON_DBW

Parameters
[out]echotrainPointer to a KS_ECHOTRAIN, which keeps track of states and what to pulsegen
[in,out]dixonDixon settings
[in]kdesignK-space deisng
[in]acq_start_timeTime when first acquisition is turned on
[in]TR_indexTR index for multi-TR support (currently not used as only DBW is supported)
[in]descDescription
Return values
STATUSSUCCESS or FAILURE
1054  {
1055  STATUS status;
1056  int readout;
1057 
1058  if (dixon->mode == KSDIXON_OFF) {
1059  return ks_error("%s should not be called for mode KSDIXON_OFF", __FUNCTION__);
1060  }
1061 
1062  switch (dixon->mode) {
1063 
1064  case KSDIXON_DBW:
1065  {
1066  if (TR_index > 0) {
1067  return ks_error("%s: Multi-TR not supported for dual BW Dixon", __FUNCTION__);
1068  }
1069  if (dixon->points != 2) {
1070  return ks_error("%s: dual BW Dixon requires exactly two points, not %d", __FUNCTION__, dixon->points);
1071  }
1072  KS_READWAVE* trap1 = &echotrain->readwaves[0];
1073  KS_READWAVE* trap2 = &echotrain->readwaves[1];
1074  status = ksdixon_eval_gre_dualBW_readwave(trap1, trap2, kdesign, dixon, acq_start_time, desc);
1075  KS_RAISE(status);
1076  for(readout = 0; readout < 2; readout++) {
1077  echotrain->controls[readout].state[0].kspace_index = readout;
1078  echotrain->controls[readout].pg.object_index = readout;
1079  echotrain->controls[readout].pg.read_type = KS_READ_WAVE;
1080  echotrain->controls[readout].pg.ampscale = (readout==0) ? 1.0f : -1.0f;
1081  }
1082  return SUCCESS;
1083  }
1084 
1085  default:
1086  return ks_error("%s: KSDIXON_MODE %d not implemented", __FUNCTION__, dixon->mode);
1087  }
1088 
1089  return SUCCESS;
1090 
1091 } /* ksdixon_eval_setup_gre_readouts() */
KS_READWAVE readwaves[KS_ECHOTRAIN_MAX_WAVES]
Definition: KSFoundation.h:2017
int object_index
Definition: KSFoundation.h:1985
Definition: ksdixon.h:98
STATUS ksdixon_eval_gre_dualBW_readwave(KS_READWAVE *readwave1, KS_READWAVE *readwave2, const KS_KSPACE_DESIGN *kdesign, KSDIXON_DESIGN *dixon, const int acq_start_time, KS_DESCRIPTION desc)
Convenience wrapper that calls ksdixon_eval_gre_dBW_acq_waves and generates readwaves
Definition: ksdixon.cc:344
float ampscale
Definition: KSFoundation.h:1987
STATUS ks_error(const char *format,...) __attribute__((format(printf
Common error message function for HOST and TGT
KS_READCONTROL_PULSEGEN pg
Definition: KSFoundation.h:2001
KS_READCONTROL_STATE state[KS_READCONTROL_MAXSTATE]
Definition: KSFoundation.h:2000
KS_READ_TYPE read_type
Definition: KSFoundation.h:1984
Definition: KSFoundation.h:1960
Definition: ksdixon.h:100
#define KS_RAISE(status)
Definition: KSFoundation.h:190
KSDIXON_MODE mode
Definition: ksdixon.h:133
int points
Definition: ksdixon.h:134
int kspace_index
Definition: KSFoundation.h:1971
Composite sequence object for data readout during a wave (1-D resampling)
Definition: KSFoundation.h:1603
KS_READCONTROL controls[KS_MAXUNIQUE_READ]
Definition: KSFoundation.h:2015

◆ ksdixon_set_design()

STATUS ksdixon_set_design ( KSDIXON_DESIGN *const  dixon_design,
const KS_KSPACE_DESIGN *const  kdesign 
)

ADDTITLEHERE

ADDDESCHERE

Parameters
dixon_designADDTEXTHERE
kdesignADDTEXTHERE
Return values
STATUSSUCCESS or FAILURE
1096  {
1097  STATUS status;
1098  /* get a resonable acq window from the default trap */
1099  KS_READTRAP default_trap;
1100  KS_KSPACE_DESIGN default_kdesign = *kdesign;
1101  status = ks_eval_design_readtrap(&default_trap, &default_kdesign, 0.0, "default");
1102  KS_RAISE(status);
1103  dixon_design->acq_duration = RUP_FACTOR(default_trap.acq.duration, 2*GRAD_UPDATE_TIME);
1104  dixon_design->sample_area = dixon_design->acq_duration * default_trap.grad.amp;
1105  dixon_design->area2center = kdesign->nover[XGRAD] == 0 ? dixon_design->sample_area / 2
1106  : dixon_design->sample_area * 1.0f/(1 + kdesign->res[XGRAD] / (2.0f * kdesign->nover[XGRAD]) ); /* Assume early echo */
1107 
1108  return SUCCESS;
1109 
1110 } /* ksdixon_set_design() */
STATUS ks_eval_design_readtrap(KS_READTRAP *trap, const KS_KSPACE_DESIGN *kdesign, const float crusher_area, const char *desc)
Function that sets up a KS_READTRAP struct based on an KS_KSPACE_DESIGN
Definition: ksdesign.cc:119
float area2center
Definition: ksdixon.h:139
int nover[3]
Definition: ksdesign.h:82
KS_TRAP grad
Definition: KSFoundation.h:1561
int duration
Definition: KSFoundation.h:841
#define KS_RAISE(status)
Definition: KSFoundation.h:190
float amp
Definition: KSFoundation.h:669
Input design for how to set up data acquistion (FOV, matrix size, parallel imaging, rBW etc)This struct is used by e.g. ks_eval_design_readtrap(), ksepi_eval_kdesign2epitrain(), ksfse_eval_numlines2acq() and is a part of all sequence generator&#39;s design structs (KSEPI_MODULE->KSEPI_DESIGN, KSFSE_MODULE->KSFSE_DESIGN, KSGRE_MODULE->KSGRE_DESIGN)
Definition: ksdesign.h:76
Composite sequence object for data readout using a trapezoid gradient
Definition: KSFoundation.h:1548
float sample_area
Definition: ksdixon.h:138
KS_READ acq
Definition: KSFoundation.h:1549
int res[3]
Definition: ksdesign.h:80
int acq_duration
Definition: ksdixon.h:136

◆ ksdixon_setup_fse_echotrain()

STATUS ksdixon_setup_fse_echotrain ( KS_ECHOTRAIN_DESIGN *const  echotrain_design,
KSDIXON_STATE *const  dixon_state,
const KSDIXON_DESIGN *const  dixon_design,
const int  ETL,
float  xcrusher_area,
const KS_DESCRIPTION  description 
)

ADDTITLEHERE

ADDDESCHERE

Parameters
echotrain_designADDTEXTHERE
dixon_stateADDTEXTHERE
dixon_designADDTEXTHERE
[in]ETLADDTEXTHERE
[in]xcrusher_areaADDTEXTHERE
[in]descriptionADDTEXTHERE
Return values
STATUSSUCCESS or FAILURE
1120  {
1121 ks_enum_crusher_strategy crusher_strategy;
1122 STATUS status;
1123 int i;
1124 int cse;
1125 int spin_echo;
1126 
1127 switch (dixon_design->mode) {
1128  case KSDIXON_OFF:
1129  return SUCCESS;
1130  case KSDIXON_SHIFTED:
1131  return SUCCESS;
1132  case KSDIXON_DBW_STAGGERED:
1133  case KSDIXON_DBW: {
1134  KS_DESCRIPTION tmpdesc;
1135  ks_create_suffixed_description(tmpdesc, description, "_dbw");
1136 
1137  /* Allocate some memory for the acq waves */
1138  ks_init_design_readwave(&echotrain_design->readwave_designs[0]);
1139  ks_init_design_readwave(&echotrain_design->readwave_designs[1]);
1140  int numstates_per_readwaves = dixon_design->mode == KSDIXON_DBW_STAGGERED ? 2 : 1;
1141  for (i = 0; i < 2; i++) {
1142  KS_WAVE* acq_ptr = (KS_WAVE*) realloc(echotrain_design->readwave_designs[i].acq_waves, sizeof(KS_WAVE) * numstates_per_readwaves);
1143  if (!acq_ptr) {
1144  return KS_THROW("Reallocation of dbw acq wave #%d failed", i);
1145  }
1146  echotrain_design->readwave_designs[i].acq_waves = acq_ptr;
1147  }
1148 
1149  float min_pfx = (dixon_design->min_pfx >= 0.5) ? dixon_design->min_pfx
1150  : 1;
1151  float pfx = 0;
1152 
1153  /* Generate dbw waves */
1154  KS_WAVE* lbw_waves = echotrain_design->readwave_designs[0].acq_waves;
1155  KS_WAVE* hbw_waves = echotrain_design->readwave_designs[1].acq_waves;
1156  ksdixon_eval_dbw_acq_waves(lbw_waves,
1157  hbw_waves,
1158  &dixon_state->shifts[0],
1159  &dixon_state->shifts[1],
1160  &pfx,
1161  min_pfx,
1162  dixon_design->acq_duration,
1163  dixon_design->sample_area,
1164  tmpdesc);
1165  int inner_ramp = (dixon_design->acq_duration - lbw_waves->duration - hbw_waves->duration) / 2;
1166  if (inner_ramp % GRAD_UPDATE_TIME) {
1167  ks_error("%s: dbw inner ramps (duration %d) are not gradient raster compatible", __FUNCTION__, inner_ramp);
1168  KS_RAISE(FAILURE);
1169  }
1170  /* Waves are centered around spin echo so pg_shifts must correct for that */
1171  int pg_shifts[2] = {(lbw_waves->duration - dixon_design->acq_duration)/2,
1172  (hbw_waves->duration - dixon_design->acq_duration)/2 + lbw_waves->duration + 2*inner_ramp};
1173  dixon_state->deadtime = 2 * inner_ramp;
1174  ks_dbg("DBW inner ramps are %d", inner_ramp);
1175 
1176  /* Setup readwave designs */
1177  echotrain_design->readwave_designs[0].flag_symmetric_padding = 0;
1178  echotrain_design->readwave_designs[0].pf = -pfx; /* Negative because it is a late echo */
1179  echotrain_design->readwave_designs[0].numstates = numstates_per_readwaves;
1180  echotrain_design->readwave_designs[0].rbw = ks_calc_tsp2bw(2);
1181 
1182  echotrain_design->readwave_designs[0].pre_crusher.area = xcrusher_area;
1184  echotrain_design->readwave_designs[0].pre_crusher.slew = 0; /* No preference */
1185  echotrain_design->readwave_designs[0].post_crusher.area = 0; /* Inner ramps */
1187  echotrain_design->readwave_designs[0].post_crusher.slew = lbw_waves->abs_max_slew; /* Inner ramps must match */
1188 
1189 
1190  echotrain_design->readwave_designs[1].flag_symmetric_padding = 0;
1191  echotrain_design->readwave_designs[1].pf = pfx;
1192  echotrain_design->readwave_designs[1].numstates = numstates_per_readwaves;
1193  echotrain_design->readwave_designs[1].rbw = ks_calc_tsp2bw(2);
1194 
1195  echotrain_design->readwave_designs[1].pre_crusher.area = 0; /* Inner ramp */
1197  echotrain_design->readwave_designs[1].pre_crusher.slew = echotrain_design->readwave_designs[0].post_crusher.slew; /* Inner ramps must match */
1198  echotrain_design->readwave_designs[1].post_crusher.area = xcrusher_area;
1200  echotrain_design->readwave_designs[1].post_crusher.slew = 0; /* No preference */
1201 
1202 
1203  /*negated state of each acq wave */
1204  /* lbw */
1205  lbw_waves[1] = lbw_waves[0];
1206  ks_waveform_multiplyval(lbw_waves[1].waveform, -1, lbw_waves[1].res);
1207 
1208  /* hbw */
1209  hbw_waves[1] = hbw_waves[0];
1210  ks_waveform_multiplyval(hbw_waves[0].waveform, -1, hbw_waves[0].res);
1211  ks_wave_compute_params(&hbw_waves[0]);
1212 
1213  /* Setup read controls */
1214  for(spin_echo=0; spin_echo < ETL; spin_echo++) {
1215  for(cse=0; cse < 2; cse++) {
1216  i = spin_echo * 2 + cse;
1217  echotrain_design->controls[i].state[0].kspace_index = cse;
1218  echotrain_design->controls[i].state[0].wave_state = 0;
1219  echotrain_design->controls[i].pg.object_index = cse;
1220  echotrain_design->controls[i].pg.read_type = KS_READ_WAVE;
1221  echotrain_design->controls[i].pg.shift = pg_shifts[cse];
1222  echotrain_design->controls[i].pg.ampscale = 1.0f; /* PG both waves positive and change their state in scan */
1223  }
1224  }
1225  break;
1226  }
1227  case KSDIXON_TRIP_TRAP:
1228  case KSDIXON_ASYM_INVRAMP:
1229  case KSDIXON_ASYM_SPLINE: {
1230  if (dixon_design->points != 2) {
1231  return ks_error("%s: KSDIXON_ASYM_SPLINE only implemented for two points (%d were prescribed)", __FUNCTION__, dixon_design->points);
1232  }
1233 
1234  ks_init_design_readwave(&echotrain_design->readwave_designs[0]);
1235 
1236  /* Generate the acq waves */
1237  for (i = 0; i < dixon_design->points; i++) {
1238  ks_init_design_readwave(&echotrain_design->readwave_designs[i]);
1239  KS_WAVE* acq_ptr = NULL;
1240  if (dixon_design->tr_mode == KSDIXON_MULTI_TR) {
1241  if (i == 0) {
1242  acq_ptr = (KS_WAVE*) realloc(echotrain_design->readwave_designs[0].acq_waves, dixon_design->points * sizeof(KS_WAVE));
1243  if (!acq_ptr) {
1244  return KS_THROW("Reallocation of %d acq waves failed", dixon_design->points);
1245  }
1246  echotrain_design->readwave_designs[0].acq_waves = acq_ptr;
1247  } else {
1248  acq_ptr = &echotrain_design->readwave_designs[0].acq_waves[i];
1249  }
1250  } else if (dixon_design->tr_mode == KSDIXON_SINGLE_TR) {
1251  acq_ptr = (KS_WAVE*) realloc(echotrain_design->readwave_designs[i].acq_waves, sizeof(KS_WAVE));
1252  if (!acq_ptr) {
1253  return KS_THROW("Reallocation of acq wave failed");
1254  }
1255  echotrain_design->readwave_designs[i].acq_waves = acq_ptr;
1256  }
1257 
1258 
1259  KS_DESCRIPTION tmpdesc;
1260 
1261  if (dixon_design->mode == KSDIXON_ASYM_SPLINE) {
1262  ks_create_suffixed_description(tmpdesc, description, "_spline%d", i);
1263  status = ksdixon_eval_asym_spline_acq_wave(acq_ptr, dixon_design->shifts[i], dixon_design->acq_duration, dixon_design->sample_area, tmpdesc);
1264  dixon_state->shifts[i] = dixon_design->shifts[i];
1265  crusher_strategy = KS_CRUSHER_STRATEGY_PLATEAU;
1266  } else if (dixon_design->mode == KSDIXON_TRIP_TRAP) {
1267  ks_create_suffixed_description(tmpdesc, description, "_pwl%d", i);
1268  status = ksdixon_eval_trip_trap_acq_wave(acq_ptr, dixon_design->shifts[i], dixon_design->acq_duration, dixon_design->sample_area, tmpdesc);
1269  dixon_state->shifts[i] = dixon_design->shifts[i];
1270  crusher_strategy = KS_CRUSHER_STRATEGY_FASTEST;
1271  } else if (dixon_design->mode == KSDIXON_ASYM_INVRAMP) {
1272  ks_create_suffixed_description(tmpdesc, description, "_invr%d", i);
1273  status = ksdixon_eval_inverted_ramp_acq_wave(acq_ptr, dixon_design->shifts[i], dixon_design->acq_duration, dixon_design->sample_area, tmpdesc);
1274  dixon_state->shifts[i] = dixon_design->shifts[i];
1275  crusher_strategy = KS_CRUSHER_STRATEGY_FASTEST;
1276  }
1277  KS_RAISE(status);
1278  }
1279 
1280  /* Setup the readwave designs and readout controls */
1281  echotrain_design->readwave_designs[0].flag_symmetric_padding = 1;
1282  echotrain_design->readwave_designs[0].pf = 1.0; /* No pf */
1283  echotrain_design->readwave_designs[0].pre_crusher.area = xcrusher_area; /* Changed later for single TR */
1284  echotrain_design->readwave_designs[0].post_crusher.area = xcrusher_area; /* Changed later for single TR */
1285  echotrain_design->readwave_designs[0].pre_crusher.strategy = crusher_strategy;
1286  echotrain_design->readwave_designs[0].post_crusher.strategy = crusher_strategy;
1287  echotrain_design->readwave_designs[0].pre_crusher.slew = 0; /* No preference */
1288  echotrain_design->readwave_designs[0].post_crusher.slew = 0; /* No preference */
1289 
1290  /* TODO: Throw if not set */
1291  echotrain_design->readwave_designs[0].pre_crusher.ampmax = dixon_design->amp_crusher;
1292  echotrain_design->readwave_designs[0].post_crusher.ampmax = dixon_design->amp_crusher;
1293  echotrain_design->readwave_designs[0].pre_crusher.slew = dixon_design->slew_crusher;
1294  echotrain_design->readwave_designs[0].post_crusher.slew = dixon_design->slew_crusher;
1295 
1296  if (dixon_design->tr_mode == KSDIXON_SINGLE_TR) {
1297  /* In single TR mode, each readwave is stored separately with a single state */
1298  for (i = 0; i < dixon_design->points; i++) {
1299  KS_WAVE* acq_ptr = echotrain_design->readwave_designs[i].acq_waves;
1300  echotrain_design->readwave_designs[i] = echotrain_design->readwave_designs[0];
1301  echotrain_design->readwave_designs[i].acq_waves = acq_ptr;
1302  echotrain_design->readwave_designs[i].numstates = 1; /* States per readwave_design */
1303  }
1304 
1305  /* It's important that crusher area matches or we get stimulated echoesin FSE. We figure out the max area out first */
1306  KS_WAVE tmp_crusher = KS_INIT_WAVE;
1307  ks_crusher_constraints crusher_constraints = echotrain_design->readwave_designs[0].pre_crusher;
1308  float max_crusher_area = xcrusher_area;
1309  for (i = 0; i < dixon_design->points; i++) {
1310  KS_WAVE* acq_ptr = echotrain_design->readwave_designs[i].acq_waves;
1311 
1312  /* Estimate coords */
1313  const float start_amp = 1.5f * acq_ptr->waveform[0] - 0.5f * acq_ptr->waveform[1];
1314  const float end_amp = 1.5f * acq_ptr->waveform[acq_ptr->res - 1] - 0.5f * acq_ptr->waveform[acq_ptr->res - 2];
1315 
1316  ks_eval_crusher(&tmp_crusher, start_amp, crusher_constraints);
1317  if (tmp_crusher.area > max_crusher_area) {
1318  max_crusher_area = tmp_crusher.area;
1319  }
1320 
1321  ks_eval_crusher(&tmp_crusher, end_amp, crusher_constraints);
1322  if (tmp_crusher.area > max_crusher_area) {
1323  max_crusher_area = tmp_crusher.area;
1324  }
1325  }
1326 
1327  echotrain_design->readwave_designs[0].pre_crusher.area = max_crusher_area;
1328  echotrain_design->readwave_designs[0].post_crusher.area = max_crusher_area;
1329 
1330 
1331  for(spin_echo=0; spin_echo < ETL; spin_echo++) {
1332  cse = spin_echo % dixon_design->points;
1333  echotrain_design->controls[spin_echo].state[0].kspace_index = cse; /* CSEs should be stored in separate k-spaces */
1334  echotrain_design->controls[spin_echo].state[0].wave_state = 0;
1335  echotrain_design->controls[spin_echo].pg.object_index = cse; /* So many readwaves */
1336  echotrain_design->controls[spin_echo].pg.read_type = KS_READ_WAVE;
1337  echotrain_design->controls[spin_echo].pg.ampscale = 1;
1338  }
1339  } else if (dixon_design->tr_mode == KSDIXON_MULTI_TR) {
1340  /* There is only one readwave with multiple states in multi TR mode */
1341  echotrain_design->readwave_designs[0].numstates = dixon_design->points;
1342 
1343  /* map cse to wavestate and kspace_index */
1344  for(spin_echo=0; spin_echo < ETL; spin_echo++) { /* readout loop */
1345  echotrain_design->controls[spin_echo].pg.read_type = KS_READ_WAVE;
1346  echotrain_design->controls[spin_echo].pg.ampscale = 1;
1347  echotrain_design->controls[spin_echo].pg.object_index = 0; /* As mentioned above, there is only one readwave in this case */
1348  for (cse = 0; cse < dixon_design->points; cse++) { /* state loop */
1349  echotrain_design->controls[spin_echo].state[cse].wave_state = cse;
1350  echotrain_design->controls[spin_echo].state[cse].kspace_index = cse;
1351  }
1352  }
1353  }
1354  break;
1355  }
1356  default:
1357  return KS_THROW("Dixon mode %d has not yet been implemented with the new latest and greatest rewrite.", dixon_design->mode);
1358  break;
1359  }
1360 
1361  return SUCCESS;
1362 }
float slew
Definition: KSFoundation.h:2372
Definition: ksdixon.h:112
Definition: ksdixon.h:99
int shifts[KSDIXON_MAX_POINTS]
Definition: ksdixon.h:181
void ks_wave_compute_params(KS_WAVE *const wave)
Fills the members of the KS_WAVE sequence object
Definition: KSFoundation_common.c:1123
float abs_max_slew
Definition: KSFoundation.h:760
STATUS ksdixon_eval_trip_trap_acq_wave(KS_WAVE *wave, int shift, int duration, float area, KS_DESCRIPTION desc)
Wrapper to ksdixon_eval_trip_trap_acq_points() to get an acq wave (KS_WAVE) given a specific shift...
Definition: ksdixon.cc:740
KS_READWAVE_DESIGN readwave_designs[KS_ECHOTRAIN_MAX_WAVES]
Definition: ksdesign.h:116
int object_index
Definition: KSFoundation.h:1985
Definition: KSFoundation.h:2369
Core sequence object making arbitrary waveforms on any board (using float data format)
Definition: KSFoundation.h:743
Definition: ksdixon.h:98
#define KS_INIT_WAVE
Definition: KSFoundation.h:295
float ampscale
Definition: KSFoundation.h:1987
STATUS ks_error(const char *format,...) __attribute__((format(printf
Common error message function for HOST and TGT
int numstates
Definition: ksdesign.h:105
KS_READCONTROL controls[KS_MAXUNIQUE_READ]
Definition: ksdesign.h:115
float slew_crusher
Definition: ksdixon.h:141
ks_crusher_constraints pre_crusher
Definition: ksdesign.h:109
Definition: ksdixon.h:102
STATUS ksdixon_eval_inverted_ramp_acq_wave(KS_WAVE *wave, int shift, int duration, float area, KS_DESCRIPTION desc)
ADDTITLEHERE
Definition: ksdixon.cc:725
KS_READCONTROL_PULSEGEN pg
Definition: KSFoundation.h:2001
float area
Definition: KSFoundation.h:757
float amp_crusher
Definition: ksdixon.h:143
KS_READCONTROL_STATE state[KS_READCONTROL_MAXSTATE]
Definition: KSFoundation.h:2000
ks_enum_crusher_strategy
Definition: KSFoundation.h:2361
KSDIXON_TR_MODE tr_mode
Definition: ksdixon.h:144
int wave_state
Definition: KSFoundation.h:1972
float ks_calc_tsp2bw(int tsp)
Convert dwell time to receiver bandwidth
Definition: KSFoundation_host.c:6212
Definition: ksdixon.h:101
KS_READ_TYPE read_type
Definition: KSFoundation.h:1984
Definition: KSFoundation.h:1960
float ampmax
Definition: KSFoundation.h:2371
STATUS ksdixon_eval_dbw_acq_waves(KS_WAVE *lbw_wave, KS_WAVE *hbw_wave, int *lbw_cse, int *hbw_cse, float *pf, const float min_pf, const int duration, const float area_without_pf, KS_DESCRIPTION desc)
Definition: ksdixon.cc:542
int deadtime
Definition: ksdixon.h:182
void ks_create_suffixed_description(char *const out, const char *const prefix, const char *suffix,...) __attribute__((format(printf
float pf
Definition: ksdesign.h:107
Definition: ksdixon.h:103
float rbw
Definition: ksdesign.h:108
KS_WAVE * acq_waves
Definition: ksdesign.h:104
Definition: ksdixon.h:100
ks_enum_crusher_strategy strategy
Definition: KSFoundation.h:2375
#define KS_RAISE(status)
Definition: KSFoundation.h:190
KSDIXON_MODE mode
Definition: ksdixon.h:133
STATUS ksdixon_eval_asym_spline_acq_wave(KS_WAVE *wave, int shift, int duration, float area, KS_DESCRIPTION desc)
Evaluate an asymmetric spline-based acq wave according to [Rydén H, et al. Chemical shift encoding us...
Definition: ksdixon.cc:384
Definition: ksdixon.h:104
int points
Definition: ksdixon.h:134
Definition: KSFoundation.h:2366
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 kspace_index
Definition: KSFoundation.h:1971
Definition: KSFoundation.h:2362
float min_pfx
Definition: ksdixon.h:137
float area
Definition: KSFoundation.h:2370
Definition: ksdixon.h:113
int flag_symmetric_padding
Definition: ksdesign.h:106
float sample_area
Definition: ksdixon.h:138
STATUS STATUS ks_dbg(const char *format,...) __attribute__((format(printf
Common debug message function for HOST and TGT
int shifts[KSDIXON_MAX_POINTS]
Definition: ksdixon.h:135
ks_crusher_constraints post_crusher
Definition: ksdesign.h:110
char KS_DESCRIPTION[KS_DESCRIPTION_LENGTH]
Definition: KSFoundation.h:351
#define KS_THROW(format,...)
Definition: KSFoundation.h:181
int shift
Definition: KSFoundation.h:1986
void ks_init_design_readwave(KS_READWAVE_DESIGN *readwave_design)
ADDTITLEHERE
Definition: ksdesign.cc:43
int res
Definition: KSFoundation.h:746
KS_WAVEFORM waveform
Definition: KSFoundation.h:748
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 duration
Definition: KSFoundation.h:747
int acq_duration
Definition: ksdixon.h:136

◆ ksdixon_eval_trip_traps()

STATUS ksdixon_eval_trip_traps ( KS_WAVE acq_waves,
int *  shifts,
int  points,
const KS_KSPACE_DESIGN kdesign,
KS_DESCRIPTION  desc 
)

Generates points readwaves, each has PWL3 acq waveform and trapezoidal crushers

Parameters
[out]acq_wavesOutput readwaves. Array with points elements must be allocated before
[in]shifts[us] Array of CSE
[in]pointsNumber of readwaves to generate
[in]kdesignA KS_KSPACE_DESIGN, required to calculate sample area and acq duration
[in]descDescription
Return values
STATUSSUCCESS or FAILURE

Variable Documentation

◆ piuset

int piuset