def rotateZernike(z, i0, angledeg): """ Inputs: <z> : 1D array of floating point values (zernike coefficients) <i0> : scalar, integer. It is the zernike index of the 1st coeff contained in <z>. <angledeg> : scalar, floating point Output: An array of floating-point values (zernike coefficients of the rotated wavefront). For a couple [a,b] of Zernike coefficients with same azimutal order m, returns the couple of coefficients of Zernike decomposition rotated by an angle <angledeg> (angle expressed in degrees) """ # number of coefficients contained in the vector z nzer = z.shape[0] # index of the last Zernike coefficient imax = nzer+i0-1 # check whether <imax> is ok or not errorImax = checkConsistencyOfNumberOfElementsOfZernikeArray( nzer+i0-1 ) # produces an error if not consistent if( errorImax ): print "The number of Zernike modes (%d to %d) does not entierely fill a radial order." % (i0, imax) print "A coefficient is missing : it is impossible to rotate the wavefront properly." print "I will ignore the last Zernike coefficient." nzer = nzer-1 # allocates memory for the result: an array of <nzer> coefficient, same length as <z>, floats. z_output = np.zeros(nzer) k = 0 while(k<nzer): i = k+i0 # index of the Zernike mode, as described in the literature (Z_1 is piston) # determine radial and azimutal orders, called <n> and <m> n,m = gral_zer.nm(i) if( m==0 ): # do nothing, coefficient is unchanged z_output[k] = z[k] else: if( (i&1)==0 ): # equivalent to "i modulo 2" # if i is an even number (2,4,6,..) tmp = rotateCoupleOfCoeffs(z[k], z[k+1], m, angledeg) z_output[k] = tmp[0] z_output[k+1] = tmp[1] else: # if i is an odd number (1,3,5,7,...) tmp = rotateCoupleOfCoeffs(z[k+1], z[k], m, angledeg) # warning: SWAP coefficients !!!! z_output[k] = tmp[1] z_output[k+1] = tmp[0] # skip the next coefficient z[k+1], that has already been processed k = k+1 k = k+1 return z_output
def flipZernike(z, i0): """ Input parameters: <z> : 1D array of zernike coefficients <i0> : Zernike index of the first element of the array Output parameters: 1D array of zernike coefficients The function returns a list of zernike coefficient that represent a flipped version of the wavefront wrt to the input. The flip is operated around the X axis (equation: y=0, i.e. "horizontal" axis). Note: the Yorick version provides a new array of values in output. For a C version of the code, it would be MUCH better to do the transformation in place, just modifying the coefficients in the same memory area, as the transformation is straightforward (just modifying the sign of some of the coefficients). """ # number of coefficients contained in the vector z nzer = z.shape[0] # allocates memory for the result: an array of <nzer> coefficient, same length as <z>, floats. z_output = np.zeros(nzer) for k in range(nzer): i= k+i0 # index of the Zernike mode, as described in the literature (Z_1 is piston) # determine radial and azimutal orders, called <n> and <m> (well, actually, n is not required here...) n,m = gral_zer.nm(i) if( m==0 ): z_output[k] = z[k] # coeff unchanged else: if( (i&1)==1 ) : # if i is an odd number (1,3,5..) z_output[k] = -z[k] # change sign else : # if i is an even number (2,4,6..) z_output[k] = z[k] # coeff unchanged return z_output
def checkConsistencyOfNumberOfElementsOfZernikeArray( imax ): """ Input: <imax> : scalar, integer. A number of Zernike mode. Output: An boolean: True=error, False=no error The function returns a boolean error code. True when there is an error, False when there is no error. """ # determine radial and azimutal orders n and m n,m = gral_zer.nm(imax) if( m==0 ): return False n2 = n//2 # Warning : interger division ! 5//2 = 2, and not 2.500 !! if( (imax&1)==(n2&1) ): # If parity of <imax> and <n2> is the same: not good. return True else: # If parity is different: ok this is correct. return False