2 import astropy.io.fits
as fits
15 """return the indices of the used actuators
18 xpos: (np.ndarray[ndim=1, dtype=np.int32]): (optional) actuator position in x
20 ypos: (np.ndarray[ndim=1, dtype=np.int32]): (optional) actuator position in y
22 Np: (int): (optional) number of actuators along the diameter
28 if (Np > 0
and Np != u.size):
29 raise ValueError(
"Number of actuator along the diameter unconsistent")
35 X = (xpos - pMin) / u[1]
36 Y = (ypos - pMin) / u[1]
37 return (Y * Np + X).astype(np.int32)
40 def get_idx(p_dm, xpos=None, ypos=None):
41 """return a correspondance between the covariance matrix indices and the covariance map indices
44 p_dm: (Param_dm): dm settings
46 xpos: (np.ndarray[ndim=1, dtype=np.int32]): (optional) actuator position in x
48 ypos: (np.ndarray[ndim=1, dtype=np.int32]): (optional) actuator position in y
51 if (xpos
is not None and ypos
is not None):
58 xx = np.tile(np.arange(Np), (Np, 1)).flatten(
'C')[csI]
59 xx = -np.tile(xx, (xx.size, 1))
62 yy = np.tile(np.arange(Np), (Np, 1)).flatten(
'F')[csI]
63 yy = -np.tile(yy, (yy.size, 1))
69 return dx.flatten(
"F") + (p_dm.nact * 2 - 1) * (dy.flatten(
"F") - 1)
73 size = sup.config.p_geom.pupdiam
74 N = 2**(int(np.log(2 * size) / np.log(2) + 1))
76 supportFi = np.zeros((N, N), dtype=np.float32)
77 fi = sup.config.p_dms[0]._influ[:, :, 0] * 1e-6
78 supportFi[:fi.shape[0], :fi.shape[1]] = fi
80 abs2fi = np.abs(np.fft.fft2(supportFi.T))**2
86 """otf = OTF_telescope(fourier)
88 Computes the OTF of the telescope, so that
89 > fft(OTF_telescope()).re
90 produces a PSF normalized with max(psf)=SR=1.0
93 size = sup.config.p_geom.pupdiam
94 N = 2**(int(np.log(2 * size) / np.log(2) + 1))
95 ud = sup.config.p_tel.diam / sup.config.p_geom.pupdiam
97 x = ud / (sup.config.p_tel.diam / 2.) * (np.arange(N) + 1 -
99 x2 = np.tile(x * x, (x.size, 1))
100 r = np.sqrt(x2 + x2.T)
103 pup = sup.config.p_geom._ipupil
107 surface_pup_m2 = sup.config.p_tel.diam**2 * (
108 1 - sup.config.p_tel.cobs**2) * np.pi / 4.
109 surface_pup_pix = surface_pup_m2 / ud**2
110 factnorm = surface_pup_pix**2
126 p_wfss = sup.config.p_wfs_ngs
128 p_wfss = sup.config.p_wfs_lgs + [sup.config.p_wfs_ngs[-1]]
130 p_wfss = sup.config.p_wfs_lgs + sup.config.p_wfs_ngs
132 validX = wfs._validpuppixx
133 validY = wfs._validpuppixy
134 toMeter = (sup.config.p_tel.diam / wfs.nxsub / wfs._pdiam)
135 validX = (validX - validX.max() / 2) * toMeter
136 validY = (validY - validY.max() / 2) * toMeter
139 nsubap.append(len(validX))
144 """ computes the autocorrelation so that
149 a: (np.ndarray[ndim=2, dtype=np.float32]): matrix to compute the autocorrelation on
153 b = np.abs(np.fft.fft2(a))
154 b = np.fft.ifft2(b * b).real * a.size
156 b = np.abs(np.fft.fft(a))
157 b = np.fft.ifft(b * b).real
159 print(
"error: autocorrelation: expect dim 1 or 2")
180 return 1.e-6 * np.exp(-(x * x + y * y) / (2 * x0 * x0))
183 def generate_files(sup, path=".", singleFile=False, dm_tt=False, WFS="all"):
184 """write inputs parameters
186 sys-params.txt: contains the system parameters
190 subaps.fits : number and position of the subapertures
193 sup: (CompassSupervisor): current supervisor
195 path: (str): (optional), default './' path where the files are written
197 singleFile: (bool): (optional), default=False write a single fits File
199 p_dm = sup.config.p_dms[0]
200 if (p_dm.type ==
'tt'):
201 print(
"ERROR: first dm must not be a 'tip-tilt")
204 ntotact = p_dm._ntotact
207 p_dm_tt = sup.config.p_dms[-1]
208 if (p_dm_tt.type !=
'tt'):
209 print(
"ERROR: tip-tilt dm must be the last one")
216 idx =
get_idx(p_dm, p_dm._xpos, p_dm._ypos)
221 hdu_idx = fits.PrimaryHDU(idx)
222 hdu_idx.header[
"NACT"] = nact
223 hdu_idx.header[
"NTOTACT"] = ntotact
224 hdul = fits.HDUList([hdu_idx])
225 hdul.writeto(path +
"/idx.fits", overwrite=1)
226 fits.writeto(path +
"/otftel.fits", otf, overwrite=1)
227 fits.writeto(path +
"/abs2fi.fits", abs2fi, overwrite=1)
229 hdu_prime = fits.PrimaryHDU(np.zeros(0))
230 hdu_nsubap = fits.ImageHDU(nsubaps, name=
"NSUBAP")
231 hdu_Xpos = fits.ImageHDU(X, name=
"XPOS")
232 hdu_Ypos = fits.ImageHDU(Y, name=
"YPOS")
233 hdul = fits.HDUList([hdu_prime, hdu_nsubap, hdu_Xpos, hdu_Ypos])
234 hdul.writeto(path +
"/subaps.fits", overwrite=1)
236 hdu_prime = fits.PrimaryHDU(np.zeros(0))
237 hdu_nsubap = fits.ImageHDU(nsubaps, name=
"NSUBAP")
238 hdu_Xpos = fits.ImageHDU(X, name=
"XPOS")
239 hdu_Ypos = fits.ImageHDU(Y, name=
"YPOS")
240 hdu_idx = fits.ImageHDU(idx, name=
"IDX")
241 hdu_idx.header[
"NACT"] = nact
242 hdu_idx.header[
"NTOTACT"] = ntotact
243 hdu_abs2fi = fits.ImageHDU(abs2fi, name=
"ABS2FI")
244 hdu_otf = fits.ImageHDU(otf, name=
"OTF")
245 hdul = fits.HDUList([
246 hdu_prime, hdu_nsubap, hdu_Xpos, hdu_Ypos, hdu_idx, hdu_abs2fi, hdu_otf
248 hdul.writeto(path +
"/sys-inputs.fits", overwrite=1)
253 if (type(a)
is np.ndarray):
254 for i
in range(a.size):
255 string += str(a[i]) +
" "
256 if (type(a)
is list):
257 for i
in range(len(a)):
258 string += str(a[i]) +
" "
269 p_wfs_ngs = sim.config.p_wfs_ngs
270 p_wfs_lgs = sim.config.p_wfs_lgs
274 p_wfss = p_wfs_lgs + [p_wfs_ngs[-1]]
276 p_wfss = p_wfs_lgs + p_wfs_ngs
277 p_wfs_ts = sim.config.p_wfs_ts
278 p_targets = sim.config.p_targets
279 p_tel = sim.config.p_tel
280 p_loop = sim.config.p_loop
282 if (len(p_wfs_lgs) > 0):
283 lgsFlux = p_wfs_lgs[0].lgsreturnperwatt * p_wfs_lgs[0].laserpower * p_wfs_lgs[
284 0].optthroughput * 10**4
285 lgsPixSize = p_wfs_lgs[0].pixsize
286 lambdaLGS = p_wfs_lgs[0].Lambda * 1e-6
287 throughLGS = p_wfs_lgs[0].optthroughput
288 spotWidth = p_wfs_lgs[0].beamsize
289 lgsAlt = p_wfs_lgs[0].gsalt
298 if (len(p_wfs_ts) > 0):
299 ts_xpos = [w.xpos
for w
in p_wfs_ts]
300 ts_ypos = [w.ypos
for w
in p_wfs_ts]
305 f = open(path +
"/sys-params.txt",
"w")
306 f.write(
"diam : meter : Telescope diameter\n")
307 f.write(
toStr(p_tel.diam))
308 f.write(
"\nobs : percent : Central obscuration\n")
309 f.write(
toStr(p_tel.cobs))
310 f.write(
"\ntFrame : second : frame rate\n")
311 f.write(
toStr(p_loop.ittime))
312 f.write(
"\nnW : : number of WFS\n")
313 f.write(
toStr(len(p_wfss)))
314 f.write(
"\nnLgs : : number of LGS\n")
315 f.write(
toStr(len(p_wfs_lgs)))
316 f.write(
"\nnTS : : number of Truth Sensor\n")
317 f.write(
toStr(len(p_wfs_ts)))
318 f.write(
"\nnTarget : : number of Target\n")
319 f.write(
toStr(len(p_targets)))
320 f.write(
"\nNssp : : number of subaperture per wfs along the diameter\n"
322 f.write(
toStr([wfs.nxsub
for wfs
in p_wfss]))
323 f.write(
"\nfracsub : % : Minimal illumination fraction for valid subap\n")
325 f.write(
"\ngsAlt : meter^-1 : inverse of lazer altitude\n")
326 f.write(
toStr([1 / w.gsalt
for w
in p_wfs_lgs] + [0
for w
in p_wfs_ngs]))
327 f.write(
"\ntype : : guide star type (1:NGS, 2:LGS)\n")
328 f.write(
toStr([2
for w
in p_wfs_lgs] + [1
for w
in p_wfs_ngs]))
329 f.write(
"\nalphaX_as : arcsec : pointing direction of the wfs on x axis\n")
330 f.write(
toStr([w.xpos
for w
in p_wfss]))
331 f.write(
"\nalphaY_as : arcsec : pointing direction of the wfs on y axis\n")
332 f.write(
toStr([w.ypos
for w
in p_wfss]))
333 f.write(
"\nXPup : meter : pupil shift of the WFS\n")
334 f.write(
toStr([0
for i
in range(len(p_wfss))]))
335 f.write(
"\nYPup : meter : pupil shift of the WFS\n")
336 f.write(
toStr([0
for i
in range(len(p_wfss))]))
337 f.write(
"\nthetaML : : rotation of the microlenses\n")
338 f.write(
toStr([0
for i
in range(len(p_wfss))]))
339 f.write(
"\nthetaCam : : rotation of the camera\n")
340 f.write(
toStr([0
for i
in range(len(p_wfss))]))
341 f.write(
"\nsensibility: : sensitivity coeff of this WFS\n")
342 f.write(
toStr([1
for i
in range(len(p_wfss))]))
343 f.write(
"\ntracking : arcsec^2 : telescope tracking error parameters (x^2, y^2 and xy)\n"
345 f.write(
toStr(
"1 1 1"))
346 f.write(
"\npasDPHI : : Precision of DPHI precomputation. //deprecated\n"
348 f.write(
toStr(0.0001))
349 f.write(
"\nncpu : : Number of CPU used (only with openMP)\n")
351 f.write(
"\nmrNGS : : magnitude of NGS\n")
352 if (len(p_wfs_ngs) > 0):
353 f.write(
toStr([w.gsmag
for w
in p_wfs_ngs]))
355 f.write(
toStr([0.0]))
356 f.write(
"\nlgsFlux : (ph/m2/s) : LGS photon return at M1\n")
357 f.write(
toStr(lgsFlux))
358 f.write(
"\nngsPixSize : arcsec : NGS pixel size\n")
359 if (len(p_wfs_ngs) > 0):
360 f.write(
toStr(p_wfs_ngs[0].pixsize))
363 f.write(
"\nlgsPixSize : arcsec : LGS pixel size\n")
364 f.write(
toStr(lgsPixSize))
365 f.write(
"\nlambdaNGS : meter : wave length for NGS\n")
366 if (len(p_wfs_ngs) > 0):
367 f.write(
toStr(p_wfs_ngs[0].Lambda * 1e-6))
370 f.write(
"\nlambdaLGS : meter : wave length for LGS\n")
371 f.write(
toStr(lambdaLGS))
372 f.write(
"\nbdw_m : meter : bandwidth\n")
374 f.write(
"\nthroughNGS : percent : transmission for NGS\n")
375 if (len(p_wfs_ngs) > 0):
376 f.write(
toStr(p_wfs_ngs[0].optthroughput))
379 f.write(
"\nthroughLGS : percent : transmission for LGS\n")
380 f.write(
toStr(throughLGS))
381 f.write(
"\nthroughAtm : percent : atmosphere transmission\n")
382 f.write(
toStr(throughAtm))
383 f.write(
"\nRON : nb of e- : Read Out Noise \n")
384 f.write(
toStr(int(np.ceil(p_wfss[0].noise))))
385 f.write(
"\nlgsCst : : constant on lgs (simulate that LGS cannot measure tip-tilt and focus)\n"
388 f.write(
"\nspotWidth : arcsec : lazer width\n")
389 f.write(
toStr(spotWidth))
390 f.write(
"\nlgsAlt : meter : sodium layer altitude\n")
391 f.write(
toStr(lgsAlt))
392 f.write(
"\nlgsDepth : meter : depth of the sodium layer\n")
393 f.write(
toStr(lgsdepth))
394 f.write(
"\ntargetX_as : arcsec : taget direction on x axis\n")
395 f.write(
toStr(ts_xpos + [t.xpos
for t
in p_targets]))
396 f.write(
"\ntargetY_as : arcsec : taget direction on y axis\n")
397 f.write(
toStr(ts_ypos + [t.ypos
for t
in p_targets]))
401 f = open(path +
"/prof-1-atmos-night0.txt",
"w")
403 f.write(
toStr(sim.config.p_atmos.nscreens))
404 f.write(
"\nr0 @ wfs lambda\n")
405 f.write(
toStr(sim.config.p_atmos.r0))
406 f.write(
"\ncn2 ESO units\n")
407 f.write(
toStr(sim.config.p_atmos.get_frac().tolist()))
408 f.write(
"\nh in meters\n")
409 f.write(
toStr(sim.config.p_atmos.get_alt().tolist()))
410 f.write(
"\nl0 in meters\n")
411 f.write(
toStr(sim.config.p_atmos.get_L0().tolist()))
413 shutil.copyfile(path +
"/prof-1-atmos-night0.txt", path +
"/prof0-atmos-night0.txt")
416 def write_metaDx(metaDx, nTS=0, nmeas=None, trans=True, path="."):
417 """Write command matrices
419 split the meta command matrix
422 metaDx: (np.ndarray[ndim=2, dtype=np.float32]): "meta" command matrix
424 nTS: (int): (optional), default=0. Number of truth sensors, command matrices are written as Di.fits where 'i' belongs to [0,nTS[ , if nTS<1 write the whole matrix as Dx.fits
426 nmeas: (np.ndarray[ndim=1, dtype=np.int32]): (optional) if set, must contains the number of measurements for each TS, the matrix is split according to theses numbers. By default, the matrix is split evenly between the nTS truth sensors
428 trans: (bool): (optional), default=True. Transpose the matrix if true
430 path: (str): (optional), default './' path where the files are written
434 fits.writeto(path +
"/Dx.fits", metaDx.T, overwrite=
True)
436 fits.writeto(path +
"/Dx.fits", metaDx, overwrite=
True)
440 n = metaDx.shape[1] // nTS
441 nmeas = np.arange(0, metaDx.shape[1] + n, n)
443 nmeas = np.append(0, nmeas.cumsum())
446 print(i + 1,
"out of", nTS, end=
'\r')
447 Dx = metaDx[:, nmeas[i]:nmeas[i + 1]]
449 fits.writeto(path +
"/Dx" + str(i) +
".fits", Dx.T, overwrite=
True)
451 fits.writeto(path +
"/Dx" + str(i) +
".fits", Dx, overwrite=
True)