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

Macros

#define ABORT_ON_KSERROR   0
 

Functions

STATUS acqq_longdab (WF_PULSE_ADDR pulse, LONG pos_ref, LONG dab_ref, LONG xtr_ref, LONG fslot_value)
 
int isNaN (float a)
 
STATUS ks_error (const char *format,...)
 
void ks_dbg_reset ()
 
STATUS ks_dbg (const char *format,...)
 
STATUS existfile (const char *fname)
 
int ks_syslimits_hasICEhardware ()
 
float ks_syslimits_ampmax_phys ()
 
float ks_syslimits_ampmax (LOG_GRAD loggrd)
 
float ks_syslimits_ampmax2 (LOG_GRAD loggrd)
 
float ks_syslimits_ampmax1 (LOG_GRAD loggrd)
 
float ks_syslimits_ampmax1p (LOG_GRAD loggrd)
 
float ks_syslimits_gradtarget (LOG_GRAD loggrd, int board)
 
int ks_syslimits_ramptimemax (LOG_GRAD loggrd)
 
int ks_syslimits_ramptimemax_phys ()
 
float ks_syslimits_slewrate_phys ()
 
float ks_syslimits_slewrate (LOG_GRAD loggrd)
 
float ks_syslimits_slewrate2 (LOG_GRAD loggrd)
 
float ks_syslimits_slewrate1 (LOG_GRAD loggrd)
 
float ks_syslimits_slewrate1p (LOG_GRAD loggrd)
 
short ks_phase_radians2hw (float phase)
 
short ks_phase_degrees2hw (float phase)
 
void ks_polyval (const double *coeffs, const int order, const float x[], const int numx, float values[])
 
float ks_calc_selgradamp (float rfbw, float slthick)
 
float ks_calc_minfov (float ampmax, int tsp)
 
float ks_calc_minslthick (float bw)
 
int ks_calc_mintsp (float ampmax, float fov)
 
float ks_calc_fov2gradareapixel (float fov)
 
void ks_phaseencoding_init_tgt (KS_PHASEENCODING_PLAN *phaseenc_plan_ptr)
 
KS_PHASEENCODING_COORD ks_phaseencoding_get (const KS_PHASEENCODING_PLAN *phaseenc_plan_ptr, int echo, int shot)
 
void ks_phaseencoding_set (KS_PHASEENCODING_PLAN *phaseenc_plan_ptr, int echo, int shot, int ky, int kz)
 
void ks_phaseencoding_print (const KS_PHASEENCODING_PLAN *phaseenc_plan_ptr)
 
STATUS ks_phaseencoding_resize (KS_PHASEENCODING_PLAN *phaseenc_plan_ptr, int etl, int num_shots)
 
STATUS ks_phaseencoding_generate_simple (KS_PHASEENCODING_PLAN *phaseenc_plan_ptr, const char *const desc, KS_PHASER *phaser, KS_PHASER *zphaser)
 
STATUS ks_phaseencoding_generate_simple_ellipse (KS_PHASEENCODING_PLAN *phaseenc_plan_ptr, const char *const desc, KS_PHASER *phaser, KS_PHASER *zphaser)
 
STATUS ks_fse_calcecho (double *bestecho, double *optecho, int *nacqlines_to_kspacecenter, KS_PHASER *pe, ks_enum_pf_earlylate_te pf_direction, int TE, int etl, int esp)
 
STATUS ks_phaseencoding_generate_2Dfse (KS_PHASEENCODING_PLAN *phaseenc_plan_ptr, const char *const desc, KS_PHASER *phaser, KS_PHASER *zphaser, ks_enum_pf_earlylate_te pf_direction, int TE, int etl, int esp)
 
STATUS ks_phaseencoding_generate_epi (KS_PHASEENCODING_PLAN *phaseenc_plan_ptr, const char *const desc, const KS_EPI *epitrain, const ks_enum_epiblipsign blipsign, const ks_enum_pf_earlylate_te pf_direction, const int numileavestoacq, ks_enum_sweep_order sweep_order, int numsegments, const int caipi_delta)
 
STATUS ks_phaseencoding_generate_allepiplans (KS_PHASEENCODING_ALLEPIPLANS phaseenc_plans, const char *const desc, const KS_EPI *epitrain, const int caipi_delta)
 
STATUS ks_phaseencoding_allepiplans_setechoes (KS_PHASEENCODING_PLAN_16PTRS peplan_echoes, KS_PHASEENCODING_ALLEPIPLANS allepiplans, int nechoes, int multishot_flag, int min1stTE, int mirrorTE, ks_enum_epiblipsign blipsign)
 
int ks_wave_res (const KS_WAVE *wave)
 
float ks_waveform_max (const KS_WAVEFORM waveform, int res)
 
float ks_wave_max (const KS_WAVE *wave)
 
float ks_waveform_min (const KS_WAVEFORM waveform, int res)
 
float ks_wave_min (const KS_WAVE *wave)
 
short ks_iwave_absmax (const KS_IWAVE waveform, int res)
 
float ks_waveform_absmax (const KS_WAVEFORM waveform, int res)
 
float ks_wave_absmax (const KS_WAVE *wave)
 
float ks_waveform_maxslew (const KS_WAVEFORM waveform, int res, int duration)
 
float ks_wave_maxslew (const KS_WAVE *wave)
 
float ks_waveform_area (const KS_WAVEFORM waveform, int start, int end, int dwelltime)
 
float ks_wave_area (const KS_WAVE *wave, int start, int end)
 
float ks_waveform_sum (const KS_WAVEFORM waveform, int res)
 
float ks_wave_sum (const KS_WAVE *wave)
 
float ks_waveform_norm (const KS_WAVEFORM waveform, int res)
 
float ks_wave_norm (const KS_WAVE *wave)
 
void ks_waveform_cumsum (KS_WAVEFORM cumsumwaveform, const KS_WAVEFORM waveform, int res)
 
void ks_wave_cumsum (KS_WAVE *cumsumwave, const KS_WAVE *wave)
 
void ks_waveform_multiply (KS_WAVEFORM waveform_mod, const KS_WAVEFORM waveform, int res)
 
void ks_wave_multiply (KS_WAVE *wave_mod, const KS_WAVE *wave)
 
void ks_waveform_add (KS_WAVEFORM waveform_mod, const KS_WAVEFORM waveform, int res)
 
void ks_wave_add (KS_WAVE *wave_mod, const KS_WAVE *wave)
 
void ks_waveform_multiplyval (KS_WAVEFORM waveform, float val, int res)
 
void ks_wave_multiplyval (KS_WAVE *wave, float val)
 
void ks_waveform_addval (KS_WAVEFORM waveform, float val, int res)
 
void ks_wave_addval (KS_WAVE *wave, float val)
 
STATUS ks_waveform2iwave (KS_IWAVE iwave, const KS_WAVEFORM waveform, int res, int board)
 
STATUS ks_wave2iwave (KS_IWAVE iwave, const KS_WAVE *wave, int board)
 
WF_PULSE * ks_pg_echossp (WF_PULSE *echo, const char *suffix)
 
STATUS ks_pg_trap (KS_TRAP *trap, KS_SEQLOC loc, KS_SEQ_CONTROL *ctrl)
 
STATUS ks_pg_phaser (KS_PHASER *phaser, KS_SEQLOC loc, KS_SEQ_CONTROL *ctrl)
 
STATUS ks_pg_read (KS_READ *read, int pos, KS_SEQ_CONTROL *ctrl)
 
STATUS ks_pg_readtrap (KS_READTRAP *readtrap, KS_SEQLOC loc, KS_SEQ_CONTROL *ctrl)
 
STATUS ks_pg_wave (KS_WAVE *wave, KS_SEQLOC loc, KS_SEQ_CONTROL *ctrl)
 
STATUS ks_pg_rf (KS_RF *rf, KS_SEQLOC loc, KS_SEQ_CONTROL *ctrl)
 
STATUS ks_pg_selrf (KS_SELRF *selrf, KS_SEQLOC loc, KS_SEQ_CONTROL *ctrl)
 
STATUS ks_pg_wait (KS_WAIT *wait, KS_SEQLOC loc, KS_SEQ_CONTROL *ctrl)
 
STATUS ks_pg_isirot (KS_ISIROT *isirot, SCAN_INFO scan_info, int pos, void(*rotfun)(), KS_SEQ_CONTROL *ctrl)
 
STATUS ks_pg_epi_dephasers (KS_EPI *epi, KS_SEQLOC loc, KS_SEQ_CONTROL *ctrl)
 
STATUS ks_pg_epi_rephasers (KS_EPI *epi, KS_SEQLOC loc, KS_SEQ_CONTROL *ctrl)
 
STATUS ks_pg_epi_echo (KS_EPI *epi, KS_SEQLOC loc, KS_SEQ_CONTROL *ctrl)
 
STATUS ks_pg_epi (KS_EPI *epi, KS_SEQLOC loc, KS_SEQ_CONTROL *ctrl)
 
void ks_pg_mod_fse_rfpulse_structs (KS_SELRF *rf1, KS_SELRF *rf2, KS_SELRF *rf3, const double *flip_angles, const int etl)
 
STATUS ks_pg_fse_flip_angle_taperoff (double *flip_angles, int etl, double flip1, double flip2, double flip3, double target_flip, int start_middle)
 
void ks_instancereset_trap (KS_TRAP *trap)
 
void ks_instancereset_wave (KS_WAVE *wave)
 
void ks_instancereset_wait (KS_WAIT *wait)
 
void ks_instancereset_phaser (KS_PHASER *phaser)
 
void ks_instancereset_readtrap (KS_READTRAP *read)
 
void ks_instancereset_rf (KS_RF *rf)
 
void ks_instancereset_selrf (KS_SELRF *selrf)
 
void ks_instancereset_epi (KS_EPI *epi)
 
void ks_mat4_zero (KS_MAT4x4 m)
 
void ks_mat4_identity (KS_MAT4x4 m)
 
void ks_mat4_print (const KS_MAT4x4 m)
 
void ks_mat4_multiply (KS_MAT4x4 lhs, const KS_MAT4x4 rhs_left, const KS_MAT4x4 rhs_right)
 
void ks_mat4_invert (KS_MAT4x4 lhs, const KS_MAT4x4 rhs)
 
void ks_mat4_setgeometry (KS_MAT4x4 lhs, float x, float y, float z, float xr, float yr, float zr)
 
void ks_mat4_setrotation1axis (KS_MAT4x4 rhs, float rot, char axis)
 
void ks_mat4_extractrotation (KS_MAT3x3 R, const KS_MAT4x4 M)
 
void ks_mat4_extracttranslation (double *T, const KS_MAT4x4 M)
 
void ks_mat3_identity (KS_MAT3x3 m)
 
void ks_mat3_multiply (KS_MAT3x3 lhs, const KS_MAT3x3 rhs_left, const KS_MAT3x3 rhs_right)
 
void ks_mat3_print (const KS_MAT3x3 m)
 
void ks_mat3_apply (double *w, const KS_MAT3x3 R, const double *v)
 
void ks_mat3_invapply (double *w, const KS_MAT3x3 R, const double *v)
 
void ks_scan_update_slice_location (SCAN_INFO *new_loc, const SCAN_INFO orig_loc, const KS_MAT4x4 M_physical, const KS_MAT4x4 M_logical)
 
int ks_compare_wfi_by_timeboard (const KS_WFINSTANCE *a, const KS_WFINSTANCE *b)
 
int ks_compare_wfi_by_boardtime (const KS_WFINSTANCE *a, const KS_WFINSTANCE *b)
 
int ks_compare_wfp_by_time (const WF_PULSE *a, const WF_PULSE *b)
 
void ks_sort_wfi_by_timeboard (KS_WFINSTANCE *a, int nitems)
 
void ks_sort_wfi_by_boardtime (KS_WFINSTANCE *a, int nitems)
 
void ks_sort_wfp_by_time (WF_PULSE *a, int nitems)
 
int ks_compare_pshort (const void *v1, const void *v2)
 
int ks_compare_pint (const void *v1, const void *v2)
 
int ks_compare_pfloat (const void *v1, const void *v2)
 
int ks_compare_short (const void *v1, const void *v2)
 
int ks_compare_int (const void *v1, const void *v2)
 
int ks_compare_float (const void *v1, const void *v2)
 
void ks_sort_getsortedindx (int *sortedindx, int *array, int n)
 
void ks_sort_getsortedindx_s (int *sortedindx, short *array, int n)
 
void ks_sort_getsortedindx_f (int *sortedindx, float *array, int n)
 
void ks_plot_slicetime_begin ()
 
void ks_plot_slicetime (KS_SEQ_CONTROL *ctrl, int nslices, float *slicepos_mm, float slthick_mm, KS_PLOT_EXCITATION_MODE excmode)
 
void ks_plot_slicetime_endofpass (KS_PLOT_PASS_MODE pass_mode)
 
void ks_plot_slicetime_endofslicegroup_tagged (const char *desc, const KS_PLOT_SLICEGROUP_MODE tag)
 
void ks_plot_slicetime_endofslicegroup (const char *desc)
 
void ks_plot_slicetime_end ()
 
void ks_plot_tgt_reset (KS_SEQ_CONTROL *ctrl)
 
void ks_plot_tgt_addframe (KS_SEQ_CONTROL *ctrl)
 
STATUS ks_eval_addtraptogradrfctrl (KS_GRADRFCTRL *gradrfctrl, KS_TRAP *trap)
 
STATUS ks_eval_addwavetogradrfctrl (KS_GRADRFCTRL *gradrfctrl, KS_WAVE *wave)
 
STATUS ks_eval_addrftogradrfctrl (KS_GRADRFCTRL *gradrfctrl, KS_RF *rf)
 
STATUS ks_eval_addreadtogradrfctrl (KS_GRADRFCTRL *gradrfctrl, KS_READ *read)
 
float ks_scan_rf_phase_spoiling (int counter)
 

Variables

int maxGradRes
 
int cfswgut
 
int cfswrfut
 
int psd_rf_wait
 
int psd_grd_wait
 
float GAM
 
int cfssctype
 
float opfov
 
int opxres
 
int opyres
 
float opslthick
 
int rhkacq_uid
 
int ks_plot_filefmt
 
char ks_psdname [256]
 
int abort_on_kserror = ABORT_ON_KSERROR
 
int ks_rhoboard = TYPRHO1
 

Detailed Description

This file contains functions accessible on both HOST and TGT.

Macro Definition Documentation

◆ ABORT_ON_KSERROR

#define ABORT_ON_KSERROR   0

Function Documentation

◆ acqq_longdab()

STATUS acqq_longdab ( WF_PULSE_ADDR  pulse,
LONG  pos_ref,
LONG  dab_ref,
LONG  xtr_ref,
LONG  fslot_value 
)
1511 {
1512  static const CHAR *routine = "acqq_longdab";
1513  CHAR *basename = NULL;
1514  INT start; /* Start of the pulse */
1515  WF_PULSE_ADDR newpulse; /* Pointer to new pulse */
1516  INT pack_ix = 0; /* CERD change */
1517  INT LONG_DAB_length = 31; /* Maximum is 31, hardware limitation */
1518  SHORT LONG_DAB_bits[31]; /* Maximum is 31, hardware limitation */
1519 #ifdef PGEN_TIMING
1520  extern INT time_pgen;
1521  if (time_pgen == 1) {
1522  start_timer(&_PGEN_start_time);
1523  }
1524 #endif
1525 
1526  pack_ix = PSD_XCVR2;
1527 
1528 #ifdef SIM_IO
1529  if (psdtrace3)
1530  {
1531  CHAR tmpname[50];
1532  if (pulse->pulsename == NULL)
1533  strcpy(tmpname,"");
1534  else
1535  strcpy(tmpname,pulse->pulsename);
1536 
1537  fprintf(stderr,"%s pulse: %s posref %d xtrref %d dabref %d fslot %d\n",
1538  routine,
1539  tmpname,
1540  pos_ref,
1541  xtr_ref,
1542  dab_ref,
1543  fslot_value);
1544  fflush(stderr);
1545  }
1546 #endif
1547 
1548  LONG_DAB_bits[0] = SSPDS + DABDC; /* CAPM , CERD change: new opcode */
1549  LONG_DAB_bits[1] = SSPOC + DXCT; /* Excitation , CERD change: ew opcode */
1550  LONG_DAB_bits[2] = SSPD;
1551  LONG_DAB_bits[3] = SSPD;
1552  LONG_DAB_bits[4] = SSPD;
1553  LONG_DAB_bits[5] = SSPD;
1554  LONG_DAB_bits[6] = SSPD;
1555  LONG_DAB_bits[7] = SSPD + DABIS; /* if DABIS is really needed is unclear, we leave it for the moment */
1556  LONG_DAB_bits[8] = SSPD;
1557  LONG_DAB_bits[9] = SSPD;
1558  LONG_DAB_bits[10] = SSPD;
1559  LONG_DAB_bits[11] = SSPD;
1560  LONG_DAB_bits[12] = SSPD;
1561  LONG_DAB_bits[13] = SSPD;
1562  LONG_DAB_bits[14] = SSPD;
1563  LONG_DAB_bits[15] = SSPD;
1564  LONG_DAB_bits[16] = SSPD;
1565  LONG_DAB_bits[17] = SSPD;
1566  LONG_DAB_bits[18] = SSPD;
1567  LONG_DAB_bits[19] = SSPD;
1568  LONG_DAB_bits[20] = SSPD;
1569  LONG_DAB_bits[21] = SSPD;
1570  LONG_DAB_bits[22] = SSPD;
1571  LONG_DAB_bits[23] = SSPD;
1572  LONG_DAB_bits[24] = SSPD;
1573  LONG_DAB_bits[25] = SSPD;
1574  LONG_DAB_bits[26] = SSPD;
1575  LONG_DAB_bits[27] = SSPD;
1576  LONG_DAB_bits[28] = SSPD;
1577  LONG_DAB_bits[29] = SSPD;
1578  LONG_DAB_bits[30] = SSPDS; /* Last field must be SSPDS, max number of fields = 31 */
1579 
1580  /* Make sure packets are ordered properly (dab->xtr->rba) and that
1581  the dab packet doesn't collide with xtr packet. */
1582  if ((dab_ref != DEFAULTPOS) && (xtr_ref != DEFAULTPOS) &&
1583  (dab_ref + LONG_DAB_length > xtr_ref))
1584  MsgHndlr(routine,
1585  MSG_PULSE, pulse,
1586  MSG_FORMAT,
1587  CODING_PBM,
1588  EMT_INVLD_INSTR_START,
1589  dab_ref);
1590 
1591  /* Make sure there is enough room for xtr packet */
1592  if ((xtr_ref != DEFAULTPOS) &&
1593  (pos_ref - xtr_ref < (XTR_length[pack_ix] + RBA_length[pack_ix])))
1594  MsgHndlr(routine,
1595  MSG_PULSE, pulse,
1596  MSG_FORMAT,
1597  CODING_PBM,
1598  EMT_INVLD_INSTR_START,
1599  xtr_ref);
1600 
1601  if ((xtr_ref == DEFAULTPOS) && (RBA_length[pack_ix] > XTRSETLNG))
1602  MsgHndlr(routine,
1603  MSG_PULSE, pulse,
1604  MSG_FORMAT,
1605  CODING_PBM,
1606  EMT_INVLD_INSTR_START,
1607  pos_ref);
1608 
1609  /* This routine assumes that the user has created the
1610  * the pulse passed into this routine with the pre-processor
1611  */
1612 
1613  /***** The bits will be created first ****/
1614  /* The first thing this routine will do will be to verify if
1615  * any bits have been created or no.
1616  */
1617 
1618  if (pulse-> ninsts == 0)
1619  {
1620 
1621  /***************************************/
1622  /**** Create DAB 'bits' Pulse **********/
1623  /***************************************/
1624 
1625  if (!(basename = (CHAR *)AllocNode(strlen(pulse-> pulsename)+1)))
1626  {
1627  MsgHndlr(routine, MSG_FORMAT, HARDWARE_PBM,
1628  EMT_ALLOC, pulse-> pulsename);
1629  }
1630  strcpy(basename, pulse-> pulsename);
1631 
1632  char* dabname = (char *)AllocNode(strlen(basename)+4);
1633  /* Create Name For Pulse */
1634  if (!dabname)
1635  {
1636  MsgHndlr(routine, MSG_PULSE, pulse,
1637  MSG_FORMAT, HARDWARE_PBM,
1638  EMT_ALLOC, "DAB Bits Pulse Name");
1639  }
1640 
1641  /* Append "dab" suffix for loaddab... routines */
1642  sprintf(dabname,"%sdab",basename);
1643  pulse-> pulsename = dabname;
1644 
1645  createbits(pulse, (WF_PROCESSOR)TYPSSP,
1646  LONG_DAB_length,
1647  LONG_DAB_bits);
1648 
1649  /***************************************/
1650  /**** Create XTR 'bits' Pulse **********/
1651  /***************************************/
1652 
1653  if (!(newpulse = (WF_PULSE *)AllocNode(sizeof(WF_PULSE))))
1654  {
1655 
1656  MsgHndlr(routine, MSG_FORMAT,
1657  HARDWARE_PBM, EMT_ALLOC, "Receive Bits");
1658 
1659  }
1660 
1661  char* xtrname = (char *)AllocNode(strlen(basename)+4);
1662  /* Create Name For Pulse */
1663  if (!xtrname)
1664  {
1665  MsgHndlr(routine, MSG_PULSE, pulse,
1666  MSG_FORMAT,
1667  HARDWARE_PBM,
1668  EMT_ALLOC,
1669  "Xtr Bits Pulse Name");
1670  }
1671 
1672  /* Attach to dab pulse */
1673  pulse-> assoc_pulse = newpulse;
1674 
1675  /* Append xtr for use by other routines */
1676  sprintf(xtrname,"%sxtr",basename);
1677  newpulse-> pulsename = xtrname;
1678 
1679  /* Verify the parameters for the pulse */
1680 
1681  /* Begin LxMGD - RJF 12/6/00 */
1682 
1683  /* For MGD, there is a maximum of 8 filters with slot numbers 0 thru 7.
1684  The filter block is terminated with a slot number of -1.
1685  So the valid filter slots are from 0 to 7 - RJF */
1686 
1687  if ((fslot_value < MIN_FILTER_SLOT) || (fslot_value > MAX_FILTER_SLOT) )
1688  {
1689  MsgHndlr(routine,
1690  MSG_PULSE, newpulse,
1691  MSG_FORMAT,
1692  CODING_PBM,
1693  EMT_DAB_SLOT_NO,
1694  fslot_value);
1695  }
1696 
1697  /* Begin LxMGD - RJF, 12/6/00 */
1698  /* The way XTR bits was set up for CERD was wrong
1699  (see sspinit.c, which initializes the
1700  packet.) The receiver values should have been at offset 10
1701  and the filter slot value should be at offset 11. */
1702 
1703  XTR_bits[pack_ix][10] = SSPOC + RFLTRS;
1704  XTR_bits[pack_ix][11] = SSPD + fslot_value;
1705 
1706  /* End CERD change */
1707 
1708  /* Insert EOS */
1709  XTR_bits[pack_ix][XTR_length[pack_ix]-1] = SSPDS;
1710 
1711  createbits(newpulse,
1712  (WF_PROCESSOR)TYPSSP,
1713  XTR_length[pack_ix],
1714  XTR_bits[pack_ix]);
1715 
1716 
1717  /*************************************/
1718  /**** Create Receive 'bits' Pulse ****/
1719  /*************************************/
1720 
1721  if (!(newpulse = (WF_PULSE *)AllocNode(sizeof(WF_PULSE))))
1722  {
1723 
1724  MsgHndlr(routine, MSG_FORMAT,
1725  HARDWARE_PBM, EMT_ALLOC, "Receive Bits");
1726 
1727  }
1728 
1729  char* rbaname = (char *)AllocNode(strlen(basename)+4);
1730  /* Create Name For Pulse */
1731  if (!rbaname)
1732  {
1733  MsgHndlr(routine, MSG_PULSE, pulse,
1734  MSG_FORMAT,
1735  HARDWARE_PBM,
1736  EMT_ALLOC,
1737  "Receive Bits Pulse Name");
1738  }
1739  sprintf(rbaname,"%srba",basename);
1740  newpulse-> pulsename = rbaname;
1741 
1742  /* Attach to exciter pulse */
1743  pulse-> assoc_pulse -> assoc_pulse = newpulse;
1744 
1745 
1746  createbits(newpulse,
1747  (WF_PROCESSOR)TYPSSP,
1748  RBA_length[pack_ix],
1749  RBA_bits[pack_ix]);
1750  /* End CERD change */
1751 
1752  }
1753 
1754  /********** End of creating bits *******************/
1755 
1756  /***************************************/
1757  /**** Create XTR Instruction ***********/
1758  /***************************************/
1759 
1760  /* Back off for settling time of exciter */
1761  if (xtr_ref == DEFAULTPOS) {
1762  start = pos_ref - (XTRSETLNG + XTR_length[pack_ix]);
1763  } else {
1764  start = xtr_ref;
1765  }
1766  createinstr(pulse -> assoc_pulse,
1767  start,
1768  XTR_length[pack_ix],
1769  0);
1770 
1771  pulse->assoc_pulse-> tag = SSPXTR;
1772 
1773  /***************************************/
1774  /**** Create DAB Instruction ***********/
1775  /***************************************/
1776  /* The DAB needs at least DABSETUP set-up time. */
1777  if (dab_ref == DEFAULTPOS) {
1778  if (xtr_ref == DEFAULTPOS) {
1779  start = pos_ref - IMax(2, DABSETUP,
1780  XTRSETLNG + XTR_length[pack_ix] + LONG_DAB_length);
1781  } else {
1782  start = IMin(2, pos_ref-DABSETUP,xtr_ref-LONG_DAB_length);
1783  }
1784 
1785  } else {
1786  start = dab_ref;
1787  }
1788 
1789  /* For DAB packet */
1790  createinstr(pulse, start, LONG_DAB_length, 0);
1791 
1792  pulse->tag = SSPDAB;
1793 
1794  /***************************************/
1795  /**** Create RBA Instruction ***********/
1796  /***************************************/
1797  /* Back off by length of RBA packet */
1798 
1799  start = pos_ref - RBA_length[pack_ix];
1800 
1801  createinstr(pulse -> assoc_pulse -> assoc_pulse,
1802  start + RBA_start,
1803  RBA_length[pack_ix],
1804  0);
1805 
1806  pulse-> assoc_pulse-> assoc_pulse-> tag = SSPRBA;
1807  /* End of CERD change */
1808  FreeNode(basename); /* Arg must be a LONG : pradeep */
1809 #ifdef PGEN_TIMING
1810  if (time_pgen == 1) {
1811 
1812  end_timer(_PGEN_start_time,acqq_ix,"acqq");
1813 
1814  }
1815 #endif
1816 
1817  return SUCCESS;
1818 }

◆ isNaN()

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

◆ ks_error()

STATUS ks_error ( const char *  format,
  ... 
)
70  {
71  char errstring[1000];
72  FILE *fp;
73  time_t mytime;
74  char mydatestr[100];
75 
76  va_list args;
77  va_start(args, format);
78  vsprintf(errstring, format, args);
79  va_end(args);
80  mytime = time(NULL);
81  sprintf(mydatestr, "%s", ctime(&mytime));
82  mydatestr[strcspn(mydatestr, "\n")] = 0; /* remove newline character */
83 
84 #ifdef PSD_HW
85  /* if on MR scanner (HOST and TGT/IPG, HW=hardware=MRscanner) */
86  fp = fopen("/usr/g/mrraw/ks_errors.txt", "a+");
87 #else
88  /* in WTools (simulation) (HOST and TGT/IPG) */
89  fp = stdout;
90 #endif
91 
92 #ifdef IPG
93  /* tgt.c: TGT/IPG side */
94  fprintf(fp, "TGT ERROR %s [%s]: %s\n", ks_psdname, mydatestr, errstring);
95 #else
96  /* host.c: HOST side */
97  fprintf(fp, "HOST ERROR %s [%s]: %s\n", ks_psdname, mydatestr, errstring);
98 #endif
99  fflush(fp);
100 
101 #ifdef PSD_HW
102  /* if on MR scanner */
103  fclose(fp);
104 #endif
105 
106 #if defined(HOST_TGT)
107  /* HOST_TGT: HOST for either WTools or MR scanner */
108  epic_error( 0, errstring, 0, EE_ARGS(0));
109 #endif
110 
111  if (abort_on_kserror) {
112  abort();
113  }
114 
115  return FAILURE;
116 }
int abort_on_kserror
Definition: KSFoundation_common.c:40
char ks_psdname[256]
Definition: GERequired.e:221

◆ ks_dbg_reset()

void ks_dbg_reset ( )

Clear debug file content

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

◆ ks_dbg()

STATUS ks_dbg ( const char *  format,
  ... 
)
134  {
135  char fname[1000];
136  char string[1000];
137  FILE *fp;
138  time_t mytime;
139  char mydatestr[100];
140 
141  va_list args;
142  va_start(args, format);
143  vsprintf(string, format, args);
144  va_end(args);
145 
146 
147 #ifdef PSD_HW
148 #ifdef IPG
149  return SUCCESS; /* Don't run ks_dbg() on MR scanner TGT to avoid potential real-time issues writing to disk */
150 #endif
151  /* MR scanner */
152  sprintf(fname, "/usr/g/mrraw/ks_debug.txt");
153 #else
154  /* Simulation (WTools) */
155  sprintf(fname, "./ks_debug.txt");
156 #endif
157 
158  mytime = time(NULL);
159  sprintf(mydatestr, "%s", ctime(&mytime));
160  mydatestr[strcspn(mydatestr, "\n")] = 0; /* remove newline character */
161 
162  fp = fopen(fname, "a+");
163 
164  fprintf(fp, "%s [%s]: %s\n", ks_psdname, mydatestr, string);
165 
166  fflush(fp);
167  fclose(fp);
168 
169  return SUCCESS;
170 
171 }
char ks_psdname[256]
Definition: GERequired.e:221

◆ existfile()

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

◆ ks_syslimits_hasICEhardware()

int ks_syslimits_hasICEhardware ( )

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

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

◆ ks_syslimits_ampmax_phys()

float ks_syslimits_ampmax_phys ( )

Returns the integer phase (not in use)

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

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

◆ ks_syslimits_ampmax()

float ks_syslimits_ampmax ( LOG_GRAD  loggrd)

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

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

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

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

◆ ks_syslimits_ampmax2()

float ks_syslimits_ampmax2 ( LOG_GRAD  loggrd)

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

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

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

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

◆ ks_syslimits_ampmax1()

float ks_syslimits_ampmax1 ( LOG_GRAD  loggrd)

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

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

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

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

◆ ks_syslimits_ampmax1p()

float ks_syslimits_ampmax1p ( LOG_GRAD  loggrd)

Returns the maximum gradient amplitude on the physical gradient axes

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

◆ ks_syslimits_gradtarget()

float ks_syslimits_gradtarget ( LOG_GRAD  loggrd,
int  board 
)

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

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

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

◆ ks_syslimits_ramptimemax()

int ks_syslimits_ramptimemax ( LOG_GRAD  loggrd)

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

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

◆ ks_syslimits_ramptimemax_phys()

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

◆ ks_syslimits_slewrate_phys()

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

◆ ks_syslimits_slewrate()

float ks_syslimits_slewrate ( LOG_GRAD  loggrd)

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

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

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

◆ ks_syslimits_slewrate2()

float ks_syslimits_slewrate2 ( LOG_GRAD  loggrd)

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

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

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

◆ ks_syslimits_slewrate1()

float ks_syslimits_slewrate1 ( LOG_GRAD  loggrd)

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

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

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

◆ ks_syslimits_slewrate1p()

float ks_syslimits_slewrate1p ( LOG_GRAD  loggrd)

Returns the maximum gradient amplitude on the physical gradient axes

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

◆ ks_phase_radians2hw()

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

◆ ks_phase_degrees2hw()

short ks_phase_degrees2hw ( float  degphase)

Returns the integer phase (internal use)

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

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

◆ ks_polyval()

void ks_polyval ( const double *  coeffs,
const int  order,
const float  x[],
const int  numx,
float  values[] 
)
297  {
298  int idx;
299  int ido;
300  for (idx = 0; idx < numx; idx++) {
301  values[idx] = coeffs[order];
302  for (ido = 0; ido < order; ido++) {
303  values[idx] += coeffs[ido]*pow(x[idx], order - ido);
304  }
305  }
306 }

◆ ks_calc_selgradamp()

float ks_calc_selgradamp ( float  rfbw,
float  slthick 
)

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

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

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

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

◆ ks_calc_minfov()

float ks_calc_minfov ( float  ampmax,
int  tsp 
)

Calculates the minimum readout FOV

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

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

◆ ks_calc_minslthick()

float ks_calc_minslthick ( float  bw)

Calculates the minimum slice thickness [mm]

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

Parameters
[in]bwRF bandwidth in [kHz]
Return values
minSliceThicknessMinimum slice thickness in [mm]
328  {
329  return (bw / (ks_syslimits_ampmax(loggrd) * (0.1) * GAM));
330 }
float GAM
LOG_GRAD loggrd
float ks_syslimits_ampmax(LOG_GRAD loggrd)
Returns the maximum gradient amplitude that can be used on all gradient boards simultaneously
Definition: KSFoundation_common.c:201

◆ ks_calc_mintsp()

int ks_calc_mintsp ( float  ampmax,
float  fov 
)

Calculates the minimum dwell time

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

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

◆ ks_calc_fov2gradareapixel()

float ks_calc_fov2gradareapixel ( float  fov)

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

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

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

◆ ks_phaseencoding_init_tgt()

void ks_phaseencoding_init_tgt ( KS_PHASEENCODING_PLAN phaseenc_plan_ptr)

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

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

◆ ks_phaseencoding_get()

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

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

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

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

◆ ks_phaseencoding_set()

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

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

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

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

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

◆ ks_phaseencoding_print()

void ks_phaseencoding_print ( const KS_PHASEENCODING_PLAN phaseenc_plan_ptr)

Print out KS_PHASEENCODING_PLAN to a text file

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

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

◆ ks_phaseencoding_resize()

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

Reallocate memory for KS_PHASEENCODING_PLAN entries

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

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

◆ ks_phaseencoding_generate_simple()

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

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

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

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

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

◆ ks_phaseencoding_generate_simple_ellipse()

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

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

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

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

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

◆ ks_fse_calcecho()

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

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

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

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

◆ ks_phaseencoding_generate_2Dfse()

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

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

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

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

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

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

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

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

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

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

◆ ks_phaseencoding_generate_epi()

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

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

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

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

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

◆ ks_phaseencoding_generate_allepiplans()

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

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

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

◆ ks_phaseencoding_allepiplans_setechoes()

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

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

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

◆ ks_wave_res()

int ks_wave_res ( const KS_WAVE wave)

Returns the number of samples in a KS_WAVE sequence object

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

◆ ks_waveform_max()

float ks_waveform_max ( const KS_WAVEFORM  waveform,
int  res 
)

Returns the maximum value in a KS_WAVEFORM

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

◆ ks_wave_max()

float ks_wave_max ( const KS_WAVE wave)

Returns the maximum value in a KS_WAVE sequence object

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

◆ ks_waveform_min()

float ks_waveform_min ( const KS_WAVEFORM  waveform,
int  res 
)

Returns the minimum value in a KS_WAVEFORM

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

◆ ks_wave_min()

float ks_wave_min ( const KS_WAVE wave)

Returns the minimum value in a KS_WAVE sequence object

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

◆ ks_iwave_absmax()

short ks_iwave_absmax ( const KS_IWAVE  waveform,
int  res 
)

Returns the max amp of a KS_IWAVE between 0 and res

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

◆ ks_waveform_absmax()

float ks_waveform_absmax ( const KS_WAVEFORM  waveform,
int  res 
)

Returns the maximum absolute value in a KS_WAVEFORM

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

◆ ks_wave_absmax()

float ks_wave_absmax ( const KS_WAVE wave)

Returns the maximum absolute value in a KS_WAVE sequence object

Parameters
[in]waveKS_WAVE object
Return values
absmaxvalMaximum absolute value
1224  {
1225  return ks_waveform_absmax(wave->waveform, wave->res);
1226 }
float ks_waveform_absmax(const KS_WAVEFORM waveform, int res)
Returns the maximum absolute value in a KS_WAVEFORM
Definition: KSFoundation_common.c:1203
int res
Definition: KSFoundation.h:667
KS_WAVEFORM waveform
Definition: KSFoundation.h:669

◆ ks_waveform_maxslew()

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

Returns the maximum absolute value in a KS_WAVEFORM

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

◆ ks_wave_maxslew()

float ks_wave_maxslew ( const KS_WAVE wave)

Returns the maximum absolute value in a KS_WAVE

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

◆ ks_waveform_area()

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

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

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

◆ ks_wave_area()

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

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

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

◆ ks_waveform_sum()

float ks_waveform_sum ( const KS_WAVEFORM  waveform,
int  res 
)

Returns the sum of a KS_WAVEFORM

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

◆ ks_wave_sum()

float ks_wave_sum ( const KS_WAVE wave)

Returns the sum of a KS_WAVE sequence object

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

◆ ks_waveform_norm()

float ks_waveform_norm ( const KS_WAVEFORM  waveform,
int  res 
)

Returns the 2-norm of a KS_WAVEFORM

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

◆ ks_wave_norm()

float ks_wave_norm ( const KS_WAVE wave)

Returns the 2-norm of a KS_WAVE

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

◆ ks_waveform_cumsum()

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

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

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

◆ ks_wave_cumsum()

void ks_wave_cumsum ( KS_WAVE cumsumwave,
const KS_WAVE wave 
)

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

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

◆ ks_waveform_multiply()

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

In-place multiplication of one KS_WAVEFORM with another KS_WAVEFORM

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

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

◆ ks_wave_multiply()

void ks_wave_multiply ( KS_WAVE wave_mod,
const KS_WAVE wave 
)

In-place multiplication of one KS_WAVE with another KS_WAVE

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

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

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

◆ ks_waveform_add()

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

In-place addition of one KS_WAVEFORM with another KS_WAVEFORM

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

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

◆ ks_wave_add()

void ks_wave_add ( KS_WAVE wave_mod,
const KS_WAVE wave 
)

In-place addition of one KS_WAVE with another KS_WAVE

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

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

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

◆ ks_waveform_multiplyval()

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

In-place scalar multiplication of a KS_WAVEFORM

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

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

◆ ks_wave_multiplyval()

void ks_wave_multiplyval ( KS_WAVE wave,
float  val 
)

In-place scalar multiplication of a KS_WAVE

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

Parameters
[in,out]waveKS_WAVE object
[in]valFloating point value to multiply with
1398  {
1399  ks_waveform_multiplyval(wave->waveform, val, wave->res);
1400 }
void ks_waveform_multiplyval(KS_WAVEFORM waveform, float val, int res)
In-place scalar multiplication of a KS_WAVEFORM
Definition: KSFoundation_common.c:1390
int res
Definition: KSFoundation.h:667
KS_WAVEFORM waveform
Definition: KSFoundation.h:669

◆ ks_waveform_addval()

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

In-place scalar addition of a KS_WAVEFORM

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

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

◆ ks_wave_addval()

void ks_wave_addval ( KS_WAVE wave,
float  val 
)

In-place scalar addition of a KS_WAVE

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

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

◆ ks_waveform2iwave()

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

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

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

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

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

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

◆ ks_wave2iwave()

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

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

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

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

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

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

◆ ks_pg_echossp()

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

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

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

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

◆ ks_pg_trap()

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

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

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

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

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

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

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

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

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

◆ ks_pg_phaser()

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

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

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

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

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

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

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

◆ ks_pg_read()

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

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

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

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

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

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

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

◆ ks_pg_readtrap()

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

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

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

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

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

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

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

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

◆ ks_pg_wave()

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

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

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

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

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

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

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

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

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

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

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

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

◆ ks_pg_rf()

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

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

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

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

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

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

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

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

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

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

◆ ks_pg_selrf()

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

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

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

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

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

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

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

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

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

◆ ks_pg_wait()

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

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

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

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

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

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

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

◆ ks_pg_isirot()

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

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

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

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

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

where the psd-specific rotation function is declared as:

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

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

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

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

◆ ks_pg_epi_dephasers()

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

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

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

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

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

◆ ks_pg_epi_rephasers()

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

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

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

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

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

◆ ks_pg_epi_echo()

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

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

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

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

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

◆ ks_pg_epi()

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

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

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

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

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

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

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

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

◆ ks_pg_mod_fse_rfpulse_structs()

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

◆ ks_pg_fse_flip_angle_taperoff()

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

◆ ks_instancereset_trap()

void ks_instancereset_trap ( KS_TRAP trap)

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

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

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

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

◆ ks_instancereset_wave()

void ks_instancereset_wave ( KS_WAVE wave)
2806  {
2807  if (!wave) { return; }
2808 
2809  wave->base.ngenerated = 0;
2810  wave->wfpulse = NULL;
2811  wave->wfi = NULL;
2812 }
KS_WFINSTANCE * wfi
Definition: KSFoundation.h:674
WF_PULSE ** wfpulse
Definition: KSFoundation.h:673
KS_BASE base
Definition: KSFoundation.h:665
int ngenerated
Definition: KSFoundation.h:410

◆ ks_instancereset_wait()

void ks_instancereset_wait ( KS_WAIT wait)

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

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

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

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

◆ ks_instancereset_phaser()

void ks_instancereset_phaser ( KS_PHASER phaser)

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

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

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

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

◆ ks_instancereset_readtrap()

void ks_instancereset_readtrap ( KS_READTRAP readtrap)

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

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

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

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

◆ ks_instancereset_rf()

void ks_instancereset_rf ( KS_RF rf)

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

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

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

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

◆ ks_instancereset_selrf()

void ks_instancereset_selrf ( KS_SELRF selrf)

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

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

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

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

◆ ks_instancereset_epi()

void ks_instancereset_epi ( KS_EPI epi)

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

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

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

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

◆ ks_mat4_zero()

void ks_mat4_zero ( KS_MAT4x4  m)

Makes a 4x4 zero matrix

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

◆ ks_mat4_identity()

void ks_mat4_identity ( KS_MAT4x4  m)

Creates a 4x4 identity matrix

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

◆ ks_mat4_print()

void ks_mat4_print ( const KS_MAT4x4  m)

Prints a 4x4 matrix to stdout

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

◆ ks_mat4_multiply()

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

Multiplication of two 4x4 matrices

Matrix product: [rhs_left] * [rhs_right]

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

◆ ks_mat4_invert()

void ks_mat4_invert ( KS_MAT4x4  lhs,
const KS_MAT4x4  rhs 
)

Inversion of a 4x4 matrix

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

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

◆ ks_mat4_setgeometry()

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

Set geometry for KS_MAT4x4

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

◆ ks_mat4_setrotation1axis()

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

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

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

◆ ks_mat4_extractrotation()

void ks_mat4_extractrotation ( KS_MAT3x3  R,
const KS_MAT4x4  M 
)

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

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

◆ ks_mat4_extracttranslation()

void ks_mat4_extracttranslation ( double *  T,
const KS_MAT4x4  M 
)

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

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

◆ ks_mat3_identity()

void ks_mat3_identity ( KS_MAT3x3  m)

Creates a 3x3 identity matrix

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

◆ ks_mat3_multiply()

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

Multiplication of two 3x3 matrices

Matrix product: [rhs_left] * [rhs_right]

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

◆ ks_mat3_print()

void ks_mat3_print ( const KS_MAT3x3  m)

Prints a 3x3 matrix to stdout

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

◆ ks_mat3_apply()

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

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

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

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

◆ ks_mat3_invapply()

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

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

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

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

◆ ks_scan_update_slice_location()

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

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

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

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

R_new = R_physical * R_slice * R_logical

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

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

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

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

◆ ks_compare_wfi_by_timeboard()

int ks_compare_wfi_by_timeboard ( const KS_WFINSTANCE a,
const KS_WFINSTANCE b 
)

Compares two WF_INSTANCEs by time, then board

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

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

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

◆ ks_compare_wfi_by_boardtime()

int ks_compare_wfi_by_boardtime ( const KS_WFINSTANCE a,
const KS_WFINSTANCE b 
)

Compares two WF_INSTANCES by board, then time

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

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

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

◆ ks_compare_wfp_by_time()

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

Compares two WF_PULSES in time

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

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

◆ ks_sort_wfi_by_timeboard()

void ks_sort_wfi_by_timeboard ( KS_WFINSTANCE a,
int  nitems 
)

Sort WF_INSTANCEs in time, then board

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

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

◆ ks_sort_wfi_by_boardtime()

void ks_sort_wfi_by_boardtime ( KS_WFINSTANCE a,
int  nitems 
)

Sort WF_INSTANCEs by board, then time

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

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

◆ ks_sort_wfp_by_time()

void ks_sort_wfp_by_time ( WF_PULSE *  a,
int  nitems 
)

Sort WF_PULSEs in time

This is the sorting method used in ks_pg_read()

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

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

◆ ks_compare_pshort()

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

Compares two short pointers

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

◆ ks_compare_pint()

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

Compares two int pointers

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

◆ ks_compare_pfloat()

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

Compares two float pointers

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

◆ ks_compare_short()

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

Compares two integers (short)

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

◆ ks_compare_int()

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

Compares two integers (int)

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

◆ ks_compare_float()

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

Compares two floats

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

◆ ks_sort_getsortedindx()

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

Sort an array of integers (int)

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

◆ ks_sort_getsortedindx_s()

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

Sort an array of integers (short)

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

◆ ks_sort_getsortedindx_f()

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

Sort an array of floats

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

◆ ks_plot_slicetime_begin()

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

◆ ks_plot_slicetime()

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

◆ ks_plot_slicetime_endofpass()

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

◆ ks_plot_slicetime_endofslicegroup_tagged()

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

◆ ks_plot_slicetime_endofslicegroup()

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

◆ ks_plot_slicetime_end()

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

◆ ks_plot_tgt_reset()

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

◆ ks_plot_tgt_addframe()

void ks_plot_tgt_addframe ( KS_SEQ_CONTROL ctrl)

Writes a plot frame to file

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

◆ ks_eval_addtraptogradrfctrl()

STATUS ks_eval_addtraptogradrfctrl ( KS_GRADRFCTRL gradrfctrl,
KS_TRAP trap 
)

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

The KS_TRAP object will be ignored if added previously

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

◆ ks_eval_addwavetogradrfctrl()

STATUS ks_eval_addwavetogradrfctrl ( KS_GRADRFCTRL gradrfctrl,
KS_WAVE wave 
)

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

The KS_WAVE object will be ignored if added previously

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

◆ ks_eval_addrftogradrfctrl()

STATUS ks_eval_addrftogradrfctrl ( KS_GRADRFCTRL gradrfctrl,
KS_RF rf 
)

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

The RF object will be ignored if added previously

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

◆ ks_eval_addreadtogradrfctrl()

STATUS ks_eval_addreadtogradrfctrl ( KS_GRADRFCTRL gradrfctrl,
KS_READ read 
)

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

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

◆ ks_scan_rf_phase_spoiling()

float ks_scan_rf_phase_spoiling ( int  counter)

Returns spoiling phase for a given RF counter

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

Variable Documentation

◆ maxGradRes

int maxGradRes

◆ cfswgut

int cfswgut

◆ cfswrfut

int cfswrfut

◆ psd_rf_wait

int psd_rf_wait

◆ psd_grd_wait

int psd_grd_wait

◆ GAM

float GAM

◆ cfssctype

int cfssctype

◆ opfov

float opfov

◆ opxres

int opxres

◆ opyres

int opyres

◆ opslthick

float opslthick

◆ rhkacq_uid

int rhkacq_uid

◆ ks_plot_filefmt

int ks_plot_filefmt

◆ ks_psdname

char ks_psdname[256]

◆ abort_on_kserror

int abort_on_kserror = ABORT_ON_KSERROR

◆ ks_rhoboard

int ks_rhoboard = TYPRHO1