def zernikeResidualSurfaceError(x, y, z_coef, r=50, eps=230e-6, lmbd=2.6e-3, verbose=False): """ Computes the effect of the residual Zernike on the surface error. """ # Simulate thermal deformation. # The Zernike polynomials is at the center of the map. x_mid = midPoint(x) y_mid = midPoint(y) z_sim = zernikePoly(x, y, x_mid, y_mid, coefficients=z_coef) z_sim[~radialMask(x,y,r)] = np.nan eps_z = np.nanstd(z_sim) if verbose: print("Residual surface error: {} microns".format(eps_z*1e6)) # Compute surface error. eps_tot = np.sqrt(eps_z**2. + eps**2.) if verbose: print("Total surface error: {} microns".format(eps_tot*1e6)) # Compute aperture efficiency. eta = lambda lmbd, eps: 0.71*np.exp(-np.power(4.*np.pi*eps/lmbd, 2.)) eta_tot = eta(lmbd, eps_tot) if verbose: print("Aperture efficiency: {}".format(eta_tot)) print("Change in aperture efficiency: {} %".format((eta(lmbd, eps) - eta_tot)/eta(lmbd, eps)*100.)) return eps_tot, eta_tot, eta
def zernikeCost(coeffs, x, y, z, w): """ Cost funtion for the least squares problem. """ zpoly = zernikePoly(x, y, midPoint(x), midPoint(y), coeffs) return w*(z.flatten() - zpoly.flatten())
def zernikeJac(coeffs, x, y, z): """ Jacobian of the cost function for the least squares problem. This does not work at the moment. """ coeffs = np.ones_like(coeffs) coeffs[:2] = 0 jac = np.zeros((len(z),len(coeffs))) for i in range(2,len(coeffs)): cis = np.zeros(36) cis[i] = 1. jac[i] = zernikePoly(x, y, midPoint(x), midPoint(y), cis) return jac
def getZernikeCoeffsCycle(x, y, diff, nZern=36, fill=0): """ """ fl_fs = getZernikeCoeffs(diff.filled(fill)[::-1].T, 36, norm='active-surface') diff_sub = np.ma.copy(diff) fl_fs_sub = np.copy(fl_fs) it = 0 while np.any(abs(fl_fs_sub[2:4]) > 50e-6): fl_fs_sub[2] *= -1. zpoly_ = zernikePoly(x, y, midPoint(x), midPoint(y), fl_fs_sub[0:4]) diff_sub = diff_sub - zpoly_ fl_fs_sub = getZernikeCoeffs(diff_sub.filled(fill)[::-1].T, 36, norm='active-surface') it += 1 return fl_fs_sub, diff_sub
def zernikeWLS(x, y, z, nZern, weights=None): """ Use weighted least-squares to compute Zernike coefficients. """ # Use WLS to determine the Zernike coefficients. if weights is None: weights = np.ones(z.shape) weights = np.ma.masked_invalid(weights) x_ = np.ma.masked_invalid(x) x_ -= midPoint(x) y_ = np.ma.masked_invalid(y) y_ -= midPoint(y) fl_wls = getZernikeCoeffsOLS(x_, y_, z, nZern, weights=weights) return fl_wls
def maskXYZ(x, y, z, n=512, guess=[60., 0., 0., 0., 0., 0.], bounds=None, radialMask=True, maskRadius=40., poly_order=2, **kwargs): """ """ xf = x[~np.isnan(z)] yf = y[~np.isnan(z)] zf = z[~np.isnan(z)] # Use masked arrays on the input data to avoid warnings. x = np.ma.masked_invalid(x) y = np.ma.masked_invalid(y) z = np.ma.masked_invalid(z) # Fit a parabola to the data. fitresult = fitLeicaData(xf, yf, zf, guess, bounds=bounds, weights=None) # Subtract the fitted parabola from the data. # The difference should be flat. c = fitresult.x zp = paraboloid(x, y, c[0]) cor = np.hstack((-1 * c[1:4], c[4:6], 0)) xrr, yrr, zrr = shiftRotateXYZ(x, y, z, cor) diff = zrr - zp if radialMask: xc = midPoint(xrr) yc = midPoint(yrr) mask = (((xrr - xc)**2. + (yrr - yc)**2.) < maskRadius**2.) mdiff = np.ma.masked_where(~mask, diff) else: mdiff = diff # Mask any pixels which deviate from the noise. # This should mask out retroreflectors, misaligned panels and sub-scans where the TLS moved due to wind. mcdiff = sigma_clip(mdiff) # Apply the mask to the original data and repeat once more. # In the end also fit a parabola to each row in the data, and mask outliers. # This allows to find more subtle features in the data. xf = np.ma.masked_where(mcdiff.mask, x) yf = np.ma.masked_where(mcdiff.mask, y) zf = np.ma.masked_where(mcdiff.mask, z) masked_fitresult = fitLeicaData(xf.compressed(), yf.compressed(), zf.compressed(), guess, bounds=bounds, weights=None) c = masked_fitresult.x zp = paraboloid(x, y, c[0]) cor = np.hstack((-1 * c[1:4], c[4:6], 0)) xrrm, yrrm, zrrm = shiftRotateXYZ(x, y, z, cor) masked_diff = zrrm - zp mcdiff2 = masked_diff # Final mask. map_mask = np.zeros((n, n), dtype=bool) xl = np.linspace(0, n, n) # Loop over rows fitting a parabola and masking any pixels that deviate from noise. for i in range(0, n): yl = mcdiff2[i] if len(xl[~yl.mask]) > 3: poly_c = np.polyfit(xl[~yl.mask], yl[~yl.mask], poly_order) poly_f = np.poly1d(poly_c) res = np.ma.masked_invalid(yl - poly_f(xl)) res_sc = sigma_clip(res, **kwargs) map_mask[i] = res_sc.mask else: map_mask[i] = True # Prepare output. origData = (x, y, z) origMaskedData = (np.ma.masked_where(map_mask, x), np.ma.masked_where(map_mask, y), np.ma.masked_where(map_mask, z)) rotatedData = (xrrm, yrrm, np.ma.masked_where(map_mask, zrrm)) fitResidual = np.ma.masked_where(map_mask, zrrm - zp) parabolaFit = (xrrm, yrrm, zp) outData = { 'origData': origData, 'origMasked': origMaskedData, 'rotated': rotatedData, 'fitResidual': fitResidual, 'parabolaFit': parabolaFit, 'parabolaFitCoeffs': c } return outData
def plotZernikes(x, y, zernCoeffs, n=512, title=None, filename=None): """ Plot Zernike polynomials with coefficients `zernCoeffs` over a square grid. Parameters ---------- x : array Array with x coordinates. Used to evaluate the Zernike polynomials. y : array Array with y coordinates. Used to evaluate the Zernike polynomials. zernCoeffs : array Array with the coefficients of a Zernike polynomial. n : int, optional Number of pixels per side of the square grid used to display the Zernike polynomial. title : string, optional filename : string, optional Save the plot to this disk location. """ # Create the linear combination of the Zernike polynomials. zPoly = zernikePoly(x, y, midPoint(x), midPoint(y), zernCoeffs) # Grid it to a regularly sampled cartesian grid. xx, yy, zz = regridXYZ(x, y, zPoly, n=n) # Make it look like the dish of the GBT by selecting a circular area. mask = (((xx - midPoint(xx))**2. + (yy - midPoint(yy))**2.) < 49.**2.) zz[~mask] = np.nan # To get meaningful plot tick labels. extent = [np.nanmin(xx), np.nanmax(xx), np.nanmin(yy), np.nanmax(yy)] fig = plt.figure(figsize=(12, 8), dpi=80) ax = fig.gca() im = ax.imshow(zz.T, cmap=cm.RdYlGn, extent=extent) plt.colorbar(im) # Add minor ticks and make them point inwards. ax.minorticks_on() ax.tick_params('both', which='both', direction='in', top=True, right=True, bottom=True, left=True) # Set a title. if title is not None: plt.title(title) # Set axis label names. ax.set_xlabel('x axis (m)') ax.set_ylabel('y axis (m)') # Save the figure to disk. if filename is not None: plt.savefig(filename)
def testMidPoint(self): x = np.array([1, 2, 3]) self.assertEqual(midPoint(x), 2)