Ejemplo n.º 1
0
pulsesequence()
{
  /* Internal variable declarations *************************/ 
  double  freqEx[MAXNSLICE];
  double  pespoil_amp,spoilMoment,maxgradtime,pe2_offsetamp=0.0,nvblock;
  double  tetime,te_delay,tr_delay,perTime;
  int     table=0,shapeEx=0,sepSliceRephase=0,image,blocknvs;
  char    spoilflag[MAXSTR],perName[MAXSTR],slab[MAXSTR];

  /* Real-time variables used in this sequence **************/
  int  vpe_steps    = v1;      // Number of PE steps
  int  vpe_ctr      = v2;      // PE loop counter
  int  vpe_offset   = v3;      // PE/2 for non-table offset
  int  vpe_mult     = v4;      // PE multiplier, ranges from -PE/2 to PE/2
  int  vper_mult    = v5;      // PE rewinder multiplier; turn off rewinder when 0
  int  vpe2_steps   = v6;      // Number of PE2 steps
  int  vpe2_ctr     = v7;      // PE2 loop counter
  int  vpe2_mult    = v8;      // PE2 multiplier
  int  vpe2_offset  = v9;      // PE2/2 for non-table offset
  int  vpe2r_mult   = v10;     // PE2 rewinder multiplier
  int  vtrigblock   = v11;     // Number of PE steps per trigger block
  int  vpe          = v12;     // Current PE step out of total PE*PE2 steps

  /*  Initialize paramaters *********************************/
  init_mri();
  getstr("spoilflag",spoilflag);                                     
  getstr("slab",slab);
  image = getval("image");
  blocknvs = (int)getval("blocknvs");
  nvblock = getval("nvblock");
  if (!blocknvs) nvblock=1;    // If blocked PEs for trigger not selected nvblock=1

  trmin = 0.0;
  temin = 0.0;

  /*  Check for external PE table ***************************/
  if (strcmp(petable,"n") && strcmp(petable,"N") && strcmp(petable,"")) {
    loadtable(petable);
    table = 1;
  }

  if (ns > 1)  abort_message("No of slices must be set to one");   

  /* RF Calculations ****************************************/
  init_rf(&p1_rf,p1pat,p1,flip1,rof1,rof2);   /* hard pulse */
  init_rf(&p2_rf,p2pat,p2,flip2,rof1,rof2);   /* soft pulse */
  calc_rf(&p1_rf,"tpwr1","tpwr1f");
  calc_rf(&p2_rf,"tpwr2","tpwr2f");

  /* Gradient calculations **********************************/
  if (slab[0] == 'y') {
    init_slice(&ss_grad,"ss",thk);
    init_slice_refocus(&ssr_grad,"ssr");
    calc_slice(&ss_grad,&p2_rf,WRITE,"gss");
    calc_slice_refocus(&ssr_grad,&ss_grad,WRITE,"gssr");
  }
  if (FP_GT(tcrushro,0.0))
    init_readout_butterfly(&ro_grad,"ro",lro,np,sw,gcrushro,tcrushro);
  else
    init_readout(&ro_grad,"ro",lro,np,sw);
  init_readout_refocus(&ror_grad,"ror");
  calc_readout(&ro_grad,WRITE,"gro","sw","at");
  ro_grad.m0ref *= grof;
  calc_readout_refocus(&ror_grad,&ro_grad,NOWRITE,"gror");
  init_phase(&pe_grad,"pe",lpe,nv);
  init_phase(&pe2_grad,"pe2",lpe2,nv2);
  calc_phase(&pe_grad,NOWRITE,"gpe","tpe");
  if (!blocknvs) nvblock=1;
  calc_phase(&pe2_grad,NOWRITE,"gpe2","");

  if (spoilflag[0] == 'y') {                         // Calculate spoil grad if spoiling is turned on
    init_dephase(&spoil_grad,"spoil");               // Optimized spoiler
    spoilMoment = ro_grad.acqTime*ro_grad.roamp;     // Optimal spoiling is at*gro for 2pi per pixel
    spoilMoment -= ro_grad.m0def;                    // Subtract partial spoiling from back half of readout
    calc_dephase(&spoil_grad,WRITE,spoilMoment,"gspoil","tspoil");
  }

  /* Is TE long enough for separate slab refocus? ***********/
  maxgradtime = MAX(ror_grad.duration,MAX(pe_grad.duration,pe2_grad.duration));
  if (spoilflag[0] == 'y')
    maxgradtime = MAX(maxgradtime,spoil_grad.duration);
  tetime = maxgradtime + alfa + ro_grad.timeToEcho + 4e-6;
  if (slab[0] == 'y') {
    tetime += ss_grad.rfCenterBack + ssr_grad.duration;
    if ((te >= tetime) && (minte[0] != 'y')) {
      sepSliceRephase = 1;                                 // Set flag for separate slice rephase
    } else {
      pe2_grad.areaOffset = ss_grad.m0ref;                 // Add slab refocus on pe2 axis
      calc_phase(&pe2_grad,NOWRITE,"gpe2","");             // Recalculate pe2 to include slab refocus
    }
  }
 
  /* Equalize refocus and PE gradient durations *************/
  pespoil_amp = 0.0;
  perTime = 0.0;
  if ((perewind[0] == 'y') && (spoilflag[0] == 'y')) {   // All four must be single shape
    if (ror_grad.duration > spoil_grad.duration) {       // calc_sim first with ror
      calc_sim_gradient(&pe_grad,&pe2_grad,&ror_grad,tpemin,WRITE);
      calc_sim_gradient(&ror_grad,&spoil_grad,&null_grad,tpemin,NOWRITE);
    } else {                                             // calc_sim first with spoil
      calc_sim_gradient(&pe_grad,&pe2_grad,&spoil_grad,tpemin,WRITE);
      calc_sim_gradient(&ror_grad,&spoil_grad,&null_grad,tpemin,NOWRITE);
    }
    strcpy(perName,pe_grad.name);
    perTime = pe_grad.duration;
    putvalue("tspoil",perTime);
    putvalue("gspoil",spoil_grad.amp);
  } else {                      // post-acquire shape will be either pe or spoil, but not both
    calc_sim_gradient(&ror_grad,&pe_grad,&pe2_grad,tpemin,WRITE);
    if ((perewind[0] == 'y') && (spoilflag[0] == 'n')) {     // Rewinder, no spoiler
      strcpy(perName,pe_grad.name);
      perTime = pe_grad.duration;
      spoil_grad.amp = 0.0;
      putvalue("tpe",perTime);
    } else if ((perewind[0] == 'n') && (spoilflag[0] == 'y')) {  // Spoiler, no rewinder
      strcpy(perName,spoil_grad.name);
      perTime = spoil_grad.duration;
      pespoil_amp = spoil_grad.amp;      // Apply spoiler on PE & PE2 axis if no rewinder
    }
  }

  if (slab[0] == 'y') pe2_offsetamp = sepSliceRephase ? 0.0 : pe2_grad.offsetamp;  // pe2 slab refocus

  /* Create optional prepulse events ************************/
  if (sat[0] == 'y')  create_satbands();
  if (fsat[0] == 'y') create_fatsat();

  sgl_error_check(sglerror);                               // Check for any SGL errors
  
  /* Min TE ******************************************/
  tetime = pe_grad.duration + alfa + ro_grad.timeToEcho;
  if (slab[0] == 'y') {
    tetime += ss_grad.rfCenterBack;
    tetime += (sepSliceRephase) ? ssr_grad.duration : 0.0;   // Add slice refocusing if separate event
  }
  else if (ws[0] == 'y')
    tetime += p2/2.0 + rof2;	/* soft pulse */
  else
    tetime += p1/2.0 + rof2;	/* hard pulse */
  temin = tetime + 4e-6;                                   // Ensure that te_delay is at least 4us
  if (minte[0] == 'y') {
    te = temin;
    putvalue("te",te);
  }
  if (te < temin) {
    abort_message("TE too short.  Minimum TE= %.2fms\n",temin*1000+0.005);   
  }
  te_delay = te - tetime;

  /* Min TR ******************************************/   	
  trmin  = te_delay + pe_grad.duration + ro_grad.duration + perTime;
  if (slab[0] == 'y') {
    trmin += ss_grad.duration;
    trmin += (sepSliceRephase) ? ssr_grad.duration : 0.0;   // Add slice refocusing if separate event
  }
  else if (ws[0] == 'y')
    trmin += p2 +rof1 + rof2;	/* soft pulse */
  else
    trmin += p1 +rof1 + rof2;	/* hard pulse */
  trmin += 8e-6;

  /* Increase TR if any options are selected *********/
  if (sat[0] == 'y')  trmin += satTime;
  if (fsat[0] == 'y') trmin += fsatTime;
  if (ticks > 0) trmin += 4e-6;

  if (mintr[0] == 'y') {
    tr = trmin;
    putvalue("tr",tr);
  }
  if (FP_LT(tr,trmin)) {
    abort_message("TR too short.  Minimum TR = %.2fms\n",trmin*1000+0.005);
  }

  /* Calculate tr delay */
  tr_delay = granularity(tr-trmin,GRADIENT_RES);

  if(slab[0] == 'y') {
    /* Generate phase-ramped pulses: 90 */
    offsetlist(pss,ss_grad.ssamp,0,freqEx,ns,seqcon[1]);
    shapeEx = shapelist(p1pat,ss_grad.rfDuration,freqEx,ns,ss_grad.rfFraction,seqcon[1]);
  }

  /* Set pe_steps for profile or full image **********/   	
  pe_steps = prep_profile(profile[0],nv,&pe_grad,&null_grad);
  F_initval(pe_steps/2.0,vpe_offset);

  pe2_steps = prep_profile(profile[1],nv2,&pe2_grad,&null_grad);
  F_initval(pe2_steps/2.0,vpe2_offset);

  assign(zero,oph);

  /* Shift DDR for pro *******************************/   	
  roff = -poffset(pro,ro_grad.roamp);

  /* Adjust experiment time for VnmrJ *******************/
  g_setExpTime(tr*(nt*pe_steps*pe2_steps));

  /* PULSE SEQUENCE *************************************/
  status(A);
  rotate();
  triggerSelect(trigger);       // Select trigger input 1/2/3
  obsoffset(resto);
  delay(4e-6);

  /* trigger */
  if (ticks > 0) F_initval((double)nvblock,vtrigblock);

  /* Begin phase-encode loop ****************************/       
  peloop2(seqcon[3],pe2_steps,vpe2_steps,vpe2_ctr);

    peloop(seqcon[2],pe_steps,vpe_steps,vpe_ctr);

      delay(tr_delay);   // relaxation delay

      sub(vpe_ctr,vpe_offset,vpe_mult);
      sub(vpe2_ctr,vpe2_offset,vpe2_mult);

      mult(vpe2_ctr,vpe_steps,vpe);
      add(vpe_ctr,vpe,vpe);

      /* PE rewinder follows PE table; zero if turned off ***/       
      if (perewind[0] == 'y') {
        assign(vpe_mult,vper_mult);
        assign(vpe2_mult,vpe2r_mult);
      }
      else {
        assign(zero,vper_mult);
        assign(zero,vpe2r_mult);
      }

      if (ticks > 0) {
        modn(vpe,vtrigblock,vtest);
        ifzero(vtest);                // if the beginning of an trigger block
          xgate(ticks);
          grad_advance(gpropdelay);
          delay(4e-6);
        elsenz(vtest);
          delay(4e-6);
        endif(vtest);
      }

      sp1on(); delay(4e-6); sp1off(); // Scope trigger

      /* Prepulse options ***********************************/
      if (sat[0] == 'y')  satbands();
      if (fsat[0] == 'y') fatsat();

      if (slab[0] == 'y') {
        obspower(p2_rf.powerCoarse);
        obspwrf(p2_rf.powerFine);
        delay(4e-6);
	obl_shapedgradient(ss_grad.name,ss_grad.duration,0,0,ss_grad.amp,NOWAIT);
	delay(ss_grad.rfDelayFront);
	shapedpulselist(shapeEx,ss_grad.rfDuration,zero,rof1,rof2,seqcon[1],zero);
	delay(ss_grad.rfDelayBack);
        if (sepSliceRephase) {
          obl_shapedgradient(ssr_grad.name,ssr_grad.duration,0,0,-ssr_grad.amp,WAIT);
          delay(te_delay + tau);   /* tau is current B0 encoding delay */
        }
      } else {
        obspower(p1_rf.powerCoarse);
        obspwrf(p1_rf.powerFine);
        delay(4e-6);
        if (ws[0] == 'y')
          shapedpulse(p2pat,p2,zero,rof1,rof2);   /* soft CS pulse */
        else
          shapedpulse(p1pat,p1,zero,rof1,rof2);   /* hard pulse */
        delay(te_delay + tau);   /* tau is current B0 encoding delay */
      }        

      pe2_shapedgradient(pe_grad.name,pe_grad.duration,-ror_grad.amp*image,0,-pe2_offsetamp,
          -pe_grad.increment,-pe2_grad.increment,vpe_mult,vpe2_mult,WAIT);

      if ((slab[0] == 'y') && !sepSliceRephase) delay(te_delay + tau);   /* tau is current B0 encoding delay */

      /* Readout gradient and acquisition ********************/
      obl_shapedgradient(ro_grad.name,ro_grad.duration,ro_grad.amp*image,0,0,NOWAIT);
      delay(ro_grad.atDelayFront);
      startacq(alfa);
      acquire(np,1.0/sw);
      delay(ro_grad.atDelayBack);
      endacq();

      /* Rewind / spoiler gradient *********************************/
      if (perewind[0] == 'y' || (spoilflag[0] == 'y')) {
        pe2_shapedgradient(perName,perTime,spoil_grad.amp,pespoil_amp,pespoil_amp,
          pe_grad.increment,pe2_grad.increment,vper_mult,vpe2r_mult,WAIT);
      }  

    endpeloop(seqcon[2],vpe_ctr);

  endpeloop(seqcon[3],vpe2_ctr);

}
Ejemplo n.º 2
0
void pulsesequence() {
  /* Internal variable declarations *************************/
  int     shapelist90,shapelist180;
  double  seqtime,tau1,tau2,tau3,te1_delay,te2_delay,te3_delay,tr_delay;
  double  freq90[MAXNSLICE], freq180[MAXNSLICE];
  
  /* Diffusion variables */
  double  te1, te1min, del1, del2, del3, del4;
  double  te_diff1, te_diff2, tmp1, tmp2;
  double  diffamp;
  char    diffpat[MAXSTR];
  
  /* Navigator variables */
  double  etlnav;
  
  /* Variable crushers */
  double  cscale;
  double  vcrush;  // flag

  /* Diffusion parameters */
#define MAXDIR 1024           /* Will anybody do more than 1024 directions or b-values? */
  double roarr[MAXDIR], pearr[MAXDIR], slarr[MAXDIR];
  int    nbval,               /* Total number of bvalues*directions */
         nbro, nbpe, nbsl,
	 i;    
  double bro[MAXDIR], bpe[MAXDIR], bsl[MAXDIR], /* b-values along RO, PE, SL */
         brs[MAXDIR], brp[MAXDIR], bsp[MAXDIR], /* and the cross-terms */
	 btrace[MAXDIR],                        /* and the trace */
	 max_bval=0,
         dcrush, dgss2,       /* "delta" for crusher and gss2 gradients */
         Dro, Dcrush, Dgss2;  /* "DELTA" for readout, crusher and gss2 gradients */

  /* Real-time variables used in this sequence **************/
  int  vpe_ctr     = v1;      // PE loop counter
  int  vpe_mult    = v2;      // PE multiplier, ranges from -PE/2 to PE/2
  int  vpe2_ctr    = v3;      // PE loop counter
  int  vpe2_mult   = v4;      // PE multiplier, ranges from -PE/2 to PE/2
  int  vpe2_offset = v5;
  int  vpe2_steps  = v6;
  int  vms_slices  = v7;      // Number of slices
  int  vms_ctr     = v8;      // Slice loop counter
  int  vseg        = v9;      // Number of ETL segments 
  int  vseg_ctr    = v10;      // Segment counter
  int  vetl        = v11;      // Echo train length
  int  vetl_ctr    = v12;      // Echo train loop counter
  int  vssc        = v13;     // Compressed steady-states
  int  vtrimage    = v14;     // Counts down from nt, trimage delay when 0
  int  vacquire    = v15;     // Argument for setacqvar, to skip steady state acquires
  int  vphase180   = v16;     // phase of 180 degree refocusing pulse
  int  vetl_loop   = v17;     // Echo train length MINUS ONE, used on etl loop
  int  vnav        = v18;     // Echo train length
  int  vcr_ctr     = v19;     // variable crusher, index into table
  int  vcr1        = v20;     // multiplier along RO
  int  vcr2        = v21;     // multiplier along PE
  int  vcr3        = v22;     // multiplier along SL
  int  vetl1       = v23;     // = etl-1, determine navigator echo location in echo loop
  int  vcr_reset   = v24;     // check for navigator echoes, reset crushers

  /* Initialize paramaters **********************************/
  get_parameters();
  te1    = getval("te1");     /* te1 is the echo time for the first echo */
  cscale = getval("cscale");  /* Scaling factor on first 180 crushers */
  vcrush = getval("vcrush");  /* Variable crusher or set amplitude? */
  getstr("diffpat",diffpat);


  /*  Load external PE table ********************************/
  if (strcmp(petable,"n") && strcmp(petable,"N") && strcmp(petable,"")) {
    loadtable(petable);
  } else {
    abort_message("petable undefined");
  }

  /* Hold variable crushers in tables 5, 6, 7 */
  settable(t5,8,crro);
  settable(t6,8,crpe);    
  settable(t7,8,crss);
    
  seqtime = 0.0;
  espmin = 0.0;

  /* RF Power & Bandwidth Calculations **********************/
  init_rf(&p1_rf,p1pat,p1,flip1,rof1,rof2);
  init_rf(&p2_rf,p2pat,p2,flip2,rof1,rof2);
//  shape_rf(&p1_rf,"p1",p1pat,p1,flip1,rof1,rof2);
//  shape_rf(&p2_rf,"p2",p2pat,p2,flip2,rof1,rof2);
  calc_rf(&p1_rf,"tpwr1","tpwr1f");
  calc_rf(&p2_rf,"tpwr2","tpwr2f");
 
  /* Initialize gradient structures *************************/
  init_readout(&ro_grad,"ro",lro,np,sw); 
  init_readout_refocus(&ror_grad,"ror");
  init_phase(&pe_grad,"pe",lpe,nv);
  init_phase(&pe2_grad,"pe2",lpe2,nv2);
  init_slice(&ss_grad,"ss",thk);   /* NOTE assume same band widths for p1 and p2 */     
  init_slice(&ss2_grad,"ss2",thk);   /* not butterfly, want to scale crushers w/ echo */
  init_slice_refocus(&ssr_grad,"ssr");

  /* Gradient calculations **********************************/
  calc_readout(&ro_grad,WRITE,"gro","sw","at");
  calc_readout_refocus(&ror_grad,&ro_grad,NOWRITE,"gror");
  calc_phase(&pe_grad,NOWRITE,"gpe","tpe");
  calc_phase(&pe2_grad,NOWRITE,"gpe2","tpe2");
  calc_slice(&ss_grad,&p1_rf,WRITE,"gss");
  calc_slice(&ss2_grad,&p1_rf,WRITE,"");
  calc_slice_refocus(&ssr_grad,&ss_grad,WRITE,"gssr");

  /* Equalize refocus and PE gradient durations *************/
  calc_sim_gradient(&ror_grad,&pe_grad,&pe2_grad,0.0,WRITE);

  /* Variable crusher */
  init_generic(&crush_grad,"crush",gcrush,tcrush);
  calc_generic(&crush_grad,WRITE,"","");

  /* Create optional prepulse events ************************/
  if (sat[0]  == 'y') create_satbands();
  if (fsat[0] == 'y') create_fatsat();
  if (mt[0]   == 'y') create_mtc();
  
  /* Optional Diffusion gradient */
  if (diff[0] == 'y') {
    init_generic(&diff_grad,"diff",gdiff,tdelta);
    if (!strcmp("sine",diffpat)) {
      diff_grad.shape = SINE;
      diffamp         = gdiff*1;
    }
 
    /* adjust duration, so tdelta is from start ramp up to start ramp down */   
    if ((ix == 1) && (diff_grad.shape == TRAPEZOID)) {
      calc_generic(&diff_grad,NOWRITE,"","");
      diff_grad.duration += diff_grad.tramp; 
    }
    calc_generic(&diff_grad,WRITE,"","");  
  }

  /* Set up frequency offset pulse shape list ********/
  offsetlist(pss,ss_grad.amp,0,freq90,ns,seqcon[1]);
  offsetlist(pss,ss2_grad.ssamp,0,freq180,ns,seqcon[1]);
  shapelist90  = shapelist(p1_rf.pulseName,ss_grad.rfDuration, freq90, ns,0,seqcon[1]);
  shapelist180 = shapelist(p2_rf.pulseName,ss2_grad.rfDuration,freq180,ns,0,seqcon[1]);

  /* same slice selection gradient and RF pattern used */
  if (ss_grad.rfFraction != 0.5)
    abort_message("RF pulse must be symmetric (RF fraction = %.2f)",ss_grad.rfFraction);
  if (ro_grad.echoFraction != 1)
    abort_message("Echo Fraction must be 1");


  /*****************************************************/
  /* TIMING FOR ECHOES *********************************/
  /*****************************************************/
  /* First echo time, without diffusion */
  tau1 = ss_grad.rfCenterBack + ssr_grad.duration + crush_grad.duration + ss2_grad.rfCenterFront;
  tau2 = ss2_grad.rfCenterBack + crush_grad.duration + pe_grad.duration + ro_grad.timeToEcho;
  te1min = 2*MAX(tau1,tau2);
  if (te1 < te1min + 2*4e-6) {
    abort_message("First echo time too small, minimum is %.2fms\n",(te1min+2*4e-6)*1000);
  }

  /* Each half-echo period in the ETL loop ********/
  tau3 = ro_grad.timeFromEcho + pe_grad.duration + crush_grad.duration + ss2_grad.rfCenterFront;
  espmin = 2*MAX(tau2,tau3);   // Minimum echo spacing
  if (minesp[0] == 'y') {
    esp = espmin + 2*4e-6;
    putvalue("esp",esp);
  }
  if (esp - (espmin + 2*4e-6) < -12.5e-9) {
    abort_message("Echo spacing too small, minimum is %.2fms\n",(espmin+2*4e-6)*1000);
  }


  te1_delay = te1/2.0 - tau1;
  te2_delay = esp/2.0 - tau2;
  te3_delay = esp/2.0 - tau3;


  /*****************************************************/
  /* TIMING FOR DIFFUSION ******************************/
  /*****************************************************/
  del1 = te1/2.0 - tau1;
  del2 = 0;
  del3 = te1/2.0 - tau2;
  del4 = 0;

  if (diff[0] == 'y') {
    tau1 += diff_grad.duration;
    tau2 += diff_grad.duration;

    te1min = 2*MAX(tau1,tau2);
    if (te1 < te1min + 4*4e-6) {  /* te1 is split into 4 delays, each of which must be >= 4us */
      abort_message("ERROR %s: First echo time too small, minimum is %.2fms\n",seqfil,te1min*1000);
    }

    /* te1 is the echo time for the first echo */
    te_diff1 = te1/2 - tau1;  /* Available time in first half of first echo */
    te_diff2 = te1/2 - tau2;  /* Available time in second half of first echo */

    tmp1 = ss2_grad.duration + 2*crush_grad.duration;  /* duration of 180 block */
    /* Is tDELTA long enough? */
    if (tDELTA < diff_grad.duration + tmp1)
      abort_message("DELTA too short, increase to %.2fms",
        (diff_grad.duration + tmp1)*1000);

    /* Is tDELTA too long? */
    tmp2 = diff_grad.duration + te_diff1 + tmp1 + te_diff2;
    if (tDELTA > tmp2) {
      abort_message("DELTA too long, increase te1 to %.2fms",
        (te1 + (tDELTA-tmp2))*1000);
    }

    /* First attempt to put lobes right after slice select, ie del1 = 0 */
    del1 = 4e-6;  /* At least 4us after setting power for 180 */
    del2 = te_diff1 - del1;
    del3 = tDELTA - (diff_grad.duration + del2 + tmp1);
    
    if (del3 < 4e-6) {  /* shift diffusion block towards acquisition */
      del3 = 4e-6;
      del2 = tDELTA - (diff_grad.duration + tmp1 + del3);
    }

    del1 = te_diff1 - del2;
    del4 = te_diff2 - del3;
    
    if (fabs(del4) < 12.5e-9) del4 = 0;
  
  }
  te = te1 + (kzero-1)*esp;                // Return effective TE
  putvalue("te",te);

  /* How many echoes in the echo loop, including navigators? */
  etlnav = (etl-1)+(navigator[0]=='y')*2.0;
  
  /* Minimum TR **************************************/
  seqtime  = 4e-6 + 2*nseg*ns*4e-6;  /* count all the 4us delays */
  seqtime += ns*(ss_grad.duration/2 + te1 + (etlnav)*esp + ro_grad.timeFromEcho + pe_grad.duration + te3_delay);

  /* Increase TR if any options are selected****************/
  if (sat[0] == 'y')  seqtime += ns*satTime;
  if (fsat[0] == 'y') seqtime += ns*fsatTime;
  if (mt[0] == 'y')   seqtime += ns*mtTime;

  trmin = seqtime + ns*4e-6;  /* Add 4us to ensure that tr_delay is always >= 4us */
  if (mintr[0] == 'y'){
    tr = trmin;
    putvalue("tr",tr+1e-6);
  }
  if (tr < trmin) {
    abort_message("TR too short.  Minimum TR = %.2fms\n",trmin*1000);
  }
  tr_delay = (tr - seqtime)/ns;


  /* Set number of segments for profile or full image **********/
  nseg      = prep_profile(profile[0],nv/etl,&pe_grad,&per_grad);
  pe2_steps = prep_profile(profile[1],nv2,&pe2_grad,&pe2r_grad);

  /* Calculate total scan time */
  g_setExpTime(tr*(nt*nseg*pe2_steps*arraydim + ssc));




  /***************************************************/
  /* CALCULATE B VALUES ******************************/
  if (diff[0] == 'y') {
    /* Get multiplication factors and make sure they have same # elements */
    /* All this is only necessary because putCmd only work for ix==1      */
    nbro = (int) getarray("dro",roarr);  nbval = nbro;
    nbpe = (int) getarray("dpe",pearr);  if (nbpe > nbval) nbval = nbpe;
    nbsl = (int) getarray("dsl",slarr);  if (nbsl > nbval) nbval = nbsl;
    if ((nbro != nbval) && (nbro != 1))
      abort_message("%s: Number of directions/b-values must be the same for all axes (readout)",seqfil);
    if ((nbpe != nbval) && (nbpe != 1))
      abort_message("%s: Number of directions/b-values must be the same for all axes (phase)",seqfil);
    if ((nbsl != nbval) && (nbsl != 1))
      abort_message("%s: Number of directions/b-values must be the same for all axes (slice)",seqfil);


    if (nbro == 1) for (i = 1; i < nbval; i++) roarr[i] = roarr[0];
    if (nbpe == 1) for (i = 1; i < nbval; i++) pearr[i] = pearr[0];
    if (nbsl == 1) for (i = 1; i < nbval; i++) slarr[i] = slarr[0];

  }
  else {
    nbval = 1;
    roarr[0] = 0;
    pearr[0] = 0;
    slarr[0] = 0;
  }

  for (i = 0; i < nbval; i++)  {
    dcrush = crush_grad.duration;       //"delta" for crusher
    Dcrush = dcrush + ss_grad.duration; //"DELTA" for crusher

    /* Readout */
    Dro     = ror_grad.duration;
    bro[i]  = bval(gdiff*roarr[i],tdelta,tDELTA);
    bro[i] += bval(ro_grad.amp,ro_grad.timeToEcho,Dro);
    bro[i] += bval(crush_grad.amp,dcrush,Dcrush);
    bro[i] += bval_nested(gdiff*roarr[i],tdelta,tDELTA,
                crush_grad.amp,dcrush,Dcrush);

    /* Slice */
    dgss2   = Dgss2 = ss_grad.rfCenterFront;
    bsl[i]  = bval(gdiff*slarr[i],tdelta,tDELTA);
    bsl[i] += bval(crush_grad.amp,dcrush,Dcrush);
    bsl[i] += bval(ss2_grad.ssamp,dgss2,Dgss2);
    bsl[i] += bval_nested(gdiff*slarr[i],tdelta,tDELTA,
                crush_grad.amp,dcrush,Dcrush);
    bsl[i] += bval_nested(gdiff*slarr[i],tdelta,tDELTA,ss2_grad.ssamp,dgss2,Dgss2);
    bsl[i] += bval_nested(ss2_grad.ssamp,dgss2,Dgss2,
                crush_grad.amp,dcrush,Dcrush);

    /* Phase */
    bpe[i]  = bval(gdiff*pearr[i],tdelta,tDELTA);
    bpe[i] += bval(crush_grad.amp,dcrush,Dcrush);
    bpe[i] += bval_nested(gdiff*pearr[i],tdelta,tDELTA,
                crush_grad.amp,dcrush,Dcrush);

    /* Readout/Slice Cross-terms */
    brs[i]  = bval2(gdiff*roarr[i],gdiff*slarr[i],tdelta,tDELTA);
    brs[i] += bval2(crush_grad.amp,
                    crush_grad.amp,dcrush,Dcrush);
    brs[i] += bval_cross(gdiff*roarr[i],tdelta,tDELTA,
                crush_grad.amp,dcrush,Dcrush);
    brs[i] += bval_cross(gdiff*slarr[i],tdelta,tDELTA,
                crush_grad.amp,dcrush,Dcrush);
    brs[i] += bval_cross(gdiff*roarr[i],tdelta,tDELTA,
                ss2_grad.ssamp,dgss2,Dgss2);
    brs[i] += bval_cross(crush_grad.amp,dcrush,Dcrush,
                ss2_grad.ssamp,dgss2,Dgss2);

    /* Readout/Phase Cross-terms */
    brp[i]  = bval2(gdiff*roarr[i],gdiff*pearr[i],tdelta,tDELTA);
    brp[i] += bval2(crush_grad.amp,
                    crush_grad.amp,dcrush,Dcrush);
    brp[i] += bval_cross(gdiff*roarr[i],tdelta,tDELTA,
                crush_grad.amp,dcrush,Dcrush);
    brp[i] += bval_cross(gdiff*pearr[i],tdelta,tDELTA,
                crush_grad.amp,dcrush,Dcrush);

    /* Slice/Phase Cross-terms */
    bsp[i]  = bval2(gdiff*pearr[i],gdiff*slarr[i],tdelta,tDELTA);
    bsp[i] += bval2(crush_grad.amp,
                    crush_grad.amp,dcrush,Dcrush);
    bsp[i] += bval_cross(gdiff*pearr[i],tdelta,tDELTA,
                crush_grad.amp,dcrush,Dcrush);
    bsp[i] += bval_cross(gdiff*slarr[i],tdelta,tDELTA,
                crush_grad.amp,dcrush,Dcrush);
    bsp[i] += bval_cross(gdiff*pearr[i],tdelta,tDELTA,
                ss2_grad.ssamp,dgss2,Dgss2);
    bsp[i] += bval_cross(crush_grad.amp,dcrush,Dcrush,
                ss2_grad.ssamp,dgss2,Dgss2);


    btrace[i] = (bro[i]+bsl[i]+bpe[i]);

    if (max_bval < btrace[i]) {
      max_bval = (bro[i]+bsl[i]+bpe[i]);
    }
  }  /* End for-all-directions */

  putarray("bvalrr",bro,nbval);
  putarray("bvalpp",bpe,nbval);
  putarray("bvalss",bsl,nbval);
  putarray("bvalrp",brp,nbval);
  putarray("bvalrs",brs,nbval);
  putarray("bvalsp",bsp,nbval);
  putarray("bvalue",btrace,nbval);
  putvalue("max_bval",max_bval);





  /* Shift DDR for pro *******************************/
  roff = -poffset(pro,ro_grad.roamp);


  /* PULSE SEQUENCE *************************************/
  if (ix == 1) grad_advance(tep);
  initval(fabs(ssc),vssc);      // Compressed steady-state counter
  setacqvar(vacquire);          // Control acquisition through vacquire
  assign(one,vacquire);         // Turn on acquire when vacquire is zero

  /* Phase cycle: Alternate 180 phase to cancel residual FID */
  mod2(ct,vphase180);           // 0101
  dbl(vphase180,vphase180);     // 0202
  add(vphase180,one,vphase180); // 1313 Phase difference from 90
  add(vphase180,oph,vphase180);

  obsoffset(resto);
  delay(4e-6);
    
  initval(nseg,vseg);
  initval(pe2_steps/2.0,vpe2_offset);
  
  initval(etl,vetl);
  initval(etl-1,vetl1);

  peloop2(seqcon[3],pe2_steps,vpe2_steps,vpe2_ctr);
  /* Use standard encoding order for 2nd PE dimension */
  sub(vpe2_ctr,vpe2_offset,vpe2_mult);

    loop(vseg,vseg_ctr);

      /* Compressed steady-states: 1st array & transient, all arrays if ssc is negative */
      if ((ix > 1) && (ssc > 0))
	assign(zero,vssc);
      sub(vseg_ctr,vssc,vseg_ctr);   // vpe_ctr counts up from -ssc
      assign(zero,vssc);
      ifzero(vseg_ctr);
	assign(zero,vacquire);       // Start acquiring when vseg_ctr reaches zero
      endif(vseg_ctr);

      msloop(seqcon[1],ns,vms_slices,vms_ctr);
	if (ticks) {
          xgate(ticks);
          grad_advance(tep);
	}
	sp1on(); delay(4e-6); sp1off();    // Scope trigger

	/* Prepulse options ***********************************/
	if (sat[0]  == 'y') satbands();
	if (fsat[0] == 'y') fatsat();
	if (mt[0]   == 'y') mtc();

	/* 90 degree pulse ************************************/         
	rotate();
	obspower(p1_rf.powerCoarse);
	obspwrf(p1_rf.powerFine);
	delay(4e-6);
	obl_shapedgradient(ss_grad.name,ss_grad.duration,0,0,ss_grad.amp,NOWAIT);   
	delay(ss_grad.rfDelayFront);
	shapedpulselist(shapelist90,ss_grad.rfDuration,oph,rof1,rof2,seqcon[1],vms_ctr);
	delay(ss_grad.rfDelayBack);

	/* Read dephase and Slice refocus *********************/
	obl_shapedgradient(ssr_grad.name,ssr_grad.duration,0.0,0.0,-ssr_grad.amp,WAIT);

	/* First half-TE delay ********************************/
	obspower(p2_rf.powerCoarse);
	obspwrf(p2_rf.powerFine);
	delay(del1);

	/* DIFFUSION GRADIENT */
	if (diff[0] == 'y')
	  obl_shapedgradient(diff_grad.name,diff_grad.duration,diff_grad.amp*dro,diff_grad.amp*dpe,diff_grad.amp*dsl,WAIT);

	delay(del2);


	/*****************************************************/
	    /* FIRST ECHO OUTSIDE LOOP ***************************/
	/*****************************************************/
	ifzero(vacquire);  // real acquisition, get PE multiplier from table
          mult(vseg_ctr,vetl,vpe_ctr);
          getelem(t1,vpe_ctr,vpe_mult);
	elsenz(vacquire);  // steady state scan 
          assign(zero,vpe_mult);
	endif(vacquire);

	/* Variable crusher */
	assign(zero,vcr_ctr);
	getelem(t5,vcr_ctr,vcr1); 
	getelem(t6,vcr_ctr,vcr2);	     
	getelem(t7,vcr_ctr,vcr3);

  if(vcrush) 
	phase_encode3_oblshapedgradient(crush_grad.name,crush_grad.name,crush_grad.name,
	  crush_grad.duration,
	  (double)0,(double)0,(double)0,                                     // base levels
	  crush_grad.amp*cscale,crush_grad.amp*cscale,crush_grad.amp*cscale, // step size
	  vcr1,vcr2,vcr3,                                                    // multipliers
	  (double)1.0,(double)1.0,(double)1.0,                               // upper limit on multipliers
	  1,WAIT,0);

  else 
  obl_shapedgradient(crush_grad.name,crush_grad.duration,
    crush_grad.amp,crush_grad.amp,crush_grad.amp,WAIT);

	/* 180 degree pulse *******************************/
	obl_shapedgradient(ss2_grad.name,ss2_grad.duration,0,0,ss2_grad.amp,NOWAIT);   
	delay(ss2_grad.rfDelayFront); 
	shapedpulselist(shapelist180,ss2_grad.rfDuration,vphase180,rof1,rof2,seqcon[1],vms_ctr);
	delay(ss2_grad.rfDelayBack);   

  if (vcrush)
	phase_encode3_oblshapedgradient(crush_grad.name,crush_grad.name,crush_grad.name,
	  crush_grad.duration,
	  (double)0,(double)0,(double)0,                                     // base levels
	  crush_grad.amp*cscale,crush_grad.amp*cscale,crush_grad.amp*cscale, // step size
	  vcr1,vcr2,vcr3,                                                    // multipliers
	  (double)1.0,(double)1.0,(double)1.0,                               // upper limit on multipliers
	  1,WAIT,0);
  else
  obl_shapedgradient(crush_grad.name,crush_grad.duration,
    crush_grad.amp,crush_grad.amp,crush_grad.amp,WAIT);

	delay(del3);

	/* DIFFUSION GRADIENT */
	if (diff[0] == 'y')
	  obl_shapedgradient(diff_grad.name,diff_grad.duration,diff_grad.amp*dro,diff_grad.amp*dpe,diff_grad.amp*dsl,WAIT);

	delay(del4);

	/* Phase-encode gradient ******************************/
	pe2_shapedgradient(pe_grad.name,pe_grad.duration,-ror_grad.amp,0,0,
	  -pe_grad.increment,-pe2_grad.increment,vpe_mult,vpe2_mult,WAIT);

	/* Readout gradient ************************************/
	obl_shapedgradient(ro_grad.name,ro_grad.duration,ro_grad.roamp,0,0,NOWAIT);
	delay(ro_grad.atDelayFront);

	/* Acquire data ****************************************/
	startacq(10e-6);
	acquire(np,1.0/sw);
	endacq();

	delay(ro_grad.atDelayBack);

	/* Rewinding phase-encode gradient ********************/
	pe2_shapedgradient(pe_grad.name,pe_grad.duration,0,0,0,
	  pe_grad.increment,pe2_grad.increment,vpe_mult,vpe2_mult,WAIT);

	/* Second half-TE delay *******************************/
	delay(te3_delay);


	/*****************************************************/
	    /* LOOP THROUGH THE REST OF ETL **********************/
	/*****************************************************/
	peloop(seqcon[2],etlnav,vetl_loop,vetl_ctr);
	  ifzero(vacquire);  // real acquisition, get PE multiplier from table
            mult(vseg_ctr,vetl,vpe_ctr);
            add(vpe_ctr,vetl_ctr,vpe_ctr);
	    add(vpe_ctr,one,vpe_ctr);
            getelem(t1,vpe_ctr,vpe_mult);
    	  elsenz(vacquire);  // steady state scan 
	    assign(zero,vpe_mult);
	  endif(vacquire);

	  /* But don't phase encode navigator echoes */
          ifrtGE(vetl_ctr,vetl1,vnav);
	    assign(zero,vpe_mult);
	  endif(vnav);


    	  /* Variable crusher */
	  incr(vcr_ctr);  /* Get next crusher level */
	  /* Except if we're doing navigators, start over */
	  sub(vetl1,vetl_ctr,vcr_reset);
	  ifzero(vcr_reset);
	    assign(zero,vcr_ctr);
	  endif(vcr_reset);

    	  getelem(t5,vcr_ctr,vcr1); 
    	  getelem(t6,vcr_ctr,vcr2);	     
    	  getelem(t7,vcr_ctr,vcr3);

	  phase_encode3_oblshapedgradient(crush_grad.name,crush_grad.name,crush_grad.name,
	    crush_grad.duration,
	    (double)0,(double)0,(double)0,                                     // base levels
	    crush_grad.amp,crush_grad.amp,crush_grad.amp,                      // step size
	    vcr1,vcr2,vcr3,                                                    // multipliers
	    (double)1.0,(double)1.0,(double)1.0,                               // upper limit on multipliers
	    1,WAIT,0);

          /* 180 degree pulse *******************************/
          obl_shapedgradient(ss2_grad.name,ss2_grad.duration,0,0,ss2_grad.amp,NOWAIT);   
    	  delay(ss2_grad.rfDelayFront); 
          shapedpulselist(shapelist180,ss2_grad.rfDuration,vphase180,rof1,rof2,seqcon[1],vms_ctr);
          delay(ss2_grad.rfDelayBack);   

          /* Variable crusher */
	  phase_encode3_oblshapedgradient(crush_grad.name,crush_grad.name,crush_grad.name,
	    crush_grad.duration,
	    (double)0,(double)0,(double)0,                                     // base levels
	    crush_grad.amp,crush_grad.amp,crush_grad.amp,                      // step size
	    vcr1,vcr2,vcr3,                                                    // multipliers
	    (double)1.0,(double)1.0,(double)1.0,                               // upper limit on multipliers
	    1,WAIT,0);

          /* Phase-encode gradient ******************************/
	  pe2_shapedgradient(pe_grad.name,pe_grad.duration,0,0,0,
	    -pe_grad.increment,-pe2_grad.increment,vpe_mult,vpe2_mult,WAIT);

          /* Second half-TE period ******************************/
	  delay(te2_delay);

          /* Readout gradient ************************************/
          obl_shapedgradient(ro_grad.name,ro_grad.duration,ro_grad.roamp,0,0,NOWAIT);
          delay(ro_grad.atDelayFront);
          startacq(10e-6);
          acquire(np,1.0/sw);
	  endacq();
          delay(ro_grad.atDelayBack);

          /* Rewinding phase-encode gradient ********************/
	  pe2_shapedgradient(pe_grad.name,pe_grad.duration,0,0,0,
	    pe_grad.increment,pe2_grad.increment,vpe_mult,vpe2_mult,WAIT);

          /* Second half-TE delay *******************************/
          delay(te3_delay);
	endpeloop(seqcon[2],vetl_ctr);

	/* Relaxation delay ***********************************/
	if (!trtype)
          delay(tr_delay);
      endmsloop(seqcon[1],vms_ctr);
      if (trtype)
	delay(ns*tr_delay);
    endloop(vseg_ctr);
  endpeloop(seqcon[3],vpe2_ctr);

  /* Inter-image delay **********************************/
  sub(ntrt,ct,vtrimage);
  decr(vtrimage);
  ifzero(vtrimage);
    delay(trimage);
  endif(vtrimage);
}
Ejemplo n.º 3
0
void pulsesequence() {
  /* Internal variable declarations *********************/
  int    shapelist90,shapelist180,shapelistte=0;
  double kzero,thk2fact,thk3fact;
  double te1=0.0,te1_delay,te2_delay,te3_delay,tr_delay,te_delay1=0.0,te_delay2=0.0;
  double crushm0,pem0,gcrushr,gcrushp,gcrushs;
  double freq90[MAXNSLICE],freq180[MAXNSLICE],freqte[MAXNSLICE];
  char   autocrush[MAXSTR];

  /* Phase encode variables */
  FILE  *fp;
  int    tab[4096],petab[4096],odd,seg0,tabscheme;
  char   tabname[MAXSTR],tabfile[MAXSTR];
  int    i,j,k;

  /* Diffusion variables */
  double Gro,Gss;          // "gdiff" for readout/readout refocus and slice/slice refocus
  double dgro,Dgro;        // delta and DELTA for readout dephase & readout
  double dgss,Dgss;        // delta and DELTA for excitation ss
  double dgss3,Dgss3;      // delta and DELTA for spin echo prep ss
  double dcrush3,Dcrush3;  // delta and DELTA for spin echo prep crusher
  double dgss2,Dgss2;      // delta and DELTA for refocus ss
  double dcrush2,Dcrush2;  // delta and DELTA for refocus crusher

  /* Real-time variables used in this sequence **********/
  int  vpe_ctr     = v2;   // PE loop counter
  int  vpe_mult    = v3;   // PE multiplier, ranges from -PE/2 to PE/2
  int  vms_slices  = v4;   // Number of slices
  int  vms_ctr     = v5;   // Slice loop counter
  int  vseg        = v6;   // Number of ETL segments 
  int  vseg_ctr    = v7;   // Segment counter
  int  vetl        = v8;   // Echo train length
  int  vetl_ctr    = v9;   // Echo train loop counter
  int  vpe2_steps  = v10;  // Number of PE2 steps
  int  vpe2_ctr    = v11;  // PE2 loop counter
  int  vpe2_mult   = v12;  // PE2 multiplier
  int  vpe2_offset = v13;  // PE2/2 for non-table offset
  int  vssc        = v14;  // Compressed steady-states
  int  vtrimage    = v15;  // Counts down from nt, trimage delay when 0
  int  vacquire    = v16;  // Argument for setacqvar, to skip steady state acquires
  int  vphase180   = v17;  // phase of 180 degree refocusing pulse
  int  vtrigblock  = v18;  // Number of slices per trigger block

  /* Initialize paramaters ******************************/
  init_mri();

  kzero = getval("kzero");
  getstr("autocrush",autocrush);
  tabscheme = getval("tabscheme");
  getstr("spoilflag",spoilflag);

  /* Allow ROxPE2 projection ****************************/
  if (profile[0] == 'y' && profile[1] == 'n') {
    etl=1; kzero=1; nv=nv2;
  } else {
    /* Check kzero is valid *****************************/
    if (kzero<1) kzero=1; if (kzero>etl) kzero=etl;
    putCmd("kzero = %d",(int)kzero); 
  }

  /* Set petable name and full path *********************/
  sprintf(tabname,"fse%d_%d_%d",(int)nv,(int)etl,(int)kzero);
  putCmd("petable = '%s'",tabname);
  strcpy(tabfile,userdir);
  strcat(tabfile,"/tablib/");
  strcat(tabfile,tabname);

  /* Generate phase encode table ************************/
  if (tabscheme) { /* New scheme */
    /* Calculate PE table for kzero=1 */
    seg0=nseg/2;
    for (j=0;j<seg0;j++) {
      for (i=0;i<etl/2;i++) tab[j*(int)etl+i] = i*nseg+seg0-j;
      for (i=1;i<=etl/2;i++) tab[(j+1)*(int)etl-i] = tab[j*(int)etl]-i*nseg;
    }
    for (j=seg0;j<nseg;j++) {
      for (i=0;i<=etl/2;i++) tab[j*(int)etl+i] = i*nseg+seg0-j;
      for (i=1;i<etl/2;i++) tab[(j+1)*(int)etl-i] = tab[j*(int)etl]-i*nseg;
    }
    /* Adjust for kzero */
    for (i=0;i<nseg;i++) { 
      k=i*etl;
      for (j=0;j<kzero-1;j++) petab[k+j]=tab[k+(int)etl-(int)kzero+j+1];
      for (j=kzero-1;j<etl;j++) petab[k+j]=tab[k+j-(int)kzero+1];
    }
  } else { /* Original scheme */
    /* Calculate PE table for kzero=1 */
    odd=(int)nseg%2; seg0=nseg/2+odd;
    k=0; for (i=0;i<etl;i++) for (j=seg0-odd*i%2-1;j>=0;j--) tab[j*(int)etl+i] = k--;
    k=1; for (i=0;i<etl;i++) for (j=seg0-odd*i%2;j<nseg;j++) tab[j*(int)etl+i] = k++;
    /* Adjust for kzero */
    for (i=0;i<nseg;i++) { 
      k=i*etl;
      for (j=0;j<kzero-1;j++) petab[k+j]=tab[k+(int)etl-j-1];
      for (j=kzero-1;j<etl;j++) petab[k+j]=tab[k+j-(int)kzero+1];
    }
  }
  /* Set petable name and full path *********************/
  sprintf(tabname,"fse%d_%d_%d",(int)nv,(int)etl,(int)kzero);
  putCmd("petable = '%s'",tabname);
  strcpy(tabfile,userdir);
  strcat(tabfile,"/tablib/");
  strcat(tabfile,tabname);
  /* Write to tabfile ***********************************/
  fp=fopen(tabfile,"w");
  fprintf(fp,"t1 =");
  for (i=0;i<nseg;i++) { 
    fprintf(fp,"\n");
    for (j=0;j<etl;j++) fprintf(fp,"%3d\t",petab[i*(int)etl+j]);
  }
  fclose(fp);

  /* Set pelist to contain table order ******************/
  putCmd("pelist = 0"); /* Re-initialize pelist */
  for (i=0;i<nseg*etl;i++) putCmd("pelist[%d] = %d",i+1,petab[i]);

  /* Avoid gradient slew rate errors */
  for (i=0;i<nseg*etl;i++) if (abs(petab[i]) > nv/2) petab[i]=0;

  /* Set phase encode table *****************************/
  settable(t1,(int)etl*nseg,petab);

  /* RF Power & Bandwidth Calculations ******************/
  shape_rf(&p1_rf,"p1",p1pat,p1,flip1,rof1,rof2);
  shape_rf(&p2_rf,"p2",p2pat,p2,flip2,rof1,rof2);
  calc_rf(&p1_rf,"tpwr1","tpwr1f");
  calc_rf(&p2_rf,"tpwr2","tpwr2f");

  /* Calculate thk2fact to ensure gss=gss2 for the choice of p1 and p2 
     so that the sequence remains robust in the absence of correct
     balancing of slice select and slice refocus gradients */
  thk2fact=p2_rf.bandwidth/p1_rf.bandwidth;
  putvalue("thk2fact",thk2fact);
  
  /* Initialize gradient structures *********************/
  init_readout(&ro_grad,"ro",lro,np,sw);
  ro_grad.pad1=alfa; ro_grad.pad2=alfa;
  init_readout_refocus(&ror_grad,"ror");
  init_phase(&pe_grad,"pe",lpe,nv);
  init_phase(&pe2_grad,"pe2",lpe2,nv2);
  init_slice(&ss_grad,"ss",thk);
  init_slice(&ss2_grad,"ss2",thk*thk2fact);
  init_slice_refocus(&ssr_grad,"ssr");
  init_dephase(&crush_grad,"crush");

  /* Gradient calculations ******************************/
  calc_readout(&ro_grad,WRITE,"gro","sw","at");
  calc_readout_refocus(&ror_grad,&ro_grad,WRITE,"gror");
  calc_phase(&pe_grad,NOWRITE,"","");
  calc_phase(&pe2_grad,NOWRITE,"","");
  calc_slice(&ss_grad,&p1_rf,WRITE,"gss");
  calc_slice(&ss2_grad,&p2_rf,WRITE,"");
  calc_slice_refocus(&ssr_grad,&ss_grad,WRITE,"gssr");

  /* Equalize refocus gradient durations ****************/
  calc_sim_gradient(&ror_grad,&null_grad,&ssr_grad,0.0,WRITE);

  /* Equalize PE gradient durations *********************/
  calc_sim_gradient(&pe_grad,&pe2_grad,&null_grad,0.0,WRITE);

  /* Set crushing gradient moment ***********************/
  crushm0=fabs(gcrush*tcrush);

  if (spoilflag[0] == 'y') {
    init_generic(&spoil_grad,"spoil",gspoil,tspoil);
    calc_generic(&spoil_grad,WRITE,"gspoil","tspoil");
  }

  /* Create optional prepulse events ********************/
  if (sat[0] == 'y')  create_satbands();
  if (fsat[0] == 'y') create_fatsat();
  if (mt[0] == 'y')   create_mtc();
  if (ir[0] == 'y')   create_inversion_recovery();
  if (diff[0] == 'y') init_diffusion(&diffusion,&diff_grad,"diff",gdiff,tdelta);

  if (diff[0] == 'y') { /* Diffusion encoding is during spin echo preparation */
    spinecho[0]='y';
    putCmd("spinecho='y'");
  }

  if (spinecho[0] == 'y') { /* spin echo preparation */
    shape_rf(&p3_rf,"p3",p3pat,p3,flip3,rof1,rof2);
    calc_rf(&p3_rf,"tpwr3","tpwr3f");
    /* Calculate thk3fact to ensure gss=gss2=gss3 for the choice of  
       p1, p2 and p3 so that the sequence remains robust in the absence
       of correct balancing of slice select and slice refocus gradients */
    thk3fact=p3_rf.bandwidth/p1_rf.bandwidth;
    putvalue("thk3fact",thk3fact);
    init_slice(&ss3_grad,"ss3",thk*thk3fact);
    calc_slice(&ss3_grad,&p3_rf,WRITE,"");
    putvalue("gss3",ss3_grad.ssamp);
    offsetlist(pss,ss3_grad.ssamp,0,freqte,ns,seqcon[1]);
    shapelistte = shapelist(p3_rf.pulseName,ss3_grad.rfDuration,freqte,ns,ss3_grad.rfFraction,seqcon[1]);
    /* Automatically set crushers to avoid unwanted echoes */
    if (autocrush[0] == 'y') {
      if (crushm0 < 0.6*ro_grad.m0) crushm0=0.6*ro_grad.m0;
    }
  }

  /* Make sure crushing in PE dimensions does not refocus signal from 180 */
  pem0 = (pe_grad.m0 > pe2_grad.m0) ? pe_grad.m0 : pe2_grad.m0;
  calc_dephase(&crush_grad,WRITE,crushm0+pem0,"","");
  gcrushr = crush_grad.amp*crushm0/crush_grad.m0;
  gcrushp = crush_grad.amp*(crushm0+pe_grad.m0)/crush_grad.m0;
  gcrushs = crush_grad.amp*(crushm0+pe2_grad.m0)/crush_grad.m0;

  sgl_error_check(sglerror);

  /* Set up frequency offset pulse shape list ***********/
  offsetlist(pss,ss_grad.amp,0,freq90,ns,seqcon[1]);
  offsetlist(pss,ss2_grad.ssamp,0,freq180,ns,seqcon[1]);
  shapelist90 = shapelist(p1_rf.pulseName,ss_grad.rfDuration,freq90,ns,ss_grad.rfFraction,seqcon[1]);
  shapelist180 = shapelist(p2_rf.pulseName,ss2_grad.rfDuration,freq180,ns,ss2_grad.rfFraction,seqcon[1]);

  /* To ensure proper overlap spin and stimulated echoes ensure that the
     middle of the refocusing RF pulse is the centre of the pulse and that
     echoes are formed in the centre of the acquisition window */
  if (ss2_grad.rfFraction != 0.5)
    abort_message(
      "ERROR %s: Refocusing RF pulse must be symmetric (RF fraction = %.2f)",
      seqfil,ss2_grad.rfFraction);
  if (ro_grad.echoFraction != 1)
    abort_message("ERROR %s: Echo Fraction must be 1",seqfil);

  /* Find sum of all events in each half-echo period ****/
  esp = granularity(esp,2*GRADIENT_RES);
  tau1 = ss_grad.rfCenterBack + ssr_grad.duration + crush_grad.duration + ss2_grad.rfCenterFront + GRADIENT_RES;
  tau2 = ss2_grad.rfCenterBack + crush_grad.duration + pe_grad.duration + ro_grad.timeToEcho + GRADIENT_RES; 
  tau3 = ro_grad.timeFromEcho + pe_grad.duration + crush_grad.duration + ss2_grad.rfCenterFront + GRADIENT_RES;
  espmin  = 2*MAX(MAX(tau1,tau2),tau3);       // Minimum echo spacing

  if (minesp[0] == 'y') {
    esp = espmin;
    putvalue("esp",esp);
  }
  if (FP_LT(esp,espmin)) {
    abort_message("ERROR %s: Echo spacing too small, minimum is %.3fms\n",seqfil,espmin*1000);
  }
  te1_delay = esp/2.0 - tau1 + GRADIENT_RES;  // Intra-esp delays
  te2_delay = esp/2.0 - tau2 + GRADIENT_RES;
  te3_delay = esp/2.0 - tau3 + GRADIENT_RES;

  /* Spin echo preparation ******************************/
  if (spinecho[0] == 'y') {
    te = granularity(te,2*GRADIENT_RES);
    te1 = te-kzero*esp;
    tau1 = ss_grad.rfCenterBack + ssr_grad.duration + crush_grad.duration + ss3_grad.duration/2.0 + GRADIENT_RES;
    tau2 = ss3_grad.duration/2.0 + crush_grad.duration + GRADIENT_RES;
    temin = 2*MAX(tau1,tau2);
    /* Diffusion */
    if (diff[0] == 'y') {
      /* granulate tDELTA */
      tDELTA = granularity(tDELTA,GRADIENT_RES);
      /* taudiff is the duration of events between diffusion gradients */
      taudiff = ss3_grad.duration + 2*crush_grad.duration;
      /* set minimum diffusion structure requirements for gradient echo: taudiff, tDELTA, te and minte[0] */
      set_diffusion(&diffusion,taudiff,tDELTA,te1,minte[0]);
      /* set additional diffusion structure requirements for spin echo: tau1 and tau2 */
      set_diffusion_se(&diffusion,tau1,tau2);
      /* calculate the diffusion structure delays.
         address &temin is required in order to update temin accordingly */
      calc_diffTime(&diffusion,&temin);
    }
    /* TE delays */
    if (minte[0] == 'y') {
      te1 = temin;
      te = te1+kzero*esp;
      putvalue("te",te);
    }
    te_delay1 = te1/2 - tau1 + GRADIENT_RES;
    te_delay2 = te1/2 - tau2 + GRADIENT_RES;
    if (FP_LT(te,temin+kzero*esp)) {
      abort_message("ERROR %s: TE too short, minimum TE = %.3f ms\n",seqfil,temin*1000);
    }
  }

  else
    putvalue("te",kzero*esp);       // Return effective TE

  /* Check nsblock, the number of slices blocked together
     (used for triggering and/or inversion recovery) */
  check_nsblock();

  /* Calculate B values *********************************/
  if (ix==1) {
    /* Calculate bvalues according to main diffusion gradients */
    calc_bvalues(&diffusion,"dro","dpe","dsl");
    /* Add components from additional diffusion encoding imaging gradients peculiar to this sequence */
    /* Initialize variables */
    dgro = 0.5*(ror_grad.duration+ro_grad.timeToEcho);        // readout dephase & readout delta
    Gro = ro_grad.m0ref/dgro;                                 // readout dephase & readout gradient strength
    Dgro = dgro+2*crush_grad.duration+ss2_grad.duration+te2_delay+pe_grad.duration; // readout dephase & readout DELTA
    dgss = 0.5*(ss_grad.rfCenterBack+ssr_grad.duration);      // slice & slice refocus delta
    Gss = ss_grad.m0ref/dgss;                                 // slice & slice refocus gradient strength
    Dgss = dgss;                                              // slice & slice refocus DELTA
    dgss2 = (ss2_grad.duration-ss2_grad.tramp)/2.0;           // refocus slice select delta
    Dgss2 = dgss2;                                            // refocus slice select DELTA
    dcrush2 = crush_grad.duration-crush_grad.tramp;           // refocus crusher delta
    Dcrush2 = crush_grad.duration+ss2_grad.duration;          // refocus crusher DELTA
    dcrush3 = crush_grad.duration-crush_grad.tramp;           // spin echo prep crusher delta
    Dcrush3 = crush_grad.duration+ss3_grad.duration;          // spin echo prep crusher DELTA
    dgss3 = (ss3_grad.duration-ss3_grad.tramp)/2.0;           // spin echo prep slice select delta
    Dgss3 = dgss3;                                            // spin echo prep slice select DELTA
    for (i = 0; i < diffusion.nbval; i++)  {
      /* set droval, dpeval and dslval */
      set_dvalues(&diffusion,&droval,&dpeval,&dslval,i);
      /* Readout */
      diffusion.bro[i] += bval(Gro,dgro,Dgro);
      diffusion.bro[i] += bval(gcrushr,dcrush2,Dcrush2);
      diffusion.bro[i] += bval_nested(Gro,dgro,Dgro,gcrushr,dcrush2,Dcrush2);
      /* Phase */
      diffusion.bpe[i] += bval(gcrushp,dcrush2,Dcrush2);
      /* Slice */
      diffusion.bsl[i] += bval(Gss,dgss,Dgss);
      diffusion.bsl[i] += bval(gcrushs,dcrush2,Dcrush2);
      diffusion.bsl[i] += bval(ss2_grad.ssamp,dgss2,Dgss2);
      diffusion.bsl[i] += bval_nested(gcrushs,dcrush2,Dcrush2,ss2_grad.ssamp,dgss2,Dgss2);
      /* Readout/Phase Cross-terms */
      diffusion.brp[i] += bval2(gcrushr,gcrushp,dcrush2,Dcrush2);
      /* Readout/Slice Cross-terms */
      diffusion.brs[i] += bval_cross(Gro,dgro,Dgro,gcrushs,dcrush2,Dcrush2);
      diffusion.brs[i] += bval_cross(Gro,dgro,Dgro,ss2_grad.ssamp,dgss2,Dgss2);
      diffusion.brs[i] += bval2(gcrushr,gcrushs,dcrush2,Dcrush2);
      diffusion.brs[i] += bval_cross(gcrushr,dcrush2,Dcrush2,ss2_grad.ssamp,dgss2,Dgss2);
      /* Slice/Phase Cross-terms */
      diffusion.bsp[i] += bval2(gcrushs,gcrushp,dcrush2,Dcrush2);
      diffusion.bsp[i] += bval_cross(gcrushp,dcrush2,Dcrush2,ss2_grad.ssamp,dgss2,Dgss2);
      if (spinecho[0] == 'y') {
        /* Readout */
        diffusion.bro[i] += bval(crush_grad.amp,dcrush3,Dcrush3);  
        diffusion.bro[i] += bval_nested(gdiff*droval,tdelta,tDELTA,crush_grad.amp,dcrush3,Dcrush3);
        /* Slice */
        diffusion.bsl[i] += bval(ss3_grad.amp,dgss3,Dgss3);
        diffusion.bsl[i] += bval_nested(gdiff*dslval,tdelta,tDELTA,ss3_grad.ssamp,dgss3,Dgss3);
        /* Readout/Slice Cross-terms */
        diffusion.brs[i] += bval_cross(gdiff*dslval,tdelta,tDELTA,crush_grad.amp,dcrush3,Dcrush3);
        diffusion.brs[i] += bval_cross(gdiff*droval,tdelta,tDELTA,ss3_grad.amp,dgss3,Dgss3);
        diffusion.brs[i] += bval_cross(crush_grad.amp,dcrush3,Dcrush3,ss3_grad.amp,dgss3,Dgss3);
        /* Readout/Phase Cross-terms */
        diffusion.brp[i] += bval_cross(gdiff*dpeval,tdelta,tDELTA,crush_grad.amp,dcrush3,Dcrush3);
        /* Slice/Phase Cross-terms */
        diffusion.bsp[i] += bval_cross(gdiff*dpeval,tdelta,tDELTA,ss3_grad.amp,dgss3,Dgss3);
      }
    }  /* End for-all-directions */
    /* Write the values */
    write_bvalues(&diffusion,"bval","bvalue","max_bval");
  }

  /* Minimum TR *****************************************/
  trmin =  ss_grad.rfCenterFront + etl*esp + ro_grad.timeFromEcho + pe_grad.duration + te3_delay + 2*GRADIENT_RES;
  if (spoilflag[0] == 'y') trmin += spoil_grad.duration;

  /* Increase TR if any options are selected ************/
  if (sat[0] == 'y')  trmin += satTime;
  if (fsat[0] == 'y') trmin += fsatTime;
  if (mt[0] == 'y')   trmin += mtTime;
  if (spinecho[0] == 'y')   trmin += te1;
  if (ticks > 0) trmin += GRADIENT_RES;

  /* Adjust for all slices ******************************/
  trmin *= ns;

  /* Inversion recovery *********************************/
  if (ir[0] == 'y') {
    /* tauti is the additional time beyond IR component to be included in ti */
    /* satTime, fsatTime and mtTime all included as those modules will be after IR */
    tauti = satTime + fsatTime + mtTime + GRADIENT_RES + ss_grad.rfCenterFront;
    /* calc_irTime checks ti and returns the time of all IR components */
    trmin += calc_irTime(tauti,trmin,mintr[0],tr,&trtype);
  }

  if (mintr[0] == 'y') {
    tr = trmin;
    putvalue("tr",tr);
  }
  if (FP_LT(tr,trmin)) {
    abort_message("ERROR %s: TR too short, minimum TR = %.3fms\n",seqfil,trmin*1000);
  }

  /* Calculate tr delay *********************************/
  tr_delay = granularity((tr-trmin)/ns,GRADIENT_RES);

  /* Set number of segments for profile or full image ***/
  nseg = prep_profile(profile[0],nseg,&pe_grad,&null_grad);

  pe2_steps = prep_profile(profile[1],nv2,&pe2_grad,&null_grad);
  F_initval(pe2_steps/2.0,vpe2_offset);

  /* Shift DDR for pro **********************************/
  roff = -poffset(pro,ro_grad.roamp);

  /* Adjust experiment time for VnmrJ *******************/
  if (ssc<0) {
    if (seqcon[2] == 's' && seqcon[3]=='s') g_setExpTime(trmean*ntmean*arraydim - ssc*arraydim);
    else if (seqcon[2]=='s') g_setExpTime(trmean*nseg*(ntmean*pe2_steps*arraydim - ssc*arraydim));
    else if (seqcon[3]=='s') g_setExpTime(trmean*pe2_steps*(ntmean*nseg*arraydim - ssc*arraydim));
    else g_setExpTime(trmean*(ntmean*pe_steps*pe2_steps*arraydim - ssc*arraydim));
  }
  else g_setExpTime(trmean*ntmean*nseg*pe2_steps*arraydim + tr*ssc);

  /* PULSE SEQUENCE *************************************/
  status(A);                          // Set status A
  rotate();                           // Set gradient rotation according to psi, phi and theta
  triggerSelect(trigger);             // Select trigger input 1/2/3
  obsoffset(resto);                   // Set spectrometer frequency
  delay(GRADIENT_RES);                // Delay for frequency setting

  initval(fabs(ssc),vssc);            // Compressed steady-state counter
  if (seqcon[2]=='s' && seqcon[3]=='s') assign(zero,vssc); // Zero for standard peloop and pe2loop
  assign(one,vacquire);               // real-time acquire flag

  /* Phase cycle: Alternate 180 phase to cancel residual FID */
  mod2(ct,vphase180);                 // 0101
  dbl(vphase180,vphase180);           // 0202
  add(vphase180,one,vphase180);       // 1313 Phase difference from 90
  add(vphase180,oph,vphase180);

  /* trigger */
  if (ticks > 0) F_initval((double)nsblock,vtrigblock);

  /* Begin phase-encode loop ****************************/       
  peloop2(seqcon[3],pe2_steps,vpe2_steps,vpe2_ctr);

    /* Begin phase-encode loop ****************************/
    peloop(seqcon[2],nseg,vseg,vseg_ctr);

      if (trtype) delay(ns*tr_delay);   // relaxation delay

      /* Compressed steady-states: 1st array & transient, all arrays if ssc is negative */
      if ((ix > 1) && (ssc > 0))
	assign(zero,vssc);
      if (seqcon[2] == 'c')
        sub(vseg_ctr,vssc,vseg_ctr);    // vseg_ctr counts up from -ssc
      else if (seqcon[3] == 'c')
        sub(vpe2_ctr,vssc,vpe2_ctr);    // vpe2_ctr counts up from -ssc
      assign(zero,vssc);
      if (seqcon[2] == 's' && seqcon[3]=='s')
	assign(zero,vacquire);          // Always acquire for non-compressed loop
      else {
        if (seqcon[2] == 'c') {
	  ifzero(vseg_ctr);
            assign(zero,vacquire);      // Start acquiring when vseg_ctr reaches zero
	  endif(vseg_ctr);
        }
        else if (seqcon[3] == 'c') {
	  ifzero(vpe2_ctr);
            assign(zero,vacquire);      // Start acquiring when vpe2_ctr reaches zero
	  endif(vpe2_ctr);
        }
      }
      setacqvar(vacquire);              // Turn on acquire when vacquire is zero

      /* Use standard encoding order for 2nd PE dimension */
      ifzero(vacquire);
        sub(vpe2_ctr,vpe2_offset,vpe2_mult);
      elsenz(vacquire);
        sub(zero,vpe2_offset,vpe2_mult);
      endif(vacquire);

      msloop(seqcon[1],ns,vms_slices,vms_ctr);

        if (!trtype) delay(tr_delay);   // Relaxation delay

        if (ticks > 0) {
          modn(vms_ctr,vtrigblock,vtest);
          ifzero(vtest);                // if the beginning of an trigger block
            xgate(ticks);
            grad_advance(gpropdelay);
            delay(GRADIENT_RES);
          elsenz(vtest);
            delay(GRADIENT_RES);
          endif(vtest);
        }

        sp1on(); delay(GRADIENT_RES); sp1off(); // Scope trigger

        /* Prepulse options ***********************************/
        if (ir[0] == 'y')   inversion_recovery();
        if (sat[0] == 'y')  satbands();
        if (fsat[0] == 'y') fatsat();
        if (mt[0] == 'y')   mtc();

        /* 90 degree pulse ************************************/         
        obspower(p1_rf.powerCoarse);
        obspwrf(p1_rf.powerFine);
        delay(GRADIENT_RES);
        obl_shapedgradient(ss_grad.name,ss_grad.duration,0,0,ss_grad.amp,NOWAIT);   
        delay(ss_grad.rfDelayFront);
        shapedpulselist(shapelist90,ss_grad.rfDuration,oph,rof1,rof2,seqcon[1],vms_ctr);
        delay(ss_grad.rfDelayBack);

        /* Spin echo preparation ******************************/
        if (spinecho[0] == 'y') {
          obl_shapedgradient(ssr_grad.name,ssr_grad.duration,0.0,0.0,-ssr_grad.amp,WAIT);
          if (diff[0] == 'y') {
            delay(diffusion.d1);
            diffusion_dephase(&diffusion,dro,dpe,dsl);
            delay(diffusion.d2);
          }
          else
            delay(te_delay1);
          obspower(p3_rf.powerCoarse);
          obspwrf(p3_rf.powerFine);
          obl_shapedgradient(crush_grad.name,crush_grad.duration,crush_grad.amp,0.0,0.0,WAIT);
          obl_shapedgradient(ss3_grad.name,ss3_grad.duration,0,0,ss3_grad.amp,NOWAIT);
          delay(ss3_grad.rfDelayFront);
          shapedpulselist(shapelistte,ss3_grad.rfDuration,vphase180,rof1,rof2,seqcon[1],vms_ctr);
          delay(ss3_grad.rfDelayBack);
          obl_shapedgradient(crush_grad.name,crush_grad.duration,crush_grad.amp,0.0,0.0,WAIT);
          if (diff[0] == 'y') {
            delay(diffusion.d3);
            diffusion_rephase(&diffusion,dro,dpe,dsl);
            delay(diffusion.d4);
          }
          else
            delay(te_delay2);
          delay(ss_grad.duration/2.0);
          delay(te1_delay);
          obspower(p2_rf.powerCoarse);
          obspwrf(p2_rf.powerFine);
          obl_shapedgradient(ror_grad.name,ror_grad.duration,ror_grad.amp,0.0,0.0,WAIT);
        }

        else {
          /* Read dephase and Slice refocus */
          obl_shapedgradient(ssr_grad.name,ssr_grad.duration,ror_grad.amp,0.0,-ssr_grad.amp,WAIT);
          /* First half-TE delay */
          obspower(p2_rf.powerCoarse);
          obspwrf(p2_rf.powerFine);
          delay(te1_delay);
        }
	
        F_initval(etl,vetl);
        loop(vetl,vetl_ctr);

          mult(vseg_ctr,vetl,vpe_ctr);
          add(vpe_ctr,vetl_ctr,vpe_ctr);
          getelem(t1,vpe_ctr,vpe_mult);

          /* 180 degree pulse *********************************/
          obl_shapedgradient(crush_grad.name,crush_grad.duration,gcrushr,gcrushp,gcrushs,WAIT);
          obl_shapedgradient(ss2_grad.name,ss2_grad.duration,0,0,ss2_grad.amp,NOWAIT);   
          delay(ss2_grad.rfDelayFront); 
          shapedpulselist(shapelist180,ss2_grad.rfDuration,vphase180,rof1,rof2,seqcon[1],vms_ctr);
          delay(ss2_grad.rfDelayBack);   
          obl_shapedgradient(crush_grad.name,crush_grad.duration,gcrushr,gcrushp,gcrushs,WAIT);

          /* Second half-TE period ****************************/
          delay(te2_delay);
	 
          /* Phase-encode gradient ****************************/
          pe2_shapedgradient(pe_grad.name,pe_grad.duration,0,0,0,-pe_grad.increment,-pe2_grad.increment,vpe_mult,vpe2_mult,WAIT);

          /* Readout gradient *********************************/
          obl_shapedgradient(ro_grad.name,ro_grad.duration,ro_grad.roamp,0,0,NOWAIT);
          delay(ro_grad.atDelayFront-alfa);

          /* Acquire data *************************************/
          startacq(alfa);
          acquire(np,1.0/sw);
          endacq();

          delay(ro_grad.atDelayBack);

          /* Rewinding phase-encode gradient ******************/
          pe2_shapedgradient(pe_grad.name,pe_grad.duration,0,0,0,pe_grad.increment,pe2_grad.increment,vpe_mult,vpe2_mult,WAIT);

          /* Second half-TE delay *****************************/
          delay(te3_delay);

        endloop(vetl_ctr);

        if (spoilflag[0] == 'y') 
          obl_shapedgradient(spoil_grad.name,spoil_grad.duration,spoil_grad.amp,spoil_grad.amp,spoil_grad.amp,WAIT);

      endmsloop(seqcon[1],vms_ctr);

    endpeloop(seqcon[2],vseg_ctr);

  endpeloop(seqcon[3],vpe2_ctr);

  /* Inter-image delay **********************************/
  sub(ntrt,ct,vtrimage);
  decr(vtrimage);
  ifzero(vtrimage);
    delay(trimage);
  endif(vtrimage);
}
Ejemplo n.º 4
0
void pulsesequence()
{
  /* Internal variable declarations *************************/
  double  freqEx[MAXNSLICE];
  double  pespoil_amp,maxgradtime,spoilMoment,perTime,pe2_offsetamp,tau1,te_delay,tr_delay;
  double  te2=0.0,te3=0.0,te2min,te3min,tau2,tau3,te2_delay,te3_delay=0;
  char    minte2[MAXSTR],minte3[MAXSTR],spoilflag[MAXSTR],perName[MAXSTR];
  int     sepSliceRephase,sepReadRephase=0,readrev,table,shapeEx;
  int     i;

  /* Real-time variables used in this sequence **************/
  int  vpe_steps    = v1;      // Number of PE steps
  int  vpe_ctr      = v2;      // PE loop counter
  int  vms_slices   = v3;      // Number of slices
  int  vms_ctr      = v4;      // Slice loop counter
  int  vpe_offset   = v5;      // PE/2 for non-table offset
  int  vpe_mult     = v6;      // PE multiplier, ranges from -PE/2 to PE/2
  int  vper_mult    = v7;      // PE rewinder multiplier; turn off rewinder when 0
  int  vpe2_steps   = v8;      // Number of PE2 steps
  int  vpe2_ctr     = v9;      // PE2 loop counter
  int  vpe2_mult    = v10;     // PE2 multiplier
  int  vpe2_offset  = v11;     // PE2/2 for non-table offset
  int  vpe2r_mult   = v12;     // PE2 rewinder multiplier
  int  vssc         = v13;     // Compressed steady-states
  int  vacquire     = v14;     // Argument for setacqvar, to skip steady state acquires
  int  vrfspoil_ctr = v15;     // RF spoil counter
  int  vrfspoil     = v16;     // RF spoil multiplier
  int  vtrimage     = v17;     // Counts down from nt, trimage delay when 0
  int  vne          = v18;     // Number of echoes
  int  vne_ctr      = v19;     // Echo loop counter
  int  vneindex     = v20;     // Echo index, odd or even
  int  vnelast      = v21;     // Check for last echo
  int  vtrigblock   = v22;     // Number of slices per trigger block

  /* Initialize paramaters **********************************/
  init_mri();

  getstr("spoilflag",spoilflag);
  te2=getval("te2");
  te3=getval("te3");
  getstr("minte2",minte2);
  getstr("minte3",minte3);
  readrev=(int)getval("readrev");

  /*  Check for external PE table ***************************/
  table = 0;
  if (strcmp(petable,"n") && strcmp(petable,"N") && strcmp(petable,"")) {
    loadtable(petable);
    table = 1;
  }

  /* Set Rcvr/Xmtr phase increments for RF Spoiling ********/
  /* Ref:  Zur, Y., Magn. Res. Med., 21, 251, (1991) *******/
  if (rfspoil[0] == 'y') {
    rcvrstepsize(rfphase);
    obsstepsize(rfphase);
  }

  /* Initialize gradient structures *************************/
  shape_rf(&p1_rf,"p1",p1pat,p1,flip1,rof1,rof2 );   // excitation pulse
  init_slice(&ss_grad,"ss",thk);                     // slice select gradient
  init_slice_refocus(&ssr_grad,"ssr");               // slice refocus gradient
  init_readout(&ro_grad,"ro",lro,np,sw);             // readout gradient
  init_readout_refocus(&ror_grad,"ror");             // dephase gradient
  init_phase(&pe_grad,"pe",lpe,nv);                  // phase encode gradient
  init_phase(&pe2_grad,"pe2",lpe2,nv2);              // 2nd phase encode gradient
  init_dephase(&spoil_grad,"spoil");                 // optimized spoiler
  init_dephase(&ref_grad,"ref");                     // readout rephase

  /* RF Calculations ****************************************/
  calc_rf(&p1_rf,"tpwr1","tpwr1f");

  /* Gradient calculations **********************************/
  calc_slice(&ss_grad,&p1_rf,WRITE,"gss");
  calc_slice_refocus(&ssr_grad, &ss_grad, WRITE,"gssr");
  calc_readout(&ro_grad, WRITE,"gro","sw","at");
  calc_readout_refocus(&ror_grad,&ro_grad,NOWRITE,"gror");
  calc_phase(&pe_grad, NOWRITE,"gpe","tpe");
  calc_phase(&pe2_grad,NOWRITE,"gpe2","");
  calc_dephase(&ref_grad,WRITE,ro_grad.m0,"","");

  if (spoilflag[0] == 'y') {
    spoilMoment = ro_grad.acqTime*ro_grad.roamp;   // Optimal spoiling is at*gro for 2pi per pixel
    spoilMoment -= ro_grad.m0def;                  // Subtract partial spoiling from back half of readout
    if (perewind[0] == 'y') 
      calc_dephase(&spoil_grad,NOWRITE,spoilMoment,"gspoil","tspoil");
    else
      calc_dephase(&spoil_grad,WRITE,spoilMoment,"gspoil","tspoil");
  }

  /* Is TE long enough for separate slab refocus? *******/
  maxgradtime = MAX(ror_grad.duration,pe_grad.duration);
  if (spoilflag[0] == 'y')
    maxgradtime = MAX(maxgradtime,spoil_grad.duration);
  tau1 = ss_grad.rfCenterBack + ssr_grad.duration + maxgradtime + alfa + ro_grad.timeToEcho + 4e-6;
  if ((te >= tau1) && (minte[0] != 'y')) sepSliceRephase = 1; // Set flag for separate slice rephase
  else {
    sepSliceRephase = 0;
    pe2_grad.areaOffset = ss_grad.m0ref;                 // Add slab refocus on pe2 axis
    calc_phase(&pe2_grad,NOWRITE,"gpe2","");             // Recalculate pe2 to include slab refocus
  }

  /* Equalize refocus and PE gradient durations *************/
  pespoil_amp = 0.0;
  perTime = 0.0;
  if ((perewind[0] == 'y') && (spoilflag[0] == 'y')) {   // All four must be single shape
    if (ror_grad.duration > spoil_grad.duration) {       // calc_sim first with ror
      calc_sim_gradient(&pe_grad,&pe2_grad,&ror_grad,tpemin,WRITE);
      calc_sim_gradient(&ror_grad,&spoil_grad,&null_grad,tpemin,NOWRITE);
    } else {                                             // calc_sim first with spoil
      calc_sim_gradient(&pe_grad,&pe2_grad,&spoil_grad,tpemin,WRITE);
      calc_sim_gradient(&ror_grad,&spoil_grad,&null_grad,tpemin,WRITE);      
    }
    strcpy(perName,pe_grad.name);
    perTime = pe_grad.duration;
  } else {                      // post-acquire shape will be either pe or spoil, but not both
    calc_sim_gradient(&ror_grad,&pe_grad,&pe2_grad,tpemin,WRITE);
    if ((perewind[0] == 'y') && (spoilflag[0] == 'n')) {     // Rewinder, no spoiler
      strcpy(perName,pe_grad.name);
      perTime = pe_grad.duration;
      spoil_grad.amp = 0.0;
    } else if ((perewind[0] == 'n') && (spoilflag[0] == 'y')) {  // Spoiler, no rewinder
      strcpy(perName,spoil_grad.name);
      perTime = spoil_grad.duration;
      pespoil_amp = spoil_grad.amp;      // Apply spoiler on PE & PE2 axis if no rewinder
    }
  }
  pe2_offsetamp = sepSliceRephase ? 0.0 : pe2_grad.offsetamp;  // pe2 slab refocus

  /* Create optional prepulse events ************************/
  if (sat[0] == 'y')  create_satbands();
  if (fsat[0] == 'y') create_fatsat();
  if (mt[0] == 'y')   create_mtc();
  if (ir[0] == 'y')   create_inversion_recovery();

  /* Set up frequency offset pulse shape list ********/   	
  offsetlist(pss,ss_grad.ssamp,0,freqEx,ns,seqcon[1]);
  shapeEx = shapelist(p1_rf.pulseName,ss_grad.rfDuration,freqEx,ns,ss_grad.rfFraction,seqcon[1]);
  
  /* Check that all Gradient calculations are ok ************/
  sgl_error_check(sglerror);

  /* Min TE ******************************************/
  tau1 = ss_grad.rfCenterBack + pe_grad.duration + alfa + ro_grad.timeToEcho;
  tau1 += (sepSliceRephase) ? ssr_grad.duration : 0.0;   // Add slice refocusing if separate event

  temin = tau1 + 4e-6;  /* ensure that te_delay is at least 4us */
  if (minte[0] == 'y') {
    te = temin;
    putvalue("te",te);
  }
  if (te < temin) {
    abort_message("TE too short.  Minimum TE= %.2fms\n",temin*1000+0.005);   
  }
  te_delay = te - tau1;

  /* Min TE2 *****************************************/
  tau2 = (readrev) ? 2*ro_grad.timeFromEcho+alfa : ro_grad.duration+ref_grad.duration;
  te2min = tau2 + 4e-6;
  if (minte2[0] == 'y') {
    te2 = te2min;
    putvalue("te2",te2);
  }
  if (te2 < te2min) {
    abort_message("TE2 too short.  Minimum TE2= %.2fms\n",te2min*1000+0.005);
  }

  if (readrev) te2_delay = te2 - tau2;
  else {
    tau2 = ro_grad.duration + 2*ror_grad.duration;
    if (te2 >= tau2) {
      sepReadRephase = 1; // Set flag for separate read rephase
      te2_delay = te2 - ro_grad.duration - 2*ror_grad.duration;
    } else {
      sepReadRephase = 0;
      if (te2 > te2min+4e-6) {
        ref_grad.duration = granularity(te2-ro_grad.duration-8e-6,GRADIENT_RES);
        ref_grad.calcFlag = AMPLITUDE_FROM_MOMENT_DURATION;
        calc_dephase(&ref_grad,WRITE,ro_grad.m0,"","");
      }
      te2_delay = te2 - ro_grad.duration - ref_grad.duration;
    }
  }

  /* Min TE3 *****************************************/
  if (readrev) {  
    tau3 = 2*ro_grad.timeToEcho + alfa;
    te3min = tau3 + 4e-6;
    if (minte3[0] == 'y') {
      te3 = te3min;
      putvalue("te3",te3);
    }
    if (te3 < te3min) {
      abort_message("TE3 too short.  Minimum TE3= %.2fms\n",te3min*1000+0.005);
    }
    te3_delay = te3 - tau3;
  }

  /* Now set the TE array accordingly */
  putCmd("TE = 0"); /* Re-initialize TE */
  putCmd("TE[1] = %f",te*1000);
  if (readrev) {
    for (i=1;i<ne;i++) {
      if (i%2 == 0) putCmd("TE[%d] = TE[%d]+%f",i+1,i,te3*1000);
      else putCmd("TE[%d] = TE[%d]+%f",i+1,i,te2*1000);
    }
  } else {
    for (i=1;i<ne;i++) putCmd("TE[%d] = TE[%d]+%f",i+1,i,te2*1000);
  }

  /* Check nsblock, the number of slices blocked together
     (used for triggering and/or inversion recovery) */
  check_nsblock();

  /* Min TR ******************************************/
  trmin  = ss_grad.duration + te_delay + pe_grad.duration + ne*ro_grad.duration + perTime + 8e-6;
  trmin += (sepSliceRephase) ? ssr_grad.duration : 0.0;   // Add slice refocusing if separate event
  if (readrev) trmin += (ne/2)*te2_delay + ((ne-1)/2)*te3_delay;
  else trmin += (sepReadRephase) ? (ne-1)*(te2_delay+2*ror_grad.duration) : (ne-1)*(te2_delay+ref_grad.duration);

  /* Increase TR if any options are selected *********/
  if (sat[0] == 'y')  trmin += satTime;
  if (fsat[0] == 'y') trmin += fsatTime;
  if (mt[0] == 'y')   trmin += mtTime;
  if (ticks > 0) trmin += 4e-6;

  /* Adjust for all slices ***************************/
  trmin *= ns;

  /* Inversion recovery *********************************/
  if (ir[0] == 'y') {
    /* tiaddTime is the additional time beyond IR component to be included in ti */
    /* satTime, fsatTime and mtTime all included as those modules will be after IR */
    tiaddTime = satTime + fsatTime + mtTime + 4e-6 + ss_grad.rfCenterFront;
    /* calc_irTime checks ti and returns the time of all IR components */
    trmin += calc_irTime(tiaddTime,trmin,mintr[0],tr,&trtype);
  }

  if (mintr[0] == 'y') {
    tr = trmin;
    putvalue("tr",tr);
  }
  if (FP_LT(tr,trmin)) {
    abort_message("TR too short.  Minimum TR = %.2fms\n",trmin*1000+0.005);
  }

  /* Calculate tr delay */
  tr_delay = granularity((tr-trmin)/ns,GRADIENT_RES);

  /* Set pe_steps for profile or full image **********/   	
  pe_steps = prep_profile(profile[0],nv,&pe_grad,&per_grad);
  F_initval(pe_steps/2.0,vpe_offset);

  pe2_steps = prep_profile(profile[1],nv2,&pe2_grad,&null_grad);
  F_initval(pe2_steps/2.0,vpe2_offset);

  /* Shift DDR for pro *******************************/   	
  roff = -poffset(pro,ro_grad.roamp);

  /* Adjust experiment time for VnmrJ *******************/
  if (ssc<0) {
    if (seqcon[2]=='s') g_setExpTime(trmean*ntmean*pe_steps*pe2_steps*arraydim);
    else if (seqcon[3]=='s') g_setExpTime(trmean*pe2_steps*(ntmean*pe_steps*arraydim - ssc*arraydim));
    else g_setExpTime(trmean*(ntmean*pe_steps*pe2_steps*arraydim - ssc*arraydim));
  } else {
    if (seqcon[2]=='s') g_setExpTime(trmean*ntmean*pe_steps*pe2_steps*arraydim);
    else g_setExpTime(trmean*ntmean*pe_steps*pe2_steps*arraydim + tr*ssc);
  }

  /* Return parameters to VnmrJ */
  putvalue("tror",ror_grad.duration);  // ROR duration
  putvalue("gpe",pe_grad.peamp);       // PE max grad amp
  putvalue("gss",ss_grad.ssamp);       // Excitation slice grad
  putvalue("gro",ro_grad.roamp);       // RO grad

  /* PULSE SEQUENCE ***************************************/
  status(A);
  rotate();
  triggerSelect(trigger);       // Select trigger input 1/2/3
  obsoffset(resto);
  delay(4e-6);
  initval(fabs(ssc),vssc);      // Compressed steady-state counter
  if (seqcon[2]=='s') assign(zero,vssc); // Zero for standard peloop
  assign(zero,vrfspoil_ctr);    // RF spoil phase counter
  assign(zero,vrfspoil);        // RF spoil multiplier
  assign(one,vacquire);         // real-time acquire flag
  setacqvar(vacquire);          // Turn on acquire when vacquire is zero 

  /* trigger */
  if (ticks > 0) F_initval((double)nsblock,vtrigblock);

  /* Begin phase-encode loop ****************************/       
  peloop2(seqcon[3],pe2_steps,vpe2_steps,vpe2_ctr);

    /* Begin phase-encode loop ****************************/       
    peloop(seqcon[2],pe_steps,vpe_steps,vpe_ctr);

      if (trtype) delay(ns*tr_delay);   // relaxation delay

      /* Compressed steady-states: 
         1st array & transient, all arrays if ssc is negative */
      if ((ix > 1) && (ssc > 0))
        assign(zero,vssc);
      sub(vpe_ctr,vssc,vpe_ctr);  // vpe_ctr counts up from -ssc
      assign(zero,vssc);
      if (seqcon[2] == 's')
        assign(zero,vacquire);    // Always acquire for non-compressed loop
      else {
        ifzero(vpe_ctr);
          assign(zero,vacquire);  // Start acquiring when vpe_ctr reaches zero
        endif(vpe_ctr);
      }

      /* Use standard encoding order for 2nd PE dimension */
      ifzero(vacquire);
        sub(vpe2_ctr,vpe2_offset,vpe2_mult);
      elsenz(vacquire);
        sub(zero,vpe2_offset,vpe2_mult);
      endif(vacquire);

      /* Set rcvr/xmtr phase for RF spoiling *******************/
      if (rfspoil[0] == 'y') {
        incr(vrfspoil_ctr);                    // vrfspoil_ctr = 1  2  3  4  5  6
        add(vrfspoil,vrfspoil_ctr,vrfspoil);   // vrfspoil =     1  3  6 10 15 21
        xmtrphase(vrfspoil);
        rcvrphase(vrfspoil);
      }

      /* Read external kspace table if set ******************/       
      if (table)
        getelem(t1,vpe_ctr,vpe_mult);
      else {
        ifzero(vacquire);
          sub(vpe_ctr,vpe_offset,vpe_mult);
        elsenz(vacquire);
          sub(zero,vpe_offset,vpe_mult);  // Hold PE mult at initial value for steady states
        endif(vacquire);
      }

      /* PE rewinder follows PE table; zero if turned off ***/       
      if (perewind[0] == 'y') {
        assign(vpe_mult,vper_mult);
        assign(vpe2_mult,vpe2r_mult);
      }
      else {
        assign(zero,vper_mult);
        assign(zero,vpe2r_mult);
      }

      /* Begin multislice loop ******************************/       
      msloop(seqcon[1],ns,vms_slices,vms_ctr);

        if (!trtype) delay(tr_delay);   // Relaxation delay

        if (ticks > 0) {
          modn(vms_ctr,vtrigblock,vtest);
          ifzero(vtest);                // if the beginning of an trigger block
            xgate(ticks);
            grad_advance(gpropdelay);
            delay(4e-6);
          elsenz(vtest);
            delay(4e-6);
          endif(vtest);
        }

        /* TTL scope trigger **********************************/       
        sp1on(); delay(4e-6); sp1off();

        /* Prepulse options ***********************************/       
        if (ir[0] == 'y')   inversion_recovery();
        if (sat[0] == 'y')  satbands();
        if (fsat[0] == 'y') fatsat();
        if (mt[0] == 'y')   mtc();

        /* Slice select RF pulse ******************************/ 
        obspower(p1_rf.powerCoarse);
        obspwrf(p1_rf.powerFine);
        delay(4e-6);
        obl_shapedgradient(ss_grad.name,ss_grad.duration,0,0,ss_grad.amp,NOWAIT);
        delay(ss_grad.rfDelayFront);
        shapedpulselist(shapeEx,ss_grad.rfDuration,oph,rof1,rof2,seqcon[1],vms_ctr);
        delay(ss_grad.rfDelayBack);

       /* Phase encode, refocus, and dephase gradient ********/
        if (sepSliceRephase) {                // separate slice refocus gradient
          obl_shapedgradient(ssr_grad.name,ssr_grad.duration,0,0,-ssr_grad.amp,WAIT);
          delay(te_delay);                    // delay between slab refocus and pe
          pe2_shapedgradient(pe_grad.name,pe_grad.duration,-ror_grad.amp,0,-pe2_offsetamp,
            -pe_grad.increment,-pe2_grad.increment,vpe_mult,vpe2_mult,WAIT);
        } else {
	  pe2_shapedgradient(pe_grad.name,pe_grad.duration,-ror_grad.amp,0,-pe2_offsetamp,
            -pe_grad.increment,-pe2_grad.increment,vpe_mult,vpe2_mult,WAIT);
          delay(te_delay);                    // delay after refocus/pe
        }

        F_initval(ne,vne);
        loop(vne,vne_ctr);

          if (readrev) {
            mod2(vne_ctr,vneindex);
            ifzero(vneindex);
              /* Shift DDR for pro *******************************/
              roff = -poffset(pro,ro_grad.roamp);
              /* Readout gradient ********************************/
              obl_shapedgradient(ro_grad.name,ro_grad.duration,ro_grad.amp,0,0,NOWAIT);
              delay(ro_grad.atDelayFront);
              /* Acquisition ***************************************/
              startacq(alfa);
              acquire(np,1.0/sw);
              delay(ro_grad.atDelayBack);
              endacq();
              sub(vne,vne_ctr,vnelast);
              sub(vnelast,one,vnelast);
              ifzero(vnelast);
              elsenz(vnelast);
                delay(te2_delay);
              endif(vnelast);
            elsenz(vneindex);
              /* Shift DDR for pro *******************************/
              roff = -poffset(pro,-ro_grad.roamp);
              /* Readout gradient ********************************/
              obl_shapedgradient(ro_grad.name,ro_grad.duration,-ro_grad.amp,0,0,NOWAIT);
              delay(ro_grad.atDelayFront);
              /* Acquisition ***************************************/
              startacq(alfa);
              acquire(np,1.0/sw);
              delay(ro_grad.atDelayBack);
              endacq();
              sub(vne,vne_ctr,vnelast);
              sub(vnelast,one,vnelast);
              ifzero(vnelast);
              elsenz(vnelast);
                delay(te3_delay);
              endif(vnelast);
            endif(vneindex);
          } else {
            /* Shift DDR for pro *******************************/
            roff = -poffset(pro,ro_grad.roamp);
            /* Readout gradient ********************************/
            obl_shapedgradient(ro_grad.name,ro_grad.duration,ro_grad.amp,0,0,NOWAIT);
            delay(ro_grad.atDelayFront);
            /* Acquisition ***************************************/
            startacq(alfa);
            acquire(np,1.0/sw);
            delay(ro_grad.atDelayBack);
            endacq();
            sub(vne,vne_ctr,vnelast);
            sub(vnelast,one,vnelast);
            ifzero(vnelast);
            elsenz(vnelast);
              if (sepReadRephase) {
                obl_shapedgradient(ror_grad.name,ror_grad.duration,-ror_grad.amp,0,0,WAIT);
                delay(te2_delay);
                obl_shapedgradient(ror_grad.name,ror_grad.duration,-ror_grad.amp,0,0,WAIT);
              } else {
                obl_shapedgradient(ref_grad.name,ref_grad.duration,-ref_grad.amp,0,0,WAIT);
                delay(te2_delay);
              }
            endif(vnelast);
          }

        endloop(vne_ctr);

        /* Rewind / spoiler gradient **************************/
        if ((perewind[0] == 'y') || (spoilflag[0] == 'y')) {
          pe2_shapedgradient(perName,perTime,spoil_grad.amp,pespoil_amp,pespoil_amp,
            pe_grad.increment,pe2_grad.increment,vper_mult,vpe2r_mult,WAIT);
        }

      endmsloop(seqcon[1],vms_ctr);

    endpeloop(seqcon[2],vpe_ctr);

  endpeloop(seqcon[3],vpe2_ctr);

  /* Inter-image delay **********************************/
  sub(ntrt,ct,vtrimage);
  decr(vtrimage);
  ifzero(vtrimage);
    delay(trimage);
  endif(vtrimage);
}