Commit c6ff6d90 authored by Laura Schreiber's avatar Laura Schreiber
Browse files

first commit

parent 820937b9
Loading
Loading
Loading
Loading
+7.29 MiB

File added.

No diff preview for this file type.

asong_slope.pro

0 → 100644
+149 −0
Original line number Diff line number Diff line
;+ Author: Laura Schreiber - August 2022
;
; ROUTINE NAME:
;asong_slope
;
;PURPOSE:
;Given a frame and the binary masks defining the four quadrants of the ASONG WFS, compute the wavefront derivatives in X and Y.
;
;CALLING SEQUENCE:
;asong_slope, frame, qmask, pup, centres, sx, sy
;
;INPUT:
;image:  measured frame with subtracted background and dovided by the flat (if required)
;qmask:  3D array with four planes, each representing a binary mask 
;        associated to one quadrant of the sensor
;pup:    footprint of sx and sy
;centres: 4 X 2-elements vector of coordinates of pupils centroids
;
;KEYWORDS:
;gain:        when this keyword is set, the gain is not computed and the value set in the keyword is used instead.
;             It can come from calibration procedures. DEFAULT VALUE = 0
;pyr:         Set this keyword in case a Pyramid WFS is used in stead of ASONG. DEFAULT VALUE = 0    
;dxf, dyf:    GTF x and y period in mm. If only one of the two keywords is set, the period in X and Y is considered to be the same. 
;             DEFAULT VALUE = 9.05 mm  
;foc:         distance from the aperture and the fourier plane
;display:     set this keyword to visualise the computed slopes in a window
;pup_scale:   System magnification. Theoretical value is given by the ratio between the focalising optic focal length and
;             and the MLA focal length. This parameter should be measured in the lab by reimaging in the pupil an object of known lenght             
;det_px:      detector pixel size in microns. DEFAULT VALUE = 2.4                        
;
;OUTPUT:
;sx, sy: array of signals in X and Y

;-
; NOTES: this procedure is adapted for the MLA with 4 lenses. In case of more lenses, minor modifications needs to be done in the code
;
;dir = 'C:\Users\schreibl\IDLWorkspace\Default\ASONG_software\TEST\'  -- directory of your test folder
;name = 'test_'
;restore, dir + 'ASONG_mask_test.sav'
;restore,dir + 'test_image.sav'
;asong_slope, ima_avg, qmask, pup, centres, sx, sy ,/disp,/save, dir = dir, name = name

PRO asong_slope, image, qmask, pup, centres, $                               ;inputs
                 GAIN = GAIN               , $
                 dxf = dxf                 , $ 
                 dyf = dyf                 , $ 
                 foc = foc                 , $ 
                 display = display         , $ 
                 pup_scale = pup_scale     , $ 
                 det_px = det_px           , $ 
                 save_file = save_file     , $
                 dir = dir                 , $
                 wscale = wscale           , $
                 name = name               , $            ;keywords
                 sx, sy                                                      ;outputs


;on_error, 2
 
if keyword_set(save_file) and not keyword_set(dir) then begin
  print, 'files will be saved in your working directory'
  pwd
endif
if not keyword_set(wscale) then wscale = 1
   if not keyword_set(name)      then name = ''
if not keyword_set(PYR)          then begin
   if not keyword_set(foc)       then foc = 500.  ; focal lengh in mm
   if not keyword_set(dxf)       then dxf = 9.05    ; period in mm
   if not keyword_set(pup_scale) then pup_scale = 12.10 ; pupil object to pupil image scale factor
   if not keyword_set(det_px)    then det_px = 2.4         ; detector pixel size in microns

   
   dyf = dxf
   gx = dxf / foc / 2. / !dpi / sqrt(2.) * (det_px * pup_scale)
   gy = dyf / foc / 2. / !dpi / sqrt(2.) * (det_px * pup_scale)
endif else begin 
   if not keyword_set(GAIN) then begin
      gx = 1.0 & gy = 1.0  
   endif else begin
      gx = gain & gy = gain
   endelse
endelse


dimpup = (size(pup,/dim))[0]
c=centres

; Extract four quadrants from the frame
nquad = 4
quad = fltarr(dimpup, dimpup, nquad)
lx = round(c[*,0]) - dimpup/2
ux = round(c[*,0]) + dimpup/2 
ly = round(c[*,1]) - dimpup/2
uy = round(c[*,1]) + dimpup/2
for q = 0, nquad - 1 do begin
      quad[*,*,q] = (image * qmask[*,*,q])[lx[q]:ux[q]-1,ly[q]:uy[q]-1]*pup
endfor

; Compute signal and derivative
sx = fltarr(dimpup, dimpup)
sy = fltarr(dimpup, dimpup)
norm = total(quad, 3)
quadmask = (pup and (norm ne 0)) and 1B
w = where(quadmask)

if keyword_set(PYR) then begin
  sx[w] = ((quad[*,*,3] + quad[*,*,1]) - (quad[*,*,2] + quad[*,*,0]))[w]
  sy[w] = ((quad[*,*,2] + quad[*,*,3]) - (quad[*,*,0] + quad[*,*,1]))[w]
  sx[w] = gx * sx[w] / norm[w]
  sy[w] = gy * sy[w] / norm[w]  
endif else begin
  sx[w] = ((quad[*,*,3] + quad[*,*,1]) - (quad[*,*,2] + quad[*,*,0]))[w];/visx
  sy[w] = ((quad[*,*,0] + quad[*,*,1]) - (quad[*,*,2] + quad[*,*,3]))[w];/visy
  sx[w] = sx[w] / norm[w]
  sy[w] = sy[w] / norm[w]  
  if total(where(abs(sx[w]) gt 1.)) gt 0 then begin
    print,'WARNING! Detected' + strcompress(string(n_elements(where(abs(sx[w]) gt 1.))))+ ' infinite values in the slope computation'
    print,'Automathic sobstitution of infinite values...'
    sx[where(abs(sx*quadmask) gt 1.)] = signum((sx[w])[where(abs(sx[w]) gt 1.)])
  endif
  if total(where(abs(sy[w]) gt 1.)) gt 0 then begin
    print,'WARNING! Detected' + strcompress(string(n_elements(where(abs(sy[w]) gt 1.))))+ ' infinite values in the slope computation'
    print,'Automathic sobstitution of infinite values...'
    sy[where(abs(sy*quadmask) gt 1.)] = signum((sy[w])[where(abs(sy[w]) gt 1.)])
  endif
  sx[w] = asin(sx[w]) * gx
  sy[w] = asin(sy[w]) * gy  
endelse

if keyword_set(display) then begin
  window, 0 , title = 'Slope X and Y', xs = wscale * dimpup*2, ys = wscale * dimpup
  tvscl,congrid([sx,sy],wscale *dimpup*2,wscale *dimpup)
endif

if keyword_set(save_file) then begin
  if keyword_set(dir) then begin
    save, sx, sy, pup, filename = dir + 'ASONG_slopes' +name + '.sav' 
   
      WRITE_BMP, dir + 'ASONG_slopes' + name + '_preview.bmp', BYTSCL([sx,sy])
  
  endif else begin
    save, sx, sy, pup, filename = 'ASONG_slopes' +name + '.sav' 
    WRITE_BMP,'ASONG_slopes' + name + '_preview.bmp', BYTSCL([sx,sy])
  endelse
endif


return
end
+253 −0
Original line number Diff line number Diff line
;+ Author: Laura Schreiber - August 2022
;
; ROUTINE NAME:
;asong_wfe_reconstruction
;
;PURPOSE:
;Given the Wavefront X and Y slopes and the pupil mask, it reconstructs the phase through iterative method proposed by 
;F. Roddier & C. Roddier (Wavefront reconstruction using iterative Fourier transforms,Applied Optics, 30, 1325, (1991))
;and through (low order) modal reconstruction using Zernike modes.  
;
;CALLING SEQUENCE:
;asong_wfe_reconstruction, sx, sy, pup, wfe_it, wfe_zer, zer_coeff 
;
;INPUT:
;sx      :     Wavefront slope X
;sy      :     Wavefront slope Y
;pup     :     footprint of sx and sy

;
;KEYWORDS:
;save_file = save_file :  Variable type: LOGICAL. Use this keyword to save the mask and the output variables.
;save_rmat = save_rmat :  Variable type: LOGICAL. Use this keyword to save the reconstruction matrix and the Zernike cube. 
;dir = dir             :  Variable type: STRING. Set this keyword to define the directory in which the variables must be stored.
;name = name           :  Variable type: STRING. Set this keyword to set the name of the file. DEFAULT VALUE = 'ASONG_mask'
;NOTILT = NOTILT       :  Variable type: LOGICAL. Set this keyword to subract the avarage value to slope maps. DEFAULT VALUE = 1
;DISPLAY = DISPLAY     :  Variable type: LOGICAL. Set this keyword to have displayed the wavefronts once computed. DEFAULT VALUE = 1
;RMAT = RMAT           :  Variable type: 2D array. This keyword can be used to pass to the procedure the reconstruction matrix if already computed.
;                         If the reconstruction matrix RMAT is not available, this keywork can be used to have it as an output and use it for a second iteration.
;n_zer = n_zer         :  Variable type: SCALAR. Number of computed Zernike modes. DEFAULT VALUE = 20. 
;                         WARNING!!!! Since the pupil resolution is very high (around 1000 X 1000 pixels)
;                         the nominal value of Zernike modes that can be used for the reconstruction is very high. The computation time and the memory required for an high
;                         number of Zernike modes can be prohibitive. If possible, use this only with low nomber of Zernike (less than few hundreds)
;sigma_cut = sigma_cut :  Data more than this number of standard deviations from the median is ignored. Suggested values: 2.0 and up. DEFAULT VALUE = 3
;calibration = calibration :If set, the slopes are multiplied by a coefficient computed during calibration operations. DEFAULT VALUE = 1
;lambda = lambda       :  Variable type: SCALAR. DEFAULT VALUE: 0.633 microns
;waves = waves         :  Variable type: LOGICAL. If set, returns the values in waves
;NOZER = NOZER         :  Variable type: LOGICAL. If set, only iterative reconstruction will be performed and all concerning modal reconstruction
;                         will be ignored
;ONLY_ZER = ONLY_ZER   :  Variable type: LOGICAL. If set, only modal reconstruction will be performed and all concerning iterative reconstruction
;                         will be ignored

;
;OUTPUT:
;wfe_it      :      Calibrated reconstructed WAVEFRONT throught the method of R&R
;wfe_zer     :      Low order reconstructed WAVEFRONT throught modal reconstruction
;zer_coeff   :      Zernike coefficients coming from modal reconstruction

;OPTIONAL OUTPUTS:
;RMAT      :      See the keyword RMAT. Precomputed Reconstruction matrix
;zermat    :      3D Array containing the Zernike modes normalised on the pupil. To be visualised they have to be multiplied by the pupil array

;-
; NOTES and DISCLAIMER: This procedures uses many libraries, personal and pubblic. Please, check for permissions and disclaimar before distribution
;
;dir = 'C:\Users\schreibl\IDLWorkspace\Default\ASONG_software\TEST\'  -- directory of your test folder 
;name = 'test_'
;restore, dir + 'ASONG_slopes_test.sav'
;restore,dir + 'test_image.sav'

;asong_wfe_reconstruction, sx, sy, pup, wfe_it, wfe_zer, zer_coeff, zermat, rmat = rmat,/sav, dir=dir, name = name

;MODIFICATION HISTORY
;29/09/2022    Added keyword ONLY_ZER and relative header

PRO asong_wfe_reconstruction, sx, sy, pup               , $
                              NOTILT = NOTILT           , $
                              DISPLAY = DISPLAY         , $
                              rmat = rmat               , $
                              NOZER = NOZER             , $
                              n_zer = n_zer             , $
                              sigma_cut = sigma_cut     , $
                              calibration = calibration , $
                              lambda = lambda           , $
                              waves = waves             , $
                              save_file = save_file     , $
                              dir = dir                 , $
                              name = name               , $
                              silent = silent           , $
                              save_rmat = save_rmat     , $
                              ONLY_ZER = ONLY_ZER       , $
                              _EXTRA = extra            , $
                              wfe_it, wfe_zer, zer_coeff, zermat   

;on_error,2

if not keyword_set(n_zer)       then n_zer = 20
if not keyword_set(calibration) then calibration = 1
if not keyword_set(sigma_cut)   then sigma_cut = 3
if not keyword_set(lambda)      then lambda = 0.633 ;wavelengh in microns
if not keyword_set(name)        then name = ''
if n_elements(display) eq 0     then display = 1
if n_elements(notilt)  eq 0     then notilt = 1
if keyword_set(save_file) and not keyword_set(dir) then begin
  print, 'files will be saved in your working directory'
  pwd
endif
if keyword_set(only_zer) and keyword_set(nozer) then begin
  print,'Keywords ONLY_ZER and NOZER set at the same time.'
  print,'At least one reconstruction method should be chosen.'
  print,'Return'
  return
endif
dimpup = (size(pup,/dim))[0]
sx = sx * calibration   &    sy = sy * calibration

; removing tilt from slopes by subtracting the average value
if keyword_set(notilt) then begin
  ww = where(pup)  
  sx_ = fltarr(dimpup,dimpup) & sy_ = fltarr(dimpup,dimpup) 
  sx_[ww]=sx[ww]-mean(sx[ww]) & sy_[ww]=sy[ww]-mean(sy[ww])
endif else begin
  sx_ = sx  &  sy_ = sy
endelse

; Modal reconstruction in case required
if not keyword_set(NOZER) then begin
  if keyword_set(rmat) then begin
    if n_elements(zermat) eq 0 then begin
      print,'Zernike cube not defined.'
      print,'Re-computing RMAT and Zernike cube...'
    endif
    if n_zer ne (size(rmat,/dim))[1] or n_elements(where(pup)) ne (size(rmat,/dim))[0]/2 then begin
      print,'Incompatible reconstruction matrix encountered.'
      print,'Re-computing RMAT...'
      rmat = 0
    endif
  endif
  if not keyword_set(rmat)  then begin ;or n_zer ne (size(rmat,/dim))[1]
    rmat = recmat(n_zer, dimpup, pup, zermat)
  endif else begin
    n_zer = (size(rmat,/dim))[1]
  endelse
  calc_wfe_ls, sx_, sy_, pup, rmat, zermat, wfe_zer , zer_coeff, notilt = notilt
  if not keyword_set(silent) then print, 'number of zernike modes computed: ', n_zer
endif

; R&R wavefront reconstruction
if not keyword_set(ONLY_ZER) then wfe_it = slope_to_wavefront_ft(sx_, sy_, pup) else wfe_it = 0

; RMS and PtV computation
if keyword_set(waves) then begin
  if not keyword_set(ONLY_ZER) then wfe_it = wfe_it / lambda
  if not keyword_set(NOZER) then wfe_zer = wfe_zer / lambda
endif

if not keyword_set(ONLY_ZER) then begin
wfe_it_rms = stddev(wfe_it[where(pup)])     &     wfe_it_PtV = max(wfe_it[where(pup)]) - min(wfe_it[where(pup)])

 if not keyword_set(silent) then begin
    if not keyword_set(waves) then print, 'WFE RMS ='+ strcompress(string(wfe_it_rms)) + ' microns' else $
                                   print, 'WFE RMS ='+ strcompress(string(wfe_it_rms)) + ' waves (@0.633 microns)'
    if not keyword_set(waves) then print, 'WFE PtV ='+ strcompress(string(wfe_it_PtV)) + ' microns' else $
                                  print, 'WFE PtV ='+ strcompress(string(wfe_it_PtV)) + ' waves (@0.633 microns)'                                  
 endif
endif 

if not keyword_set(NOZER) then begin
  wfe_zer_rms = stddev(wfe_zer[where(pup)])    &     wfe_zer_PtV = max(wfe_zer[where(pup)]) - min(wfe_zer[where(pup)])
endif  
if not keyword_set(ONLY_ZER) then begin
   wfe_diff_rms = stddev(wfe_it[where(pup)]-wfe_zer[where(pup)])
   wfe_diff_PtV = max(wfe_it[where(pup)]-wfe_zer[where(pup)]) - min(wfe_it[where(pup)]-wfe_zer[where(pup)])
endif

;resistant_mean, wfe_it[where(pup)], sigma_cut, wfe_it_mean, wfe_it_rms_clip, _EXTRA = extra     
;wfe_it_PtV_clip = max((wfe_it[where(pup)])(where(abs(wfe_it[where(pup)]-wfe_it_mean) lt 3 * wfe_it_rms_clip ))) - min((wfe_it[where(pup)])(where(abs(wfe_it[where(pup)]-wfe_it_mean) lt 3 * wfe_it_rms_clip )))
;resistant_mean, wfe_zer[where(pup)], sigma_cut, wfe_zer_mean, wfe_zer_rms_clip, _EXTRA = extra
;wfe_zer_PtV_clip = max((wfe_zer[where(pup)])(where(abs(wfe_zer[where(pup)]-wfe_zer_mean) lt 3 * wfe_zer_rms_clip ))) - min((wfe_zer[where(pup)])(where(abs(wfe_zer[where(pup)]-wfe_zer_mean) lt 3 * wfe_zer_rms_clip )))


; display options
if keyword_set(display) then begin
  if not keyword_set(NOZER) and not keyword_set(ONLY_ZER) then begin
   im1 = image(wfe_it*pup,image_dim = [dimpup,dimpup],image_loc = [0,0], dim = [dimpup*3,dimpup*1.2],xrange = [0,dimpup*3], yrange = [-0.2*dimpup,dimpup])
   im2 = image(wfe_zer*pup,/over,image_dim = [dimpup,dimpup],image_loc = [dimpup,0])
   im3 = image(wfe_it*pup - wfe_zer*pup, /over, image_dim = [dimpup,dimpup],image_loc = [2*dimpup,0])
   text_tit_it = text(0.1, 0.15, 'Reconstructed Wavefront Roddier & Roddier',/current)
   text_tit_zer = text(0.4, 0.15, ' Modal Wavefront (first ' +strcompress(string(n_zer))+' modes)',/current)
   text_tit_diff = text(0.7, 0.15, ' Difference',/current)
   if not keyword_set(waves) then text_rms_it = text(0.1,0.1,'WFE RMS ='+ strcompress(string(wfe_it_rms)) + ' $\mu m$',/current) else $
                               text_rms_it = text(0.1,0.1,'WFE RMS ='+ strcompress(string(wfe_it_rms)) + ' waves (@0.633 $\mu m$)',/current)
   if not keyword_set(waves) then text_ptv_it = text(0.1,0.05,'WFE PtV ='+ strcompress(string(wfe_it_ptv)) + ' $\mu m$',/current) else $
                               text_ptv_it = text(0.1,0.05,'WFE PtV ='+ strcompress(string(wfe_it_ptv)) + ' waves (@0.633 $\mu m$)',/current)
   if not keyword_set(waves) then text_rms_zer = text(0.4,0.1,'WFE RMS ='+ strcompress(string(wfe_zer_rms)) + ' $\mu m$',/current) else $
                               text_rms_zer = text(0.4,0.1,'WFE RMS ='+ strcompress(string(wfe_zer_rms)) + ' waves (@0.633 $\mu m$)',/current)
   if not keyword_set(waves) then text_ptv_zer = text(0.4,0.05,'WFE PtV ='+ strcompress(string(wfe_zer_ptv)) + ' $\mu m$',/current) else $
                               text_ptv_zer = text(0.4,0.05,'WFE PtV ='+ strcompress(string(wfe_zer_ptv)) + ' waves (@0.633 $\mu m$)',/current)
   if not keyword_set(waves) then text_rms_diff = text(0.7,0.1,'WFE RMS ='+ strcompress(string(wfe_diff_rms)) + ' $\mu m$',/current) else $
                               text_rms_diff = text(0.7,0.1,'WFE RMS ='+ strcompress(string(wfe_diff_rms)) + ' waves (@0.633 $\mu m$)',/current)
   if not keyword_set(waves) then text_ptv_diff = text(0.7,0.05,'WFE PtV ='+ strcompress(string(wfe_diff_ptv)) + ' $\mu m$',/current) else $
                               text_ptv_diff = text(0.7,0.05,'WFE PtV ='+ strcompress(string(wfe_diff_ptv)) + ' waves (@0.633 $\mu m$)',/current)                               
   if not keyword_set(waves) then p = plot(findgen(n_zer), zer_coeff,dim = [2000,1000],xtitle = 'zernike mode', ytitle = 'WFE rms [um]', title = 'Zernike Coefficients', thick = 3) else $
                               p = plot(findgen(n_zer), zer_coeff,dim = [2000,1000],xtitle = 'zernike mode', ytitle = 'WFE rms [waves (@0.633 $\mu m$)]', title = 'Zernike Coefficients', thick = 3)
  if keyword_set(save_file) then im3.save, dir + 'ASONG_wfe'+ name + '_preview.jpg'
  endif 
  if keyword_set(NOZER) then begin
    im1 = image(wfe_it*pup,image_dim = [dimpup,dimpup],image_loc = [0,0], dim = [dimpup,dimpup*1.2],xrange = [0,dimpup], yrange = [-0.2*dimpup,dimpup])
    if not keyword_set(waves) then text_rms_it = text(0.1,0.1,'WFE RMS ='+ strcompress(string(wfe_it_rms)) + ' $\mu m$',/current) else $
      text_rms_it = text(0.1,0.1,'WFE RMS ='+ strcompress(string(wfe_it_rms)) + ' waves (@0.633 $\mu m$)',/current)
    if not keyword_set(waves) then text_ptv_it = text(0.1,0.05,'WFE PtV ='+ strcompress(string(wfe_it_ptv)) + ' $\mu m$',/current) else $
      text_ptv_it = text(0.1,0.05,'WFE PtV ='+ strcompress(string(wfe_it_ptv)) + ' waves (@0.633 $\mu m$)',/current)
     if keyword_set(save_file) then im1.save, dir + 'ASONG_wfe'+ name + '_preview.jpg'
  endif
  if keyword_set(ONLY_ZER) then begin
    im1 = image(wfe_zer*pup,image_dim = [dimpup,dimpup],image_loc = [0,0], dim = [dimpup,dimpup*1.2],xrange = [0,dimpup], yrange = [-0.2*dimpup,dimpup])
    if not keyword_set(waves) then text_rms_zer = text(0.1,0.1,'WFE RMS ='+ strcompress(string(wfe_zer_rms)) + ' $\mu m$',/current) else $
      text_rms_zer = text(0.1,0.1,'WFE RMS ='+ strcompress(string(wfe_zer_rms)) + ' waves (@0.633 $\mu m$)',/current)
      if not keyword_set(waves) then text_ptv_it = text(0.1,0.05,'WFE PtV ='+ strcompress(string(wfe_zer_ptv)) + ' $\mu m$',/current) else $
      text_ptv_it = text(0.1,0.05,'WFE PtV ='+ strcompress(string(wfe_zer_ptv)) + ' waves (@0.633 $\mu m$)',/current)
      if keyword_set(save_file) then im1.save, dir + 'ASONG_wfe'+ name + '_preview.jpg'
  endif
endif

if not keyword_set(silent) then begin
if not keyword_set(NOZER) then begin
  for i = 0,n_zer - 1 do begin
    print, 'zernike coefficient ' + strcompress(string(i)) + ' = ' + strcompress(string(zer_coeff[i]))
  endfor
endif
endif

;saving data
if keyword_set(save_file) then begin
  if keyword_set(dir) then begin
    if not keyword_set(NOZER) or not keyword_set(ONLY_ZER) then begin
     if not keyword_set(ONLY_ZER) then save, wfe_it, wfe_zer, zer_coeff, pup, wfe_zer_rms, wfe_it_rms, wfe_zer_PtV, wfe_it_PtV, filename = dir + 'ASONG_wfe'+ name + '.sav' $
      else save, wfe_zer, zer_coeff, pup, wfe_zer_rms, wfe_zer_PtV, filename = dir + 'ASONG_wfe'+ name + '.sav' 
     if keyword_set(save_rmat) then save,rmat, zermat, pup, filename = dir + 'rmat' + name + '.sav'     
     id = findgen(n_zer)
     openw, lun, dir + 'ASONG_Zernike_coeff'+ name + '.txt', /GET_LUN
     out=[transpose(id),transpose(zer_coeff)]
     if not keyword_set(waves) then printf,lun, '# id       Value in microns' else   printf,lun, '# id       Value in waves (@0.633 microns)''
     printf, lun, out, format='(I5,3x, F9.4)'  &  free_lun, lun     
    endif 
    if keyword_set(NOZER) then $
     save, wfe_it, pup, wfe_it_rms, wfe_it_PtV, filename = dir + 'ASONG_wfe'+ name + '.sav'
  endif else begin
   if not keyword_set(NOZER) or not keyword_set(ONLY_ZER) then begin
       if not keyword_set(ONLY_ZER) then save, wfe_it, wfe_zer, zer_coeff, pup, wfe_zer_rms, wfe_it_rms, wfe_zer_PtV, wfe_it_PtV, filename = 'ASONG_wfe' + name + '.sav' $
        else save, wfe_zer, zer_coeff, pup, wfe_zer_rms,  wfe_zer_PtV, filename = 'ASONG_wfe' + name + '.sav'  
      if keyword_set(save_rmat) then save, rmat, zermat, pup, filename =  'rmat' + name + '.sav'
      id = findgen(n_zer)
      openw, lun, 'ASONG_Zernike_coeff'+ name + '.txt', /GET_LUN
      out=[transpose(id),transpose(zer_coeff)]
      if not keyword_set(waves) then printf,lun, '# id       Value in microns' else   printf,lun, '# id       Value in waves (@0.633 microns)''
      printf, lun, out, format='(I5,3x, F9.4)'  &  free_lun, lun
    endif 
      if keyword_set(NOZER) then save, wfe_it, pup, wfe_it_rms, wfe_it_PtV, filename = 'ASONG_wfe' + name + '.sav'
  endelse
endif

return
end
 No newline at end of file