4 ---------------------------------------------------------------------------------------------------------
5 File name: slopesCovariance.py
6 Authors: E. Gendron & F. Vidal
8 Re-written in Python from the original code in Yorick made in 2010 for the CANARY experiment.
10 See also Vidal F., Gendron E. and Rousset G, "Tomography approach for multi-object adaptive optics
11 ", JOSA-A Vol. 27, Issue 11, pp. A253-A264 (2010)
13 This file contains all the routines needed to compute slopes covariance matrices between 2 SH WFS and serves as
14 a basis for the Learn & Apply algorithm.
16 The main function to be called should be: compute_Caa (see example in the function docstring)
17 compute_Caa is a low-level function.
19 To be used properly, compute_Caa shall be called wrapped into several loops:
20 - 2 nested loops over all the WFSs of the experiment
21 - 1 other nested loop (with sum) over the layers
22 in order to fill the whole covariance matrix of the system and
23 integrate over the Cn2(h) profile.
25 The fitting process (LEARN part) shall use a minimization algorithm (such as https://lmfit.github.io/lmfit-py/model.html)
26 to compute the minimum mean square error between the measured covariance slopes matrix from WFSs data
27 and the slopes covariance matrix model provided by this file to estimate the desired atmospheric parameters
28 (I.e fitted parameters are usually Cn2 parameters such as r_0 and altitude).
30 A special care shall be taken in slopes order in the pupil (see example docstring in compute_Caa).
32 ---------------------------------------------------------------------------------------------------------
37 def KLmodes(x, y, L0, filterPiston):
40 <x, y> : x,y coordinates of the DM actuators.
41 Could come from the output of the function:
42 x, y, mask = generateActuCoordinates(nx, ny, Nactu)
43 OR can be any x,y DM coordinates.
45 <L0> : outer scale, in the same units as x and y.
46 <filterPiston> : True/False, to filter the piston out or not
54 x, y, mask = generateActuCoordinates(nx, ny, Nactu)
56 modes = KLmodes(x, y, L0, True)
60 print(
'Computing matrix of distances')
61 mdist2 = (x[:,
None] - x[
None, :])**2 + (y[:,
None] - y[
None, :])**2
65 print(
'Computing covariance matrix')
67 kolmo = -0.5 * 6.88 * mdist2**(5. / 6)
69 kolmo = -0.5 *
rodconan(np.sqrt(mdist2), L0 / D)
71 print(
'Filtering piston out')
73 kolmo = P.dot(kolmo.dot(P))
74 print(
'Diagonalisation')
78 l, U = np.linalg.eigh(kolmo)
81 return U[:, ::-1], l[::-1]
86 Creates a sqaure matrix that removes (=filter out) piston mode.
87 <n> : size of the matrix
89 P = computePistonFilteringMatrix(64)
91 P = np.full((n, n), -1. / n)
97 def compute_Caa(vv1, vv2, s1, s2, Cn2h, L0, alt1, alt2, altitude):
100 This function computes the covariance matrix between TWO wavefront-sensors,
101 each pointing in some particular direction of a given Guide Star for a UNI turbulent
102 layer at a given altitude.
108 The simulated guide star ---IS ALWAYS--- TT sensitive i.e if the GS is set at a finite altitude, it simulates a LGS with the perfect TT sensing capability.
109 If needed, the TT filtering should be performed afterwards on the covariance matrix by applying an averaging slopes matrix for the given WFS (see computeTTFiltMatrix for TT filtering or Filt_Zer for filtering other Zernikes modes)
111 <vv1>, <vv2> is the array (N,2) of the subaperture coordinates projected
112 onto altitude layer. vv[:,0] is the x-coordinate, vv([:,1] is y.
113 vv1 is for WFS number 1, vv2 for the other. It's up to the user to
114 order the subapertures in the way he expects to find them in the
116 <s1>, <s2> are the sizes of the subapertures **in the pupil** (i.e.
117 at ground level, i.e. the real physical size in the telescope
118 pupil, not the one projected onto the layer)
119 <Cn2h> strengh of the layer: it must be equal to r0**(-5./3) with r0
120 in metres. Initially this parameter was r0 but the Learn & Apply fit was
121 non-linear. Now the Learn will be searching for <Cn2h> which makes
122 it a linear fit and convergence is faster.
123 <L0> the outer scale (in metres)
124 <alt1>, <alt2>: altitude of the source (the Guide Star) of the WFS (metres). A value
125 of alt==0 is interpreted as alt==infinity.
126 <altitude>: altitude of the layer (metres). Used to recompute the real ssp size at the given altitude.
132 # Generating Arbitrary ssp coordinates with a 14x14 SH on a 8m telescope:
134 x = np.linspace(-1,1,Nssp)
135 x, y = np.meshgrid(x,x)
136 r=np.sqrt(x*x+y*y)<1.1
137 vv1 = np.array([x[r].flatten(), y[r].flatten()]).T
139 # We shift the second WFS by 0.5m
142 s1=0.5 # Physical size of the ssp in meters of wfs #1
143 s2=0.5 # Physical size of the ssp in meters of wfs #2
148 alt1 = 0 # GS altitude 0 meters == infinite GS == NGS
149 alt2 = 0 # GS altitude 0 meters == infinite GS == NGS
151 altitude = 3500 # Altitude of the turbulence layer in meters
153 caa = compute_Caa(vv1, vv2, s1, s2, Cn2h, L0, alt1, alt2, altitude) # Computes the WFS covariance matrix
155 # --------------------------------------------
156 # Optional modes filtering:
157 # --------------------------------------------
159 nbslopes = caa.shape[0] # x +y slopes = total nb of slopes
160 FiltMatrix = computeTTFiltMatrix(nbslopes) # ex with TT filtering (use Filt_Zer for other modes filtering)
162 # Case of Covmat of NGS/LGS:
163 caaFilt = np.dot(filtTTMat, caa)
165 # Case of Covmat of LGS/LGS:
166 caaFilt = np.dot(np.dot(filtTTMat, caa), filtTTMat.T)
177 vx = vx1[:,
None] - vx2[
None, :]
178 vy = vy1[:,
None] - vy2[
None, :]
183 s1 = s1*(alt1-altitude)/alt1
185 s2 = s2*(alt2-altitude)/alt2
189 if (s1 <= 0)
or (s2 <= 0):
190 return np.zeros((2*nssp1, 2*nssp2))
199 caa_xx = -
DPHI(vx+ac,vy,L0) +
DPHI(vx+ad,vy,L0) +
DPHI(vx+bc,vy,L0) -
DPHI(vx+bd,vy,L0)
203 caa_yy = -
DPHI(vx,vy+ac,L0) +
DPHI(vx,vy+ad,L0) +
DPHI(vx,vy+bc,L0) -
DPHI(vx,vy+bd,L0)
209 s0 = np.sqrt(s1**2+s2**2)
210 caa_xy = -
DPHI(vx+s0/2,vy-s0/2,L0) +
DPHI(vx+s0/2,vy+s0/2,L0) +
DPHI(vx-s0/2,vy-s0/2,L0) -
DPHI(vx-s0/2,vy+s0/2,L0)
214 r = np.max([s1,s2])/np.min([s1,s2])
215 coeff = 1./(1-(1-1/r)**2)
218 caa_xy = -
DPHI(vx+s1/2,vy-s2/2,L0) +
DPHI(vx+s1/2,vy+s2/2,L0) +
DPHI(vx-s1/2,vy-s2/2,L0) -
DPHI(vx-s1/2,vy+s2/2,L0)
221 caa = np.zeros((2*nssp1, 2*nssp2))
222 caa[:nssp1, :nssp2] = caa_xx
223 caa[:nssp1, nssp2:nssp2*2] = caa_xy
224 caa[nssp1:nssp1*2, :nssp2] = caa_xy
225 caa[nssp1:nssp1*2, nssp2:nssp2*2] = caa_yy
227 k = 1.0/s1_pup/s2_pup
228 lambda2 = (206265*0.5e-6/2/np.pi)**2
229 caa *=k*lambda2*(np.abs(Cn2h))
239 Computation of the Mc Donald function.
241 f(x) = x**(5/6)*K_{5/6}(x)
242 using a series for the esimation of K_{5/6}, taken from Rod Conan thesis :
243 K_a(x)=1/2 \sum_{n=0}**\infty \frac{(-1)**n}{n!}
244 \left(\Gamma(-n-a) (x/2)**{2n+a} + \Gamma(-n+a) (x/2)**{2n-a} \right) ,
247 Setting x22 = (x/2)**2, setting uda = (1/2)**a, and multiplying by x**a,
249 x**a * Ka(x) = 0.5 $ -1**n / n! [ G(-n-a).uda x22**(n+a) + G(-n+a)/uda x22**n ]
250 Then we use the following recurrence formulae on the following quantities :
251 G(-(n+1)-a) = G(-n-a) / -a-n-1
252 G(-(n+1)+a) = G(-n+a) / a-n-1
254 x22**(n+1) = x22**n * x22
255 and at each iteration on n, one will use the values already computed
257 The values of G(a) and G(-a) are hardcoded instead of being computed.
259 The first term of the series has also been skipped, as it
260 vanishes with another term in the expression of Dphi.
269 Ga = 2.01126983599717856777
270 Gma = -3.74878707653729348337
271 s = np.zeros(x.shape)
295 Computes a term involved in the computation of the phase struct
296 function with a finite outer scale according to the Von-Karman
297 model. The term involves the MacDonald function (modified bessel
298 function of second kind) K_{5/6}(x), and the algorithm uses the
299 asymptotic form for x ~ infinity.
301 - This function makes a floating point interrupt for x=0
302 and should not be used in this case.
303 - Works only for x>0.
309 k2 = 1.00563491799858928388289314170833
310 k3 = 1.25331413731550012081
311 a1 = 0.22222222222222222222
312 a2 = -0.08641975308641974829
313 a3 = 0.08001828989483310284
315 res = k2 - k3*np.exp(-x)*x**(1/3.)*(1.0 + x_1*(a1 + x_1*(a2 + x_1*a3)))
322 """ DOCUMENT rodconan(r,L0,k=)
323 The phase structure function is computed from the expression
324 Dphi(r) = k1 * L0**(5./3) * (k2 - (2.pi.r/L0)**5/6 K_{5/6}(2.pi.r/L0))
326 For small r, the expression is computed from a development of
327 K_5/6 near 0. The value of k2 is not used, as this same value
328 appears in the series and cancels with k2.
329 For large r, the expression is taken from an asymptotic form.
334 k1 = 0.1716613621245709486
335 dprf0 = (2*np.pi/L0)*r
339 res = np.zeros(r.shape)
344 smallX = np.logical_not(largeX)
345 res[smallX] = -
macdo_x56(dprf0[smallX], k=k)
346 return (k1 * L0**(5./3)) * res
354 dphi = DPHI(x,y,L0) * r0**(-5./3)
356 Computes the phase structure function for a separation (x,y).
357 The r0 is not taken into account : the final result of DPHI(x,y,L0)
358 has to be scaled with r0**-5/3, with r0 expressed in meters, to get
362 r = np.sqrt(x**2+y**2)
364 """ BEFORE ...... when rod conan did not exist
365 fracDim = 5./3. # Can vary fracDim for those who do not believe in Kolmogorov...
377 Returns a matrix <p1> that filters the average TT on the vector of slopes <vec>
378 with nbslopes = x+y slopes
385 p1 = computeTTFiltMatrix(nbslopes)
386 vec_filt = np.dot(p1,vec)
390 Id = np.identity(nbslopes)
397 Returns a matrix p that averages the slopes over x and y.
404 p = filt_TTvec(nbslopes)
405 vec_filtered = np.dot(p,vec)
408 p = np.zeros((nbslopes, nbslopes))
409 p[0:nbslopes//2,0:nbslopes//2] = 1
410 p[nbslopes//2:,nbslopes//2:] = 1
418 MatFiltZer = Filt_Zer(modesfilt, miz);
419 This function generates a matrix that filters all the zernikes modes up to <modesfilt>
420 It requires a zernike interaction matrix <miz> of the given the WFS
427 MatFiltZer = Filt_Zer(modesfilt, miz)
429 The filtered vector can be computed using:
430 vec_filt = np.dot(MatFiltZer,miz)
432 With <miz> computed using doit:
433 from hraa.tools.doit import doit
437 miz = doit(nssp, obs, nbzer)
440 mizFilt = miz[:,:modesfilt]
441 mrz = np.linalg.pinv(mizFilt)
442 MatFiltZer = np.identity(miz.shape[0]) - np.dot(mizFilt, mrz)
def computeTTFiltMatrix(nbslopes)
Returns a matrix <p1> that filters the average TT on the vector of slopes <vec> with nbslopes = x+y s...
def rodconan(r, L0, k=10)
DOCUMENT rodconan(r,L0,k=) The phase structure function is computed from the expression Dphi(r) = k1 ...
def macdo_x56(x, k=10)
Computation of the Mc Donald function.
def compute_Caa(vv1, vv2, s1, s2, Cn2h, L0, alt1, alt2, altitude)
This function computes the covariance matrix between TWO wavefront-sensors, each pointing in some par...
def computePistonFilteringMatrix(n)
Creates a sqaure matrix that removes (=filter out) piston mode.
def filt_TTvec(nbslopes)
Returns a matrix p that averages the slopes over x and y.
def KLmodes(x, y, L0, filterPiston)
Return modes <x, y> : x,y coordinates of the DM actuators.
def asymp_macdo(x)
Computes a term involved in the computation of the phase struct function with a finite outer scale ac...
def Filt_Zer(modesfilt, miz)