void calc_grad_duty(double time) { double gradenergy[3],gradduty[3]; double mult=1.0; double currentlimit,RMScurrentlimit; double sglduty,dutylimit; int checksilent,r,nrcvrs,arraydim,error=0; char rcvrs[MAXSTR]; currentlimit = getval("currentlimit"); RMScurrentlimit = getval("RMScurrentlimit"); getRealSetDefault(GLOBAL, "sglduty", &sglduty,0.0); dutylimit = RMScurrentlimit/currentlimit; checksilent = option_check("checksilent"); /* Adjust array dimenstion for multiple receivers */ nrcvrs = 0; getstr("rcvrs",rcvrs); arraydim = getvalnwarn("arraydim"); for (r = 0; r < strlen(rcvrs); r++) { if (rcvrs[r] == 'y') nrcvrs++; } arraydim /= nrcvrs; if (seqcon[2] == 'c') mult *= nv; if (seqcon[3] == 'c') mult *= nv2; if (!checkflag) mult *= arraydim; getgradpowerintegral(gradenergy); gradduty[0] = sqrt(gradenergy[0]/(mult*time)); gradduty[1] = sqrt(gradenergy[1]/(mult*time)); gradduty[2] = sqrt(gradenergy[2]/(mult*time)); if (sglduty && ((checkflag && !checksilent) || (!checksilent && ix == arraydim))) { text_message("Grad energy X: %.3g Grad energy Y: %.3g Grad energy Z: %.3g",gradenergy[0],gradenergy[1],gradenergy[2]); text_message("Grad duty X: %.3g%% Grad duty Y: %.3g%% Grad duty Z: %.3g%%",100*gradduty[0],100*gradduty[1],100*gradduty[2]); } if ((gradduty[0] > dutylimit) && ((checkflag && !checksilent) || (!checksilent && ix == arraydim))) { text_message("%s: X gradient duty cycle %5.1f%% exceeds allowed limit of %5.1f%%",seqfil,100*gradduty[0],100*dutylimit); error = 1; } if ((gradduty[1] > dutylimit) && ((checkflag && !checksilent) || (!checksilent && ix == arraydim))) { text_message("%s: Y gradient duty cycle %5.1f%% exceeds allowed limit of %5.1f%%",seqfil,100*gradduty[1],100*dutylimit); error = 1; } if ((gradduty[2] > dutylimit) && ((checkflag && !checksilent) || (!checksilent && ix == arraydim))) { text_message("%s: Z gradient duty cycle %5.1f%% exceeds allowed limit of %5.1f%%",seqfil,100*gradduty[2],100*dutylimit); error = 1; } if (error) { if (sglduty) warn_message("%s: Duty cycle exceeds allowed limit",seqfil); else abort_message("%s: Duty cycle exceeds allowed limit",seqfil); } }
void calc_readout_rampsamp(READOUT_GRADIENT_T *grad, double *dwell, double *minskip, int *npr) { double this_gro, skip, skip_m0, dwint, dw, dwflat; double ramp_t, ramp_m0, flat_t, flat_m0, dwell_sum, rt; int npro, np_ramp, np_flat; int pt, pt2; GENERIC_GRADIENT_T gg; /* Calculate gradient amplitude based on FOV and sw */ this_gro = sw/grad->gamma/lro; grad->amp = this_gro; /* Nominal dwell time is 1/sw */ dw = granularity(1/sw,1/epi_grad.ddrsr); dwflat = dw; /* we may later end up with different dw on flat part */ dwint = dw*grad->amp; /* Gradient Integral per point */ npro = (int) np/2; /* Calculate the area that is skipped for phase encoding blip */ skip = *minskip; if (skip == 0) skip = dw + getval("aqtm")-at; /* Need at least a dwell time at the end */ skip_m0 = skip*(skip*grad->slewRate)/2; /* Area of that skipped part */ /* Calculate the ramp time */ grad->tramp = granularity(grad->amp/grad->slewRate,grad->resolution); grad->slewRate = grad->amp/grad->tramp; ramp_m0 = grad->tramp*grad->amp/2; np_ramp = (int) ((ramp_m0 - skip_m0)/dwint); /* Check that we even need points on the flat part */ if (np_ramp >= npro/2) { np_ramp = npro/2 - 1; /* Center two points considered "flat part" */ /* We may not need to go all the way to full gradient in order to fit all points on ramp; adjust amplitude */ ramp_m0 = np_ramp*dwint + skip_m0; grad->tramp = granularity(sqrt(2*ramp_m0/grad->slewRate),grad->resolution); grad->amp = grad->tramp * grad->slewRate; /* recalculate ramp area and np_ramp */ ramp_m0 = grad->tramp*grad->amp/2; /* Now adjust the dwell time necessary on flat part with this amplitude */ dwflat = granularity(dwint/grad->amp,1/epi_grad.ddrsr); } np_flat = npro - 2*np_ramp; /* How long must the flat part be? */ grad->duration = granularity((np_flat-1)*dwflat,grad->resolution); /* Due to rounding of gradient duration, we may be able to fit more points on flat part */ while (grad->duration - (np_flat-1)*dwflat >= 2*dwflat) { np_flat += 2; np_ramp -= 1; } /* Now add the ramp times to the total duration */ grad->duration += 2*grad->tramp; /* Use a generic to calculate the actual shape, ie fill in dataPoints */ initGeneric(&gg); gg.amp = grad->amp; gg.duration = grad->duration; gg.tramp = grad->tramp; gg.slewRate = grad->slewRate; gg.calcFlag = MOMENT_FROM_DURATION_AMPLITUDE_RAMP; gg.writeToDisk = FALSE; calcGeneric(&gg); if (gg.error != ERR_NO_ERROR) { printf("Temporary generic gradient: \n"); displayGeneric(&gg); abort_message("Problem calculating RO gradient with ramp sampling"); } grad->m0 = gg.m0; grad->m1 = gg.m1; grad->m0ref = grad->m0/2; grad->numPoints = gg.numPoints; free(grad->dataPoints); // release original array grad->dataPoints = gg.dataPoints; /* Recalculate the skipped part with new gradient integral */ skip_m0 = (grad->m0 - dwint*(np/2-1))/2; skip = sqrt(2*skip_m0/grad->slewRate); /* Skip rampsampling if gradient is longer than necessary */ if ((grad->m0-ramp_m0*2) > npro*dwint) { warn_message("The gradient is too long, no ramp sampling"); for (pt = 0; pt < npro; pt++) { dwell[pt] = dwflat; } *minskip = grad->tramp; return; } /* Calculate where we start acquisition on the ramp */ /* Do we have extra integral on flat part? */ flat_m0 = (grad->m0 - 2*ramp_m0) - ((np_flat-1)*dwflat*grad->amp); /* if yes, then what is the duration of this? */ if (flat_m0 > 0) flat_t = (grad->duration - 2*grad->tramp) / ((np_flat - 1)*dwflat); else flat_t = 0; /* Double-check that gradient is long enough */ if (flat_m0 < -1e-9) abort_message("Gradient integral too small %f vs %f (%f) G/cm*us", (grad->m0-2*ramp_m0)*1e6,(np_flat-1)*dwint*1e6,flat_m0*1e9); /* If extra integral > 2*dwint, we could move a point from each ramp up to the flat */ if (flat_m0 > 2*dwint) warn_message("Gradient flat part longer than necessary with ramp sampling, check gradient calculation\n"); /**************************************************************************/ /* Calculate dwell times */ /**************************************************************************/ dwell_sum = 0; /* Start at top of ramp and walk down ramp */ pt = np_ramp - 1; rt = grad->tramp; /* Top point on ramp first, it may use a bit of time from the flat part */ if (flat_m0 > 0) { ramp_m0 -= (dwint - flat_m0/2); /* area at ramp at previous point */ flat_t = (grad->duration - 2*grad->tramp) - ((np_flat - 1)*dwflat); /* extra duration at top */ flat_t /= 2; /* divide evenly between both ramps */ } else { ramp_m0 -= dwint; /* area at ramp at previous point */ flat_t = 0; } ramp_t = sqrt(2*ramp_m0/grad->slewRate); /* time at ramp at previous point */ dwell[pt] = granularity(flat_t + (rt-ramp_t), 1/epi_grad.ddrsr); dwell_sum += dwell[pt]; ramp_t = rt - (dwell[pt] - flat_t); /* correct for rounding of dwell */ /* Do all the rest of the points on the ramp */ for(pt = np_ramp-2; pt >= 0; pt--) { rt = ramp_t; /* remember current time */ ramp_m0 -= dwint; /* total area of ramp at previous point */ if (ramp_m0 <= 0) { if (fabs(ramp_m0) > dwint*0.01) abort_message("problem: ramp_m0 negative %f\n",ramp_m0*1000); ramp_t = 0; } else ramp_t = sqrt(2*ramp_m0/grad->slewRate); /* time at previous point */ dwell[pt] = granularity(rt-ramp_t,1/epi_grad.ddrsr); dwell_sum += dwell[pt]; ramp_t = rt - dwell[pt]; } skip = ramp_t; dwell_sum += skip; for (pt = np_ramp; pt < np_ramp+np_flat-1; pt++) { /* Flat part */ dwell[pt] = dwflat; dwell_sum += dwflat; } pt2 = np_ramp-1; for (pt = np_ramp+np_flat-1; pt < np_ramp*2 + np_flat - 1; pt++) { dwell[pt] = dwell[pt2--]; dwell_sum += dwell[pt]; } /* Very last point, just set dwell = 1/sw */ pt = np_ramp*2 + np_flat - 1; dwell[pt] = dw; dwell_sum += skip; if (fabs(dwell_sum - grad->duration) > 12.5e-9) warn_message("Mismatch in sum of dwells (%.3fus) vs. gradient duration (%.3fus)", dwell_sum*1e6,grad->duration*1e6); /* Return values */ *minskip = skip; *npr = np_ramp; if (sgldisplay) { printf("============== DEBUG DWELL (%d, %d, %d) ===============\n",np_ramp,np_flat,np_ramp); dwell_sum = skip; for (pt = 0; pt < npro-1; pt++) { printf("[%3d] %3.6f\t%3.3f\t%3.6f\n",pt+1,dwell_sum*1e6,dwell[pt]*1e6,(dwell[pt]+dwell_sum)*1e6); dwell_sum += dwell[pt]; } printf("[%3d] %3.6f\t%3.3f\t%3.6f\n",(pt+1),dwell_sum*1e6,0.0,dwell_sum*1e6);pt++; printf("[%3d] %3.6f\t%3.3f\t%3.6f\n",pt+1,dwell_sum*1e6,skip*1e6,(skip+dwell_sum)*1e6); dwell_sum += skip; printf("Did we get a full gradient's worth? %.6f, %.6f\n",grad->duration*1e6,dwell_sum*1e6); printf("=======================================================\n"); } }
pulsesequence() { /* Internal variable declarations *************************/ double freq90[MAXNSLICE],freq180[MAXNSLICE],freqIR[MAXNSLICE]; int shape90=0, shape180=0, shapeIR=0; double te_delay1, te_delay2, tr_delay, ti_delay = 0; double del1=0, del2=0, del3=0, del4=0; double tau1=0, tau2=0, difftime=0, tetime=0; int table=0; /* 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 ************************************/ int vpe_steps = v1; int vpe_ctr = v2; int vms_slices = v3; int vms_ctr = v4; int vpe_offset = v5; int vpe_index = v6; int vph180 = v7; // Phase of 180 pulse int vph2 = v8; // alternate phase of 180 on odd transients /* Initialize paramaters *********************************/ init_mri(); /* Check for external PE table ***************************/ if (strcmp(petable,"n") && strcmp(petable,"N") && strcmp(petable,"")) { loadtable(petable); table = 1; } if ((diff[0] == 'y') && (gcrush < 4)) warn_message("Advisory: set gcrush to higher value to avoid image artifacts"); /* Initialize gradient structures *************************/ init_rf(&p1_rf,p1pat,p1,flip1,rof1,rof2); init_rf(&p2_rf,p2pat,p2,flip2,rof2,rof2); init_slice(&ss_grad,"ss",thk); init_slice_butterfly(&ss2_grad,"ss2",thk*1.1,gcrush,tcrush); init_slice_refocus(&ssr_grad,"ssr"); init_readout_butterfly(&ro_grad,"ro",lro,np,sw,gcrushro,tcrushro); init_readout_refocus(&ror_grad,"ror"); init_phase(&pe_grad,"pe",lpe,nv); /* RF Calculations ****************************************/ calc_rf(&p1_rf,"tpwr1","tpwr1f"); calc_rf(&p2_rf,"tpwr2","tpwr2f"); if (p2_rf.header.rfFraction != 0.5) abort_message("RF pulse for refocusing (%s) must be symmetric",p2pat); /* Gradient calculations **********************************/ calc_slice(&ss_grad,&p1_rf,WRITE,"gss"); calc_slice(&ss2_grad,&p2_rf,WRITE,"gss2"); calc_slice_refocus(&ssr_grad, &ss_grad, NOWRITE,"gssr"); calc_readout(&ro_grad, WRITE, "gro","sw","at"); ro_grad.m0ref *= grof; calc_readout_refocus(&ror_grad, &ro_grad, NOWRITE, "gror"); calc_phase(&pe_grad, NOWRITE, "gpe","tpe"); /* Equalize refocus and PE gradient durations *************/ calc_sim_gradient(&ror_grad, &pe_grad, &ssr_grad,0,WRITE); /* Set up diffusion gradient */ if (diff[0] == 'y') { init_generic(&diff_grad,"diff",gdiff,tdelta); calc_generic(&diff_grad,NOWRITE,"",""); /* adjust duration, so tdelta is from start ramp up to start ramp down */ if (ix == 1) diff_grad.duration += diff_grad.tramp; calc_generic(&diff_grad,WRITE,"",""); } /* Min TE *************************************************/ tau1 = ss_grad.rfCenterBack + pe_grad.duration + 4e-6 + ss2_grad.rfCenterFront; tau2 = ss2_grad.rfCenterBack + ror_grad.duration + ro_grad.timeToEcho + alfa; temin = 2*(MAX(tau1,tau2) + 2*4e-6); /* have at least 4us between gradient events */ /* Calculate te_delays with the current TE, then later see how diffusion fits */ if ((minte[0] == 'y') || (te < temin)) { te_delay1 = temin/2 - tau1; te_delay2 = temin/2 - tau2; } else { te_delay1 = te/2 - tau1; te_delay2 = te/2 - tau2; } if (diff[0] =='y') { /* Is tDELTA long enough for RF refocusing gradient? */ if (tDELTA < diff_grad.duration + ss2_grad.duration) abort_message("DELTA too short, increase to %.2fms", (diff_grad.duration + ss2_grad.duration)*1000+0.005); /* Is tDELTA too long for TE dead time? */ difftime = tDELTA + diff_grad.duration; // tDELTA + front & back half diff_grad tetime = ss2_grad.duration + te_delay1 + te_delay2; if (difftime > tetime) { temin += (difftime - tetime); } } /* We now know the minimum TE incl. diffusion */ if (minte[0] == 'y') { te = temin; putvalue("te",ceil(te*1e6)*1e-6); /* round up to nearest us */ } if (te < temin) { if (diff[0] == 'n') { abort_message("TE too short. Minimum TE = %.2fms\n",temin*1000); } else { abort_message("TE too short, increase to %.2fms or reduce DELTA to %.2fms", temin*1000,(tetime-diff_grad.duration)*1000); } } te_delay1 = te/2 - tau1; te_delay2 = te/2 - tau2; /* Set up delays around diffusion gradients */ /* RF1 - del1 - diff - del2 - RF2 - del3 - diff - del4 - ACQ */ if (diff[0] == 'y') { del1 = (tetime - difftime)/2; del4 = del1; del2 = te_delay1 - diff_grad.duration; del3 = te_delay2 - diff_grad.duration; if (del3 < 0.0) { // shift diff block to right del1 += del3; del2 -= del3; del4 -= del3; del3 = 0; } else if (del2 < 0.0) { // shift diff block to left del1 -= del2; del3 -= del2; del4 += del2; del2 = 0; } } else { /* No diffusion */ del1 = 0; del3 = 0; del2 = te_delay1; del4 = te_delay2; } /* Min TR *************************************************/ trmin = (ss_grad.duration - ss_grad.rfCenterBack) + te + ro_grad.timeFromEcho; if (navigator[0] == 'y') trmin += (pe_grad.duration + ro_grad.duration); /* Optional prepulse calculations *************************/ if (sat[0] == 'y') { create_satbands(); trmin += satTime; } if (fsat[0] == 'y') { create_fatsat(); trmin += fsatTime; } if (mt[0] == 'y') { create_mtc(); trmin += mtTime; } if (ir[0] == 'y') { init_rf(&ir_rf,pipat,pi,flipir,rof2,rof2); calc_rf(&ir_rf,"tpwri","tpwrif"); init_slice_butterfly(&ssi_grad,"ssi",thk,gcrushir,tcrushir); calc_slice(&ssi_grad,&ir_rf,WRITE,"gssi"); tau1 = ss_grad.duration - ss_grad.rfCenterBack; /* duration of ss_grad before RF center */ ti_delay = ti - (ssi_grad.rfCenterBack + tau1); if (ti_delay < 0) { abort_message("TI too short, Minimum TI = %.2fms\n",(ti-ti_delay)*1000); } irTime = ti + ssi_grad.duration - ssi_grad.rfCenterBack; /* time to add to TR */ trmin += irTime; trmin -= tau1; /* but subtract out ss_grad which was already included in TR */ } trmin *= ns; if (mintr[0] == 'y'){ tr = trmin + ns*4e-6; putvalue("tr",tr); } if (tr < trmin) { abort_message("TR too short. Minimum TR= %.2fms\n",trmin*1000); } tr_delay = (tr - trmin)/ns > 4e-6 ? (tr - trmin)/ns : 4e-6; /***************************************************/ /* 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++) { /* Readout */ Dro = ror_grad.duration; bro[i] = bval(gdiff*roarr[i],tdelta,tDELTA); bro[i] += bval(ro_grad.amp,ro_grad.timeToEcho,Dro); /* Slice */ dgss2 = Dgss2 = ss_grad.rfCenterFront; dcrush = tcrush; //"delta" for crusher part of butterfly Dcrush = dcrush + ss_grad.rfDuration; //"DELTA" for crusher bsl[i] = bval(gdiff*slarr[i],tdelta,tDELTA); bsl[i] += bval(gcrush,dcrush,Dcrush); bsl[i] += bval(ss2_grad.ssamp,dgss2,Dgss2); bsl[i] += bval_nested(gdiff*slarr[i],tdelta,tDELTA,gcrush,dcrush,Dcrush); bsl[i] += bval_nested(gdiff*slarr[i],tdelta,tDELTA,ss2_grad.ssamp,dgss2,Dgss2); bsl[i] += bval_nested(gcrush,dcrush,Dcrush,ss2_grad.ssamp,dgss2,Dgss2); /* Phase */ bpe[i] = bval(gdiff*pearr[i],tdelta,tDELTA); /* Readout/Slice Cross-terms */ brs[i] = bval2(gdiff*roarr[i],gdiff*slarr[i],tdelta,tDELTA); brs[i] += bval_cross(gdiff*roarr[i],tdelta,tDELTA,gcrush,dcrush,Dcrush); brs[i] += bval_cross(gdiff*roarr[i],tdelta,tDELTA,ss2_grad.ssamp,dgss2,Dgss2); /* Readout/Phase Cross-terms */ brp[i] = bval2(gdiff*roarr[i],gdiff*pearr[i],tdelta,tDELTA); /* Slice/Phase Cross-terms */ bsp[i] = bval2(gdiff*slarr[i],gdiff*pearr[i],tdelta,tDELTA); bsp[i] += bval_cross(gdiff*pearr[i],tdelta,tDELTA,gcrush,dcrush,Dcrush); bsp[i] += bval_cross(gdiff*pearr[i],tdelta,tDELTA,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); /* Generate phase-ramped pulses: 90, 180, and IR */ offsetlist(pss,ss_grad.ssamp,0,freq90,ns,seqcon[1]); shape90 = shapelist(p1pat,ss_grad.rfDuration,freq90,ns,0,seqcon[1]); offsetlist(pss,ss2_grad.ssamp,0,freq180,ns,seqcon[1]); shape180 = shapelist(p2pat,ss2_grad.rfDuration,freq180,ns,0,seqcon[1]); if (ir[0] == 'y') { offsetlist(pss,ssi_grad.ssamp,0,freqIR,ns,seqcon[1]); shapeIR = shapelist(pipat,ssi_grad.rfDuration,freqIR,ns,0,seqcon[1]); } /* Set pe_steps for profile or full image **********/ pe_steps = prep_profile(profile[0],nv,&pe_grad,&null_grad); initval(pe_steps/2.0,vpe_offset); sgl_error_check(sglerror); /* Return parameters to VnmrJ */ putvalue("rgss",ss_grad.tramp); //90 slice ramp if (ss2_grad.enableButterfly) { //180 slice ramps putvalue("rcrush",ss2_grad.crusher1RampToCrusherDuration); putvalue("rgss2",ss2_grad.crusher1RampToSsDuration); } else { putvalue("rgss2",ss2_grad.tramp); } if (ro_grad.enableButterfly) { putvalue("rgro",ro_grad.crusher1RampToSsDuration); } else { putvalue("rgro",ro_grad.tramp); //RO ramp } putvalue("tror",ror_grad.duration); //ROR duration putvalue("rgror",ror_grad.tramp); //ROR ramp putvalue("gpe",pe_grad.peamp); //PE max amp putvalue("gss",ss_grad.ssamp); putvalue("gro",ro_grad.roamp); g_setExpTime(tr*(nt*pe_steps*arraydim + ssc)); /* PULSE SEQUENCE *************************************/ rotate(); obsoffset(resto); roff = -poffset(pro,ro_grad.roamp); delay(4e-6); /* Begin phase-encode loop ****************************/ peloop(seqcon[2],pe_steps,vpe_steps,vpe_ctr); /* Read external kspace table if set ******************/ if (table) getelem(t1,vpe_ctr,vpe_index); else sub(vpe_ctr,vpe_offset,vpe_index); settable(t2,2,ph180); // initialize phase tables and variables getelem(t2,vpe_ctr,vph180); add(oph,vph180,vph180); // 180 deg pulse phase alternates +/- 90 from receiver mod2(ct,vph2); dbl(vph2,vph2); add(vph180,vph2,vph180); // Alternate phase for 180 on odd transients /* Begin multislice loop ******************************/ msloop(seqcon[1],ns,vms_slices,vms_ctr); if (ticks) { xgate(ticks); grad_advance(gpropdelay); } /* TTL scope trigger **********************************/ sp1on(); delay(5e-6); sp1off(); /* Prepulses ******************************************/ if (sat[0] == 'y') satbands(); if (fsat[0] == 'y') fatsat(); if (mt[0] == 'y') mtc(); /* Optional IR pulse **********************************/ if (ir[0] == 'y') { obspower(ir_rf.powerCoarse); obspwrf(ir_rf.powerFine); delay(4e-6); obl_shapedgradient(ssi_grad.name,ssi_grad.duration,0,0,ssi_grad.amp,NOWAIT); delay(ssi_grad.rfDelayFront); shapedpulselist(shapeIR,ssi_grad.rfDuration,oph,rof1,rof2,seqcon[1],vms_ctr); delay(ssi_grad.rfDelayBack); delay(ti_delay); } /* 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(shape90,ss_grad.rfDuration,oph,rof1,rof2,seqcon[1],vms_ctr); delay(ss_grad.rfDelayBack); /* Phase encode, refocus, and dephase gradient ********/ pe_shapedgradient(pe_grad.name,pe_grad.duration,0,0,-ssr_grad.amp, pe_grad.increment,vpe_index,WAIT); delay(del1); // delay to start of first 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); // delay from end of diffusion to slice refocusing /* Refocusing RF pulse ********************************/ obspower(p2_rf.powerCoarse); obspwrf(p2_rf.powerFine); delay(4e-6); obl_shapedgradient(ss2_grad.name,ss2_grad.duration,0,0,ss2_grad.amp,NOWAIT); delay(ss2_grad.rfDelayFront); shapedpulselist(shape180,ss2_grad.rfDuration,vph180,rof2,rof2,seqcon[1],vms_ctr); delay(ss2_grad.rfDelayBack); delay(del3); // delay from slice refocusing to second 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); // delay from end of diffusion gradient to readout event /* Readout gradient and acquisition ********************/ roff = -poffset(pro,ro_grad.roamp); obl_shapedgradient(ror_grad.name,ror_grad.duration,-ror_grad.amp,0,0,WAIT); obl_shapedgradient(ro_grad.name,ro_grad.duration,ro_grad.amp,0,0,NOWAIT); delay(ro_grad.atDelayFront); startacq(alfa); acquire(np,1.0/sw); delay(ro_grad.atDelayBack); endacq(); /* Rewind Phase encoding ******************************/ pe_shapedgradient(pe_grad.name,pe_grad.duration,0,0,0, pe_grad.increment,vpe_index,WAIT); if (navigator[0] == 'y') { roff = -poffset(pro,-ro_grad.roamp); obl_shapedgradient(ro_grad.name,ro_grad.duration,-ro_grad.amp,0,0,NOWAIT); delay(ro_grad.atDelayFront); startacq(alfa); acquire(np,1.0/sw); delay(ro_grad.atDelayBack); endacq(); } /* Relaxation delay ***********************************/ delay(tr_delay); endmsloop(seqcon[1],vms_ctr); endpeloop(seqcon[2],vpe_ctr); }
void calc_epi(EPI_GRADIENT_T *epi_grad, READOUT_GRADIENT_T *ro_grad, PHASE_ENCODE_GRADIENT_T *pe_grad, REFOCUS_GRADIENT_T *ror_grad, PHASE_ENCODE_GRADIENT_T *per_grad, READOUT_GRADIENT_T *nav_grad, int write_flag) { /* Variables for generating gradient shapes */ double dwint,blipint,skip,dw; int np_ramp, np_flat, npro; double *dwell, *dac; double pos, neg; int pt, pts, inx=0, lobe, lobes, blippts, zeropts; /* Variables for generating tables */ int n, seg, steps; char order_str[MAXSTR],gpe_tab[MAXSTR],tab_file[MAXSTR]; FILE *fp; /********************************************************************************/ /* Error Checks */ /********************************************************************************/ /* make sure that the combination of nseg, ky_order and fract_ky make sense */ if (ky_order[0] == 'c') { /* Can't do just one segment with centric */ if (nseg == 1) { if (ix == 1) { warn_message("WARNING %s: ky_order set to 'l'\n",seqfil); warn_message(" Only linear ordering allowed with single-shot acquisition\n"); } ky_order[0] = 'l'; } /* Must have even number of shots with centric ordering */ if (((int) nseg % 2) == 1) abort_message("ERROR %s: Must do even number of shots with centric ordering\n",seqfil); /* Can't do fractional ky with centric acquisition */ if (fract_ky != pe_grad->steps/2) abort_message("ERROR %s: fract_ky must be = nv/2 (%d) for centric acquisition\n",seqfil,(int) (pe_grad->steps/2)); } if (fract_ky > pe_grad->steps/2) abort_message("ERROR %s: fract_ky must be <= nv/2 (%d)\n",seqfil,(int) (pe_grad->steps/2)); /* calculate etl */ switch(ky_order[0]) { case 'l': epi_grad->etl = (pe_grad->steps/2 + fract_ky)/nseg; epi_grad->center_echo = fract_ky/nseg - 1; if (epi_grad->etl - ((int) epi_grad->etl) > 0.005) abort_message("%s: Echo train length ((%d/2+%d)/%d = %.2f) not an integer\n", seqfil,(int)pe_grad->steps,(int) fract_ky, (int) nseg,epi_grad->etl); break; case 'c': epi_grad->etl = pe_grad->steps/nseg; epi_grad->center_echo = 0; if (epi_grad->etl - ((int) epi_grad->etl) > 0.005) abort_message("%s: Echo train length (%d/%d) not an integer\n", seqfil,(int)pe_grad->steps,(int) nseg); break; default: abort_message("%s: ky_order %s not recognized, use 'l' (linear) or 'c' (centric)\n", seqfil, ky_order); break; } epi_grad->center_echo += ssepi*2; /********************************************************************************/ /* Generate a single phase encoding blip and the dephaser */ /********************************************************************************/ blipint = 1/(pe_grad->fov/10*nuc_gamma()); /* base step in PE dimension */ switch(ky_order[0]) { case 'l': /* each blip will jump nseg lines in ky: */ pe_grad->m0 = blipint*nseg; per_grad->steps = pe_grad->steps; /* each increment is = one blip unit */ per_grad->m0 = blipint*per_grad->steps/2; /* start at nv/2, step 1 */ break; case 'c': /* each blip will jump nseg/2 lines in ky: */ pe_grad->m0 = blipint*nseg/2; per_grad->steps = nseg; per_grad->m0 = blipint*per_grad->steps/2; /* start at nseg/2, step 1 */ break; default: abort_message("ky_order %s not recognized, use 'l' (linear) or 'c' (centric)\n",ky_order); break; } /* Phase encoding blip */ pe_grad->calcFlag = SHORTEST_DURATION_FROM_MOMENT; pe_grad->writeToDisk = FALSE; calcPhase(pe_grad); if ((pe_grad->duration < epi_grad->tblip) || (pe_grad->amp/pe_grad->tramp > pe_grad->slewRate)) { pe_grad->tramp = granularity(MAX(epi_grad->tblip/2,pe_grad->amp/pe_grad->slewRate),pe_grad->resolution); pe_grad->duration = 2*pe_grad->tramp; pe_grad->calcFlag = AMPLITUDE_FROM_MOMENT_DURATION_RAMP; calcPhase(pe_grad); } /* Phase encoding dephaser */ per_grad->calcFlag = SHORTEST_DURATION_FROM_MOMENT; per_grad->maxGrad *= glim; per_grad->writeToDisk = write_flag; calcPhase(per_grad); /* Now adjust the initial dephaser for fractional k-space */ per_grad->amp *= (fract_ky/(pe_grad->steps/2)); /* Create an array for dwell times */ npro = ro_grad->numPointsFreq; if ((dwell = (double *)malloc(sizeof(double)*npro)) == NULL) { abort_message("Can't allocate memory for dwell"); } /********************************************************************************/ /* Generate a single readout lobe */ /********************************************************************************/ ro_grad->writeToDisk = nav_grad->writeToDisk = FALSE; calcReadout(ro_grad); dw = granularity(1/sw,1/epi_grad->ddrsr); dwint = dw*ro_grad->amp; skip = getval("skip"); /* User specified min delay between acquisitions */ skip = MAX(skip,pe_grad->duration); /* Is PE blip longer? Then use that */ skip /= 2; /* in further calculations, skip is the skipped part on either ramp */ /* If RO ramp - skip isn't at least one dwell, then we can't do ramp sampling */ if (ro_grad->tramp - (skip + dw + getval("aqtm") - at) < dw) { if (rampsamp[0] == 'y') { printf("Blip duration or Min echo spacing is long (%.2fus), ramp sampling turned off",2*skip*1e6); rampsamp[0] = 'n'; } /* set ramp time and recalculate */ ro_grad->tramp = skip + (dw + getval("aqtm") - at); calcReadout(ro_grad); } /********************************************************************************/ if (rampsamp[0] == 'n') { /* No rampsampling, just use simple RO shape */ /********************************************************************************/ np_ramp = 0; np_flat = npro; skip = (ro_grad->atDelayFront + ro_grad->atDelayBack)/2; /* Set dwell array */ for (pt = 0; pt < npro; pt++) dwell[pt] = dw; dwell[npro-1] = 2*dw; /* gradient duration is actually dw too long */ } /********************************************************************************/ else { /* rampsampling */ /********************************************************************************/ calc_readout_rampsamp(ro_grad,dwell,&skip,&np_ramp); np_flat = npro - 2*np_ramp; } /* end if rampsampling = 'y' */ switch (ro_grad->error) { case ERR_NO_ERROR: break; case ERR_AMPLITUDE: abort_message("Readout gradient too large, increase FOV to %.2fmm", ro_grad->bandwidth/(ro_grad->gamma * ro_grad->maxGrad * MM_TO_CM)); break; default: abort_message("Error in calculation of readout gradient (error %d)", (int)ro_grad->error); break; } /* We now have a single lobe, keep that for the navigator echo */ nav_grad->amp = ro_grad->amp; nav_grad->duration = ro_grad->duration; nav_grad->tramp = ro_grad->tramp; nav_grad->m0 = ro_grad->m0; nav_grad->m0ref = ro_grad->m0ref; nav_grad->dataPoints = ro_grad->dataPoints; nav_grad->numPoints = ro_grad->numPoints; nav_grad->slewRate = ro_grad->slewRate; /********************************************************************************/ /* Readout dephaser */ /********************************************************************************/ ror_grad->balancingMoment0 = nav_grad->m0ref; /* ideal moment */ /* adjust with grora tweaker */ if (grora == 0) { warn_message("grora tweaker is 0, probably using old protocol - changed to 1.0"); grora = 1.0; } ror_grad->balancingMoment0 *= grora; ror_grad->writeToDisk = write_flag; calcRefocus(ror_grad); /********************************************************************************/ /* Expand gradient shapes to full echo train */ /********************************************************************************/ /* Readout - The navigator shape holds a single lobe */ /********************************************************************************/ /* Does positive or negative lobe need to be adjusted for tweaker? */ pos = neg = 1; if (groa >= 0) /* Adjust negative downwards, since positive is already at max */ neg = (1 - groa/nav_grad->amp); else /* groa < 0, Adjust positive downwards */ pos = (1 + groa/nav_grad->amp); /* Increase the size of ro shape to hold full shape */ /* free(ro_grad->dataPoints); Don't free this, it's now assigned to the navigator echo */ lobes = (epi_grad->etl + ssepi*2); ro_grad->numPoints *= lobes; if ((ro_grad->dataPoints = (double *)malloc(ro_grad->numPoints*sizeof(double))) == NULL) abort_message("%s: Problem allocating memory for EPI readout gradient",seqfil); ro_grad->duration *= lobes; /* Concatenate positive and negative lobes */ /* until we have a full echo train (plus ssepi) */ pts = nav_grad->numPoints; inx = 0; for (lobe = 0; lobe < lobes/2; lobe++) { for (pt = 0; pt < pts; pt++) ro_grad->dataPoints[inx++] = nav_grad->dataPoints[pt] * pos; for (pt = 0; pt < pts; pt++) ro_grad->dataPoints[inx++] = -nav_grad->dataPoints[pt] * neg; } /********************************************************************************/ /* Phase encoding */ /********************************************************************************/ /* Keep blip */ blippts = pe_grad->numPoints; if ((dac = (double *)malloc(blippts*sizeof(double))) == NULL) abort_message("%s: Problem allocating memory for EPI blip",seqfil); for (pt = 0; pt < blippts; pt++) dac[pt] = pe_grad->dataPoints[pt]; /* Increase the size of pe shape to hold full shape */ free(pe_grad->dataPoints); if ((pe_grad->dataPoints = (double *)malloc(ro_grad->numPoints*sizeof(double))) == NULL) abort_message("%s: Problem allocating memory for EPI phase encoding gradient",seqfil); /* Pad front of shape - including ssepi time - with zeros */ inx = 0; lobes = ssepi*2; zeropts = nav_grad->numPoints; for (lobe = 0; lobe < lobes; lobe++) { for (pt = 0; pt < zeropts; pt++) { pe_grad->dataPoints[inx++] = 0; } } zeropts = nav_grad->numPoints - blippts/2; for (pt = 0; pt < zeropts; pt++) pe_grad->dataPoints[inx++] = 0; lobes = epi_grad->etl-1; zeropts = nav_grad->numPoints - blippts; for (lobe=0; lobe < lobes; lobe++) { for (pt = 0; pt < blippts; pt++) pe_grad->dataPoints[inx++] = dac[pt]; for (pt = 0; pt < zeropts; pt++) pe_grad->dataPoints[inx++] = 0; } /* Add a few zeros, half the duration of the blip + one lobe, at the very end */ zeropts = blippts/2 + nav_grad->numPoints; zeropts = blippts/2; for (pt = 0; pt < zeropts; pt++) pe_grad->dataPoints[inx++] = 0; pe_grad->numPoints = ro_grad->numPoints; pe_grad->duration = ro_grad->duration; /********************************************************************************/ /* Keep some parameters in EPI struct */ /********************************************************************************/ epi_grad->skip = skip; epi_grad->np_flat = np_flat; epi_grad->np_ramp = np_ramp; epi_grad->dwell = dwell; epi_grad->duration = ro_grad->duration; epi_grad->numPoints = ro_grad->numPoints; epi_grad->amppos = nav_grad->amp*pos; epi_grad->ampneg = -nav_grad->amp*neg; epi_grad->amppe = pe_grad->amp; /********************************************************************************/ /* Write shapes to disk */ /********************************************************************************/ if (writeToDisk(ro_grad->dataPoints, ro_grad->numPoints, 0, ro_grad->resolution, TRUE /* rollout */, ro_grad->name) != ERR_NO_ERROR) abort_message("Problem writing shape %s (%d points) to disk", ro_grad->name,(int) ro_grad->numPoints); if (writeToDisk(nav_grad->dataPoints, nav_grad->numPoints, 0, nav_grad->resolution, TRUE /* rollout */, nav_grad->name) != ERR_NO_ERROR) abort_message("Problem writing shape %s (%d points) to disk", nav_grad->name,(int) nav_grad->numPoints); if (writeToDisk(pe_grad->dataPoints, pe_grad->numPoints, 0, pe_grad->resolution, TRUE /* rollout */, pe_grad->name) != ERR_NO_ERROR) abort_message("Problem writing shape %s (%d points) to disk", pe_grad->name,(int) pe_grad->numPoints); /********************************************************************************/ /* Create EPI tables */ /********************************************************************************/ /* Generates two tables: */ /* t1 that specifies the k-space ordering, used by recon_all */ /* t2 that specifies which direction in k-space to go in a given shot */ /* 1 = positive blips, and -1 = negative blips */ if ((epi_grad->table1 = (int *)malloc(pe_grad->steps*sizeof(int))) == NULL) abort_message("%s: Problem allocating memory for EPI table1",seqfil); if ((epi_grad->table2 = (int *)malloc(nseg*sizeof(int))) == NULL) abort_message("%s: Problem allocating memory for EPI table2",seqfil); switch(ky_order[0]) { case 'l': inx = 0; for (seg = 0; seg < nseg; seg++) { epi_grad->table1[inx++] = -fract_ky + seg; for (n = 0; n < epi_grad->etl-1; n++) { epi_grad->table1[inx] = (int) (epi_grad->table1[inx-1]+nseg); inx++; } } for (seg = 0; seg < nseg; seg++) epi_grad->table2[seg] = 1; break; case 'c': inx = 0; for (seg = 0; seg < nseg; seg++) { epi_grad->table1[inx++] = -(nseg/2 - seg); for (n = 0; n < epi_grad->etl-1; n++) { if (epi_grad->table1[inx-1] >= 0) epi_grad->table1[inx] = (int) (epi_grad->table1[inx-1] + nseg/2); else epi_grad->table1[inx] = (int) (epi_grad->table1[inx-1] - nseg/2); inx++; } } for (seg = 0; seg < nseg; seg++) epi_grad->table2[seg] = ((nseg/2 - 1 - seg >= 0) ? -1 : 1); break; default: /* This should have been caught earlier */ break; } steps = inx; /* Print table t1 to file in tablib */ switch(ky_order[0]) { case 'l': sprintf(order_str,"lin"); break; case 'c': sprintf(order_str,"cen"); break; default: abort_message("%s: ky_order %s not recognized, use 'l' (linear) or 'c' (centric)\n", seqfil,ky_order); } if (ky_order[1] == 'r') /* not reversed */ sprintf(gpe_tab,"%s_nv%d_f%d_%s%d_rev",seqfil, (int)pe_grad->steps,(int)fract_ky,order_str,(int)nseg); else sprintf(gpe_tab,"%s_nv%d_f%d_%s%d",seqfil, (int)pe_grad->steps,(int)fract_ky,order_str,(int)nseg); sprintf(tab_file,"%s/tablib/%s",userdir,gpe_tab); if ((fp = fopen(tab_file,"w")) == NULL) { abort_message("Error opening file %s\n",gpe_tab); } fprintf(fp,"t1 = "); for (inx = 0; inx < steps; inx++) { if (ky_order[1] == 'r') fprintf(fp,"%d ", epi_grad->table1[inx]); else fprintf(fp,"%d ", -epi_grad->table1[inx]); } fprintf(fp,"\n"); fclose(fp); strcpy(petable,gpe_tab); putstring("petable",gpe_tab); /* Return values in VnmrJ parameter pe_table */ putCmd("exists('pe_table','parameter'):$ex\n"); putCmd("if ($ex > 0) then\n"); putCmd(" pe_table = 0\n"); //reset pe_table array for (inx = 0; inx < steps; inx++) { putCmd(" pe_table[%d] = %d\n",inx+1, ((ky_order[1] == 'r') ? 1 : -1)*epi_grad->table1[inx]); } putCmd("endif\n"); }
void shapedgradient(char *name,double width,double amp,char which, int loops,int wait4me) { cPatternEntry *tmp; int axisCode = 0; long long extra; double value; char buffer[4]; int divs, duration; buffer[0] = which; buffer[1] = '\0'; value = width; switch (buffer[0]) { case 'x': case 'X': axisCode = 1; break; case 'y': case 'Y': axisCode = 2; break; case 'z': case 'Z': axisCode = 3; break; default: abort_message("invalid gradient axis in shapedgradient. abort!\n"); } GradientBase *gC = P2TheConsole->getConfiguredGradient(); if (gC->isNoWaitMode()) abort_message("gradient event requested too soon after previous one in NOWAIT mode. abort!\n"); tmp = gC->resolveOblShpGrdPattern(name,axisCode,"shapedgradient",1); P2TheConsole->newEvent(); gC->clearSmallTicker(); if (wait4me) gC->setActive(); else gC->setNOWAITMode(); divs = tmp->getNumberStates(); duration = gC->calcTicksPerState(value,divs,4,0.1,"Shaped Gradient",0); if (duration < 320) // 4 us is lower limit. abort_message("gradient duration too short for shapedgradient. abort!\n"); // This should work for all imaging, micro imaging & Z axis and Triax PFG gC->setGradScale("X",0x4000); gC->setGradScale("Y",0x4000); gC->setGradScale("Z",0x4000); int fifobuffer[5]; fifobuffer[0] = tmp->getReferenceID(); if (axisCode == 1) { fifobuffer[1] = XGRADKEY; } else if (axisCode == 2) { fifobuffer[1] = YGRADKEY; } else { fifobuffer[1] = ZGRADKEY; } int daclimit = gC->getDACLimit(); if (daclimit == 0x7ff) { if ( (amp > 2047.5) || (amp < -2047.5 ) ) abort_message("gradient amplitude value %g is outside valid dac range in shapedgradient. abort!\n", amp); amp *= 16.0; } int dacvalue = ((int) floor(amp)); if (dacvalue < -32767) { warn_message("advisory: gradient amplitude value %d outside valid dac range clipped to -32767\n",dacvalue); dacvalue = -32767; } else if (dacvalue > 32767) { warn_message("advisory: gradient amplitude value +%d outside valid dac range clipped to +32767\n",dacvalue); dacvalue = 32767; } fifobuffer[2] = dacvalue; fifobuffer[3] = duration; fifobuffer[4] = 1; // Loops gC->outputACode(SHAPEDGRD, 5, fifobuffer); gC->noteDelay(duration*divs); // maybe plus 1.. extra = gC->getSmallTicker(); if (wait4me) P2TheConsole->update4Sync(extra); else gC->incr_NOWAIT_eventTicker(extra); grad_flag = TRUE; return; }