Beispiel #1
0
/**
   Make basic arrays for servo analysis.
*/
static void servo_calc_init(SERVO_CALC_T *st, const dmat *psdin, double dt, long dtrat){
    if(psdin->ny!=2){
	error("psdin should have two columns\n");
    }
    double Ts=dt*dtrat;
    st->fny=0.5/Ts;
    dmat *nu=st->nu=dlogspace(-3,log10(0.5/dt),1000);
    st->psd=dinterp1(psdin, 0, nu, 1e-40);
    st->var_sig=psd_inte2(psdin);
    dcomplex pi2i=COMPLEX(0, TWOPI);
    if(st->Hsys || st->Hwfs || st->Hint || st->s){
	error("Already initialized\n");
    }
    st->Hsys=cnew(nu->nx, 1);
    st->Hwfs=cnew(nu->nx, 1);
    st->Hint=cnew(nu->nx, 1);
    st->s=cnew(nu->nx,1);
    for(long i=0; i<nu->nx; i++){
	dcomplex s=st->s->p[i]=pi2i*nu->p[i];
	dcomplex zInv=cexp(-s*Ts);
	dcomplex Hint=st->Hint->p[i]=1./(1-zInv);
	dcomplex Hwfs, Hdac;
	if(dtrat==1){//we have a pure delay
	    Hwfs=st->Hwfs->p[i]=zInv; 
	    Hdac=1;
	}else{
	    Hwfs=st->Hwfs->p[i]=(1-zInv)/(Ts*s);
	    Hdac=Hwfs;
	}
	dcomplex Hlag=cexp(-s*dt);/*lag due to readout/computation*/
	dcomplex Hmir=1;/*DM */
	st->Hsys->p[i]=Hwfs*Hlag*Hdac*Hmir*Hint;
    }
}
Beispiel #2
0
/**
   Add two PSDs that doesn't have the same frequency. the first column of each
   dmat is the frequency nu, and the second column is PSD. Bug discovered on
   2013-03-24:only psd2 was added to to psd.*/
static dmat *add_psd_nomatch(const dmat *psd1,const dmat *psd2){
    dmat *nu1=dsub(psd1,0,psd1->nx,0,1);
    dmat *p2ynew=dinterp1(psd2, 0, nu1, 1e-40);
    dmat *psd=dnew(nu1->nx,2);
    double *py=psd->p+psd->nx;
    const double *p1y=psd1->p+psd1->nx;
    for(long i=0; i<psd->nx; i++){
	psd->p[i]=nu1->p[i];
	py[i]=p1y[i]+p2ynew->p[i];
    }
    dfree(nu1);
    dfree(p2ynew);
    return psd;
}
Beispiel #3
0
/**
   Convert PSD into time series.*/
dmat* psd2time(const dmat *psdin, rand_t *rstat, double dt, int nstepin){
    if(!psdin){
	error("psdin cannot be null\n");
    }
    long nstep=nextpow2(nstepin);
    double df=1./(dt*nstep);
    dmat *fs=dlinspace(0, df, nstep);
    dmat *psd=NULL;
    if(psdin->ny==1){//[alpha, beta, fmin, fmax] discribes power law with cut on/off freq.
	psd=dnew(nstep, 1);
	double alpha=psdin->p[0];
	double beta=psdin->p[1];
	long i0=1, imax=nstep;
	if(psdin->nx>2){
	    i0=(long)round(psdin->p[2]/df);
	    if(i0<1) i0=1;
	}
	if(psdin->nx>3){
	    imax=(long)round(psdin->p[3]/df);
	}
	info("fmin=%g, fmax=%g, df=%g, i0=%ld, imax=%ld\n", 
	     psdin->p[2], psdin->p[3], df, i0, imax);
	for(long i=i0; i<imax; i++){
	    psd->p[i]=beta*pow(i*df, alpha);
	}
    }else if(psdin->ny==2){
	if(psdin->nx<2){ 
	    error("Invalid PSD\n");
	}
	psd=dinterp1(psdin, 0, fs, 1e-40);
	psd->p[0]=0;/*disable pistion. */
    }else{
	error("psdin is invalid format.\n");
    }
    cmat *wshat=cnew(nstep, 1);
    //cfft2plan(wshat, -1);
    for(long i=0; i<nstep; i++){
	wshat->p[i]=sqrt(psd->p[i]*df)*COMPLEX(randn(rstat), randn(rstat));
    }
    cfft2(wshat, -1);
    dmat *out=NULL;
    creal2d(&out, 0, wshat, 1);
    cfree(wshat);
    dfree(psd);
    dfree(fs);
    dresize(out, nstepin, 1);
    return out;
}
Beispiel #4
0
/*Interpolate psd onto new f. We interpolate in log space which is more linear.*/
dmat *psdinterp1(const dmat *psdin, const dmat *fnew, int uselog){
    dmat *f1=drefcols(psdin, 0, 1);
    dmat *psd1=dsub(psdin, 0, 0, 1, 1);//copy
    dmat *f2=dref(fnew);
    double t1=dtrapz(f1, psd1);
    double ydefault=1e-40;
    if(uselog){
	dcwlog(psd1);
	ydefault=log(ydefault);
    }
    dmat *psd2=dinterp1(f1, psd1, f2, ydefault);
    if(uselog){
	dcwexp(psd2,1);
    }
    double t2=dtrapz(f2, psd2);
    if(fabs(t1-t2)>fabs(0.5*(t1+t2)*2)){
	warning("psd interpolation failed. int_orig=%g, int_interp=%g\n", t1, t2);
    }
    //Don't scale psd2 as it may have overlapping frequency regions
    dfree(f1); dfree(f2); dfree(psd1);
    return psd2;
}
Beispiel #5
0
/*
  Add a PSD scaled by scale to another. The first column of each dmat is the
   frequency nu, and the second column is PSD.
*/
void add_psd2(dmat **pout, const dmat *in, double scale){
    if(!*pout){
	*pout=ddup(in);
    }else{
	dmat *out=*pout;
	double *p1=PCOL(out,1);
	dmat *p2new=0;
	const long nx=out->nx;
	const double *p2=0;
	if(check_psd_match(out, in)){
	    p2=PCOL(in, 1);
	}else{
	    dmat *nu1=dsub(out,0, nx,0,1);
	    p2new=dinterp1(in, 0, nu1, 1e-40);
	    p2=PCOL(p2new,0);
	    dfree(nu1);
	}
	
	for(long i=0; i<nx; i++){
	    p1[i]+=p2[i]*scale;
	}
	dfree(p2new);
    }
}
Beispiel #6
0
/**
   Setup the detector transfer functions. See maos/setup_powfs.c
 */
static void setup_powfs_dtf(POWFS_S *powfs, const PARMS_S* parms){
    const int npowfs=parms->maos.npowfs;
    for(int ipowfs=0; ipowfs<npowfs; ipowfs++){
	const int ncomp=parms->maos.ncomp[ipowfs];
	const int ncomp2=ncomp>>1;
	const int embfac=parms->maos.embfac[ipowfs];
	const int pixpsa=parms->skyc.pixpsa[ipowfs];
	const double pixtheta=parms->skyc.pixtheta[ipowfs];
	const double blur=parms->skyc.pixblur[ipowfs]*pixtheta;
	const double e0=exp(-2*M_PI*M_PI*blur*blur);
	const double dxsa=parms->maos.dxsa[ipowfs];
	const double pixoffx=parms->skyc.pixoffx[ipowfs];
	const double pixoffy=parms->skyc.pixoffy[ipowfs];
	const double pxo=-(pixpsa/2-0.5+pixoffx)*pixtheta;
	const double pyo=-(pixpsa/2-0.5+pixoffy)*pixtheta;
	loc_t *loc_ccd=mksqloc(pixpsa, pixpsa, pixtheta, pixtheta, pxo, pyo);
	powfs[ipowfs].dtf=mycalloc(parms->maos.nwvl,DTF_S);
	for(int iwvl=0; iwvl<parms->maos.nwvl; iwvl++){
	    const double wvl=parms->maos.wvl[iwvl];
	    const double dtheta=wvl/(dxsa*embfac);
	    const double pdtheta=pixtheta*pixtheta/(dtheta*dtheta);
	    const double du=1./(dtheta*ncomp);
	    const double du2=du*du;
	    const double dupth=du*pixtheta;
	    cmat *nominal=cnew(ncomp,ncomp);
	    //cfft2plan(nominal,-1);
	    //cfft2plan(nominal,1);
	    cmat* pn=nominal;
	    const double theta=0;
	    const double ct=cos(theta);
	    const double st=sin(theta);
	    for(int iy=0; iy<ncomp; iy++){
		int jy=iy-ncomp2;
		for(int ix=0; ix<ncomp; ix++){
		    int jx=ix-ncomp2;
		    double ir=ct*jx+st*jy;
		    double ia=-st*jx+ct*jy;
		    IND(pn,ix,iy)=sinc(ir*dupth)*sinc(ia*dupth)
			*pow(e0,ir*ir*du2)*pow(e0,ia*ia*du2)
			*pdtheta;
		}
	    }
	    if(parms->skyc.fnpsf1[ipowfs]){
		warning("powfs %d has additional otf to be multiplied\n", ipowfs);
		dcell *psf1c=dcellread("%s", parms->skyc.fnpsf1[ipowfs]);
		dmat *psf1=NULL;
		if(psf1c->nx == 1){
		    psf1=dref(psf1c->p[0]);
		}else if(psf1c->nx==parms->maos.nwvl){
		    psf1=dref(psf1c->p[iwvl]);
		}else{
		    error("skyc.fnpsf1 has wrong dimension\n");
		}
		dcellfree(psf1c);
		if(psf1->ny!=2){
		    error("skyc.fnpsf1 has wrong dimension\n");
		}
		dmat *psf1x=dnew_ref(psf1->nx, 1, psf1->p);
		dmat *psf1y=dnew_ref(psf1->nx, 1, psf1->p+psf1->nx);
		dmat *psf2x=dnew(ncomp*ncomp, 1);
		for(int iy=0; iy<ncomp; iy++){
		    int jy=iy-ncomp2;
		    for(int ix=0; ix<ncomp; ix++){
			int jx=ix-ncomp2;
			psf2x->p[ix+iy*ncomp]=sqrt(jx*jx+jy*jy)*dtheta;
		    }
		}
		info("powfs %d, iwvl=%d, dtheta=%g\n", ipowfs, iwvl, dtheta*206265000);
		writebin(psf2x, "powfs%d_psf2x_%d", ipowfs,iwvl);
		dmat *psf2=dinterp1(psf1x, psf1y, psf2x, 0);
		normalize_sum(psf2->p, psf2->nx*psf2->ny, 1);
		psf2->nx=ncomp; psf2->ny=ncomp;
		writebin(psf2, "powfs%d_psf2_%d", ipowfs,iwvl);
		cmat *otf2=cnew(ncomp, ncomp);
		//cfft2plan(otf2, -1);
		ccpd(&otf2, psf2);//peak in center
		cfftshift(otf2);//peak in corner
		cfft2(otf2, -1);
		cfftshift(otf2);//peak in center
		writebin(otf2, "powfs%d_otf2_%d", ipowfs, iwvl);
		writebin(nominal, "powfs%d_dtf%d_nominal_0",ipowfs,iwvl);
		for(int i=0; i<ncomp*ncomp; i++){
		    nominal->p[i]*=otf2->p[i];
		}
		writebin(nominal, "powfs%d_dtf%d_nominal_1",ipowfs,iwvl);
		dfree(psf1x);
		dfree(psf1y);
		dfree(psf2x);
		dfree(psf1);
		cfree(otf2);
		dfree(psf2);
	    }
	    cfftshift(nominal);//peak in corner
	    cfft2(nominal,-1);
	    cfftshift(nominal);//peak in center
	    cfft2i(nominal,1);
	    warning_once("double check nominal for off centered skyc.fnpsf1\n");
	    /*This nominal will multiply to OTF with peak in corner. But after
	      inverse fft, peak will be in center*/
	    ccp(&powfs[ipowfs].dtf[iwvl].nominal, nominal);
	    cfree(nominal);

	    loc_t *loc_psf=mksqloc(ncomp, ncomp, dtheta, dtheta, -ncomp2*dtheta, -ncomp2*dtheta);
	    powfs[ipowfs].dtf[iwvl].si=mkh(loc_psf,loc_ccd,0,0,1);
	    locfree(loc_psf);
	    if(parms->skyc.dbg){
		writebin(powfs[ipowfs].dtf[iwvl].nominal,
		       "%s/powfs%d_dtf%d_nominal",dirsetup,ipowfs,iwvl);
		writebin(powfs[ipowfs].dtf[iwvl].si,
			"%s/powfs%d_dtf%d_si",dirsetup,ipowfs,iwvl);
	    }
	    powfs[ipowfs].dtf[iwvl].U=cnew(ncomp,1);
	    dcomplex *U=powfs[ipowfs].dtf[iwvl].U->p;

	    for(int ix=0; ix<ncomp; ix++){
		int jx=ix<ncomp2?ix:(ix-ncomp);
		U[ix]=COMPLEX(0, -2.*M_PI*jx*du);
	    }
	}/*iwvl */
	locfree(loc_ccd);
    }/*ipowfs */
}
Beispiel #7
0
static inline void clipdm(SIM_T *simu, dcell *dmcmd){
    const PARMS_T *parms=simu->parms;
    if(!dmcmd) return;
    /*
      clip integrator. This both limits the output and
      feeds back the clip since we are acting on the integrator directly.
    */
    if(parms->sim.dmclip){
	for(int idm=0; idm<parms->ndm; idm++){
	    const int nact=dmcmd->p[idm]->nx;
	    if(parms->dm[idm].stroke->nx==1){
		if(parms->dm[idm].stroke->ny!=1){
		    error("dm.stroke is in wrong format\n");
		}
		int nclip=dclip(dmcmd->p[idm], 
				-parms->dm[idm].stroke->p[0],
				parms->dm[idm].stroke->p[0]);
		if(nclip>0){
		    info2("step %d DM %d: %d actuators clipped\n", simu->isim, idm, nclip);
		}
	    }else if(parms->dm[idm].stroke->nx==nact){
		if(parms->dm[idm].stroke->ny!=2){
		    error("dm.stroke is in wrong format\n");
		}
		
		double *pcmd=dmcmd->p[idm]->p;
		double *plow=parms->dm[idm].stroke->p;
		double *phigh=parms->dm[idm].stroke->p+nact;
		for(int iact=0; iact<nact; iact++){
		    if(pcmd[iact]<plow[iact]){
			pcmd[iact]=plow[iact];
		    }else if(pcmd[iact]>phigh[iact]){
			pcmd[iact]=phigh[iact];
		    }
		}		    
	    }else{
		error("Invalid format\n");
	    }
	}
    }
    if(parms->sim.dmclipia){
	/*Clip interactuator stroke*/
	for(int idm=0; idm<parms->ndm; idm++){
	    /* Embed DM commands to a square array (borrow dmrealsq) */
	    double iastroke;
	    int nx=simu->recon->anx->p[idm];
	    double (*dmr)[nx];
	    dmat *dm;
	    if(parms->dm[idm].iastrokescale){ //convert dm to voltage
		dm=dinterp1(parms->dm[idm].iastrokescale->p[0], 0, dmcmd->p[idm], NAN);
		iastroke=parms->dm[idm].iastroke;//voltage.
	    }else{
		dm=dmcmd->p[idm];
		iastroke=parms->dm[idm].iastroke*2;//surface to opd
	    }
	    if(!parms->fit.square){
		loc_embed(simu->dmrealsq->p[idm], simu->recon->aloc->p[idm], dm->p);
		dmr=(double(*)[nx])simu->dmrealsq->p[idm]->p;
	    }else{
		dmr=(double(*)[nx])dm->p;
	    }
	    lcell *actstuck=simu->recon->actstuck;
	    long *stuck=actstuck?(actstuck->p[idm]?actstuck->p[idm]->p:0):0;
	    int count=0,trials=0;
	    do{
		count=0;
		PDMAT(simu->recon->amap->p[idm],map);
		for(int iy=0; iy<simu->recon->any->p[idm]-1; iy++){
		    for(int ix=0; ix<nx; ix++){
			int iact1=map[iy][ix];
			int iact2=map[iy+1][ix];
			if(iact1>0 && iact2>0){
			    count+=limit_diff(&dmr[iy][ix], &dmr[iy+1][ix], iastroke, 
					      stuck?stuck[iact1-1]:0, stuck?stuck[iact2-1]:0);
			}
		    } 
		}
		for(int iy=0; iy<simu->recon->any->p[idm]; iy++){
		    for(int ix=0; ix<nx-1; ix++){
			int iact1=map[iy][ix];
			int iact2=map[iy][ix+1];
			if(iact1>0 && iact2>0){
			    count+=limit_diff(&dmr[iy][ix], &dmr[iy][ix+1], iastroke, 
					      stuck?stuck[iact1-1]:0, stuck?stuck[iact2-1]:0);
			}
		    }
		}
		trials++;
		if(trials==1 && count>0) {
		    info2("Step %d, DM %d: %d actuators over ia limit. ", simu->isim, idm, count);
		}
	    }while(count>0 && trials<100);
	    if(trials>1){
		info2("trials=%d: %s\n", trials, count?"failed.":"success.");
	    }
	    if(!parms->fit.square){//copy data back
		loc_extract(simu->dmreal->p[idm], simu->recon->aloc->p[idm], simu->dmrealsq->p[idm]);
	    }
	    if(parms->dm[idm].iastrokescale){//convert back to opd
		dmat *dm2=dinterp1(parms->dm[idm].iastrokescale->p[1], 0, dm, NAN);
		dcp(&dmcmd->p[idm], dm2);
		dfree(dm); dfree(dm2);
	    }
	}
    }
}
Beispiel #8
0
/**
   Setup matched filter for stars.
 */
static void setup_star_mtch(const PARMS_S *parms, POWFS_S *powfs, STAR_S *star, int nstar, dcell**nonlin){
    const long nwvl=parms->maos.nwvl;
    const long npowfs=parms->maos.npowfs;
    dmat* rnefs=parms->skyc.rnefs;
 
    for(int istar=0; istar<nstar; istar++){
	if(!star[istar].idtrat){
	    star[istar].idtrat=dnew(npowfs, 1);
	}
	double radius=sqrt(pow(star[istar].thetax,2)+pow(star[istar].thetay,2));
	int igg=round(radius*206265/parms->maos.ngsgrid);
	//info("radius=%g as, igg=%d\n", radius*206265, igg);
	for(int ipowfs=0; ipowfs<npowfs; ipowfs++){
	    const long nsa=parms->maos.nsa[ipowfs];
	    const long pixpsa=parms->skyc.pixpsa[ipowfs];
	    //size of PSF
	    const double sigma_theta=parms->skyc.wvlmean/parms->maos.dxsa[ipowfs];
	    PISTAT_S *pistat=&star[istar].pistat[ipowfs];
	    pistat->i0=dcellnew(nsa,nwvl);
	    pistat->gx=dcellnew(nsa,nwvl);
	    pistat->gy=dcellnew(nsa,nwvl);
	    
	    pistat->i0s=dcellnew(nsa,1);
	    pistat->gxs=dcellnew(nsa,1);
	    pistat->gys=dcellnew(nsa,1);

	    dcell*  psf=pistat->psf;
	    dcell*  i0=pistat->i0;
	    dcell*  gx=pistat->gx;
	    dcell*  gy=pistat->gy;
	    for(long iwvl=0; iwvl<nwvl; iwvl++){
		for(long isa=0; isa<nsa; isa++){
		    double siglev=star[istar].siglev->p[ipowfs]->p[iwvl];
		    IND(i0,isa,iwvl)=dnew(pixpsa,pixpsa);
		    IND(gx,isa,iwvl)=dnew(pixpsa,pixpsa);
		    IND(gy,isa,iwvl)=dnew(pixpsa,pixpsa);
		    psf2i0gxgy(IND(i0,isa,iwvl),IND(gx,isa,iwvl),IND(gy,isa,iwvl),
			       IND(psf,isa,iwvl),powfs[ipowfs].dtf+iwvl);
		    dadd(&pistat->i0s->p[isa], 1, IND(i0,isa,iwvl), siglev);
		    dadd(&pistat->gxs->p[isa], 1, IND(gx,isa,iwvl), siglev);
		    dadd(&pistat->gys->p[isa], 1, IND(gy,isa,iwvl), siglev);
		}
		 
	    }
	    if(parms->skyc.dbg){
		writebin(pistat->i0s, "%s/star%d_ipowfs%d_i0s", dirsetup,istar,ipowfs);
	    }
	    const double pixtheta=parms->skyc.pixtheta[ipowfs];
	    int ndtrat=parms->skyc.ndtrat;
	    pistat->mtche=mycalloc(ndtrat,dcell*);
	    pistat->sanea=dcellnew(ndtrat,1);
	    pistat->sanea0=dcellnew(ndtrat,1);
	    pistat->snr=dnew(ndtrat,1);
	    dcell *i0s=NULL; dcell *gxs=NULL; dcell *gys=NULL;

	    for(int idtrat=0; idtrat<ndtrat; idtrat++){
		int dtrat=parms->skyc.dtrats->p[idtrat];
		dcelladd(&i0s, 0, pistat->i0s, dtrat);
		dcelladd(&gxs, 0, pistat->gxs, dtrat);
		dcelladd(&gys, 0, pistat->gys, dtrat);
		genmtch(&pistat->mtche[idtrat], &pistat->sanea->p[idtrat],
			i0s, gxs, gys, pixtheta, IND(rnefs,idtrat,ipowfs), 
			star[istar].bkgrnd->p[ipowfs]*dtrat, parms->skyc.mtchcr);
		/*Add nolinearity*/
		if(nonlin){
		    //add linearly not quadratically since the errors are related.
		    dmat *nea_nonlin=dinterp1(nonlin[ipowfs]->p[igg], NULL, pistat->sanea->p[idtrat], 0);
		    for(int i=0; i<nsa*2; i++){
			//info2("%g mas", pistat->sanea->p[idtrat]->p[i]*206265000);
			pistat->sanea->p[idtrat]->p[i]=sqrt(pow(pistat->sanea->p[idtrat]->p[i],2)
							    +pow(nea_nonlin->p[i],2));
			//info2("-->%g mas\n", pistat->sanea->p[idtrat]->p[i]*206265000);
		    }
		    dfree(nea_nonlin);
		}
		dcp(&pistat->sanea0->p[idtrat], pistat->sanea->p[idtrat]);
		if(parms->skyc.neaaniso){
		    for(int i=0; i<nsa*2; i++){
			pistat->sanea->p[idtrat]->p[i]=sqrt(pow(pistat->sanea->p[idtrat]->p[i],2)
							    +pow(star[istar].pistat[ipowfs].neaspec->p[i]->p[idtrat], 2));
		    }
		}
		if(parms->skyc.dbg){
		    writebin(pistat->mtche[idtrat], "%s/star%d_ipowfs%d_mtche_dtrat%d",
			       dirsetup,istar,ipowfs,dtrat);
		}
#if 1
		double nea=sqrt(dsumsq(pistat->sanea->p[idtrat])/(nsa*2));
#else
		double nea=dmax(pistat->sanea->p[idtrat]);
#endif
		double snr=sigma_theta/nea;
		pistat->snr->p[idtrat]=snr;
		if(parms->skyc.verbose) info2("dtrat=%3d, nea=%4.1f, snr=%4.1f\n",
					      (int)parms->skyc.dtrats->p[idtrat], nea*206265000, snr);
		if(snr>parms->skyc.snrmin->p[parms->skyc.snrmin->nx==1?0:idtrat]){
		    star[istar].idtrat->p[ipowfs]=idtrat;
		}
	    }//for idtrat
	    if(parms->skyc.dbg){
		info2("star %2d optim: powfs %1d: dtrat=%3d\n", istar, ipowfs,
		      (int)parms->skyc.dtrats->p[(int)star[istar].idtrat->p[ipowfs]]);
		writebin(pistat->sanea, "%s/star%d_ipowfs%d_sanea",
			   dirsetup,istar,ipowfs);
	    }/*idtrat */
	    dcellfree(i0s);
	    dcellfree(gxs);
	    dcellfree(gys);
	}/*for istar */
    }/*for ipowfs */
}
Beispiel #9
0
/**
   Read in pistat information, used to compute matched filter, and SANEA.
*/
static void setup_star_read_pistat(SIM_S *simu, STAR_S *star, int nstar, int seed){
    const PARMS_S *parms=simu->parms;
    const int npowfs=parms->maos.npowfs;
    const int nwvl=parms->maos.nwvl;
    const double ngsgrid=parms->maos.ngsgrid;
    for(int istar=0; istar<nstar; istar++){
	STAR_S *stari=&star[istar];
	stari->pistat=mycalloc(npowfs,PISTAT_S);
	const double thetax=stari->thetax*206265;/*in as */
	const double thetay=stari->thetay*206265;
	double thxnorm=thetax/ngsgrid;
	double thynorm=thetay/ngsgrid;
	long thxl=(long)floor(thxnorm);
	long thyl=(long)floor(thynorm);
	double wtx=thxnorm-thxl;
	double wty=thynorm-thyl;
	for(int ipowfs=0; ipowfs<npowfs; ipowfs++){
	    const int msa=parms->maos.msa[ipowfs];
	    const int nsa=parms->maos.nsa[ipowfs];
	    dcell *avgpsf=NULL;
	    dcell *neaspec=NULL;
	    double wtsum=0;
	    for(int ix=0; ix<2; ix++){
		double thx=(thxl+ix)*ngsgrid;
		for(int iy=0; iy<2; iy++){
		    double thy=(thyl+iy)*ngsgrid;
		    double wtxi=fabs(((1-ix)-wtx)*((1-iy)-wty));
		    if(wtxi<0.01){
			/*info("skipping ix=%d,iy=%d because wt=%g\n",ix,iy,wtxi); */
			continue;
		    }
		    char fn[PATH_MAX];
		    snprintf(fn,PATH_MAX,"%s/pistat/pistat_seed%d_sa%d_x%g_y%g",
			     dirstart, seed,msa,thx,thy);
		    if(!zfexist(fn)){
			/*warning("%s doesn't exist\n",fn); */
		    }else{
			dcell *avgpsfi=dcellread("%s",fn);
			dcelladd(&avgpsf, 1, avgpsfi, wtxi);
			dcellfree(avgpsfi);
			wtsum+=wtxi;
			
			snprintf(fn,PATH_MAX,"%s/neaspec/neaspec_seed%d_sa%d_x%g_y%g",
				 dirstart, seed, msa, thx, thy);
			dcell *neaspeci=dcellread("%s",fn);
			dcelladd(&neaspec, 1, neaspeci, wtxi);
			dcellfree(neaspeci);
		    }
		}
	    }
	    if(wtsum<0.01){
		warning("PISTAT is not available for (%g,%g) msa=%d\n",thetax,thetay,msa);
	    }
	    dcellscale(neaspec, 1./wtsum);
	    dcellscale(avgpsf, 1./wtsum);
	    dmat *scale=NULL;
	    if(parms->skyc.bspstrehl){
		scale=dnew(nsa,nwvl);
		dmat *gx=dnew(1,1); gx->p[0]=thxnorm;
		dmat *gy=dnew(1,1); gy->p[0]=thynorm;
		if(nsa!=avgpsf->nx || nwvl!=avgpsf->ny){
		    error("Mismatch: nsa=%d, nwvl=%d, avgpsf->nx=%ld, avgpsf->ny=%ld\n",
			  nsa, nwvl, avgpsf->nx, avgpsf->ny);
		}
		for(int ic=0; ic<nsa*nwvl; ic++){
		    dmat *val=dbspline_eval(simu->bspstrehl[ipowfs][ic],
					    simu->bspstrehlxy,simu->bspstrehlxy,
					    gx, gy);
		    double ratio=val->p[0]/avgpsf->p[ic]->p[0];
		    /*info("strehl: bilinear: %g, cubic: %g\n", avgpsf->p[ic]->p[0],val->p[0]); */
		    if(ratio<0){
			warning("Ratio=%g is less than zero.\n", ratio);
			scale->p[ic]=1;
		    }else{
			dscale(avgpsf->p[ic], ratio);
			scale->p[ic]=ratio;
		    }
		    dfree(val);
		}
		dfree(gx);
		dfree(gy);
	    }

	    stari->pistat[ipowfs].psf=avgpsf;/*PSF is in corner. */
	    stari->pistat[ipowfs].neaspec=dcellnew(nsa*2, 1);
	    for(int ig=0; ig<nsa*2; ig++){
		dmat *tmp=0;
		for(int iwvl=0; iwvl<nwvl; iwvl++){
		    dadd(&tmp, 0, neaspec->p[ig+nsa*2*iwvl], parms->skyc.wvlwt->p[iwvl]);
		}
		stari->pistat[ipowfs].neaspec->p[ig]=dinterp1(simu->neaspec_dtrats, tmp, parms->skyc.dtrats, 0);
		dfree(tmp);
	    }
	    dcellfree(neaspec);
	    stari->pistat[ipowfs].scale=scale;
	    {/* skip stars with large PSF.*/
		int size=INT_MAX;
		for(int ic=0; ic<avgpsf->nx*avgpsf->ny; ic++){
		    int size0=dfwhm(avgpsf->p[ic]);
		    if(size0<size) size=size0;
		}
		if(size>6){
		    stari->use[ipowfs]=-1;
		}
	    }
	    if(parms->skyc.dbg){
		writebin(avgpsf, "%s/avgpsf_star%d_ipowfs%d_psf",dirsetup,istar,ipowfs);
		writebin(stari->pistat[ipowfs].neaspec, "%s/pistat_star%d_ipowfs%d_neaspec",dirsetup,istar,ipowfs);
	    }
	}
    }
}