def CDIprobe(theta, iw): """ apply a probe shape to DM to achieve CDI The probe applied to the DM to achieve CDI is that originally proposed in Giv'on et al 2011, doi: 10.1117/12.895117; and was used with proper in Matthews et al 2018, doi: 10.1117/1.JATIS.3.4.045001. The pupil coordinates used in those equations relate to the sampling of the pupil (plane of the DM). However, in Proper, the prop_dm uses a DM map that is the size of (n_ao_act, n_ao_act). This map was resampled from its original size to the actuator spacing, and the spacing in units of [m] is supplied as a keyword to the prop_dm function during the call. All that is to say, we apply the CDI probe using the coordinates of the DM actuators, and supply the probe height as an additive height to the DM map, which is passed to the prop_dm function. :param theta: desired phase of the probe :param iw: index of wavelength number in ap.wvl_range :return: height of phase probes to add to the DM map in adaptive.py """ x = np.linspace(-1 / 2, 1 / 2, tp.ao_act) y = x X, Y = np.meshgrid(x, y) probe = cdip.probe_amp * np.sinc(cdip.probe_w * X) \ * np.sinc(cdip.probe_h * Y) \ * np.sin(2*np.pi*cdip.probe_center*X + theta) # dprint(f"CDI Probe: Min={np.min(probe)*1e9:.2f} nm, Max={np.max(probe)*1e9:.2f} nm") # if cdip.show_probe and iw == 0 and theta == cdip.phase_list[0]: # quick2D(probe, title=f"Phase Probe at " r'$\theta$' + f"={cdip.phase_list[iw]/np.pi:.2f}" + r'$\pi$', # vlim=(-1e-6, 1e-6), # colormap="YlGnBu_r") # logZ=True) # Testing FF propagation if cdip.show_probe and iw == 0 and theta == cdip.phase_list[0]: probe_ft = (1 / np.square(2 * np.pi)) * np.fft.ifftshift( np.fft.fft2(np.fft.fftshift(probe))) # quick2D(probe_ft.real, # title=f"Real FFT of CDI probe, " r'$\theta$' + f"={cdip.phase_list[iw]/np.pi:.2f}" + r'$\pi$', # vlim=(-1e-6, 1e-6), # colormap="YlGnBu_r") # quick2D(probe_ft.imag, # title=f"Imag FFT of CDI probe, " r'$\theta$' + f"={cdip.phase_list[iw]/np.pi:.2f}" + r'$\pi$', # vlim=(-1e-6, 1e-6), # colormap="YlGnBu_r") quick2D( np.arctan2(probe_ft.imag, probe.real), title="Phase of Probe", # vlim=(-1e-6, 1e-6), colormap="YlGnBu_r") return probe
def array_QE(self, plot=False): """Assigns each pixel a phase responsivity between 0 and 1""" dist = Distribution(gaussian(mp.g_mean, mp.g_sig, np.linspace(0, 1, mp.res_elements)), interpolation=True) QE = dist(self.array_size[0] * self.array_size[1])[0] / float( mp.res_elements) if plot: plt.xlabel('Responsivity') plt.ylabel('#') plt.hist(QE) plt.show() QE = np.reshape(QE, self.array_size[::-1]) if plot: quick2D(QE) #plt.imshow(QE) # plt.show() return QE
def responvisity_scaling_map(self, plot=False): """Assigns each pixel a phase responsivity between 0 and 1""" dist = Distribution(gaussian(mp.r_mean, mp.r_sig, np.linspace(0, 2, mp.res_elements)), interpolation=True) responsivity = dist(self.array_size[0] * self.array_size[1])[0] / float(mp.res_elements) * 2 if plot: plt.xlabel('Responsivity') plt.ylabel('#') plt.hist(responsivity) plt.show() responsivity = np.reshape(responsivity, self.array_size) if plot: quick2D(responsivity) #plt.imshow(QE) # plt.show() return responsivity
def retro_wfs(star_fields, wfo, plane_name='wfs'): """ Retrospective wfs (measure an old field) :param star_fields: :param wfo: :param plane_name: :return: """ WFS_map = np.zeros((len(star_fields), sp.grid_size, sp.grid_size)) from skimage.restoration import unwrap_phase for iw in range(len(star_fields)): quick2D(np.angle(star_fields), title='before mask', colormap='sunlight') phasemap = np.angle(star_fields[iw]) masked_phase = np.ma.masked_equal(phasemap, 0) quick2D(masked_phase, title='before unwrap', colormap='sunlight') WFS_map[iw] = unwrap_phase(masked_phase, wrap_around=[False, False]) WFS_map[iw][phasemap == 0] = 0 quick2D(WFS_map[iw], title='after') if 'retro_closed_wfs' in sp.save_list: wfo.save_plane(location='WFS_map') return WFS_map
planet_photons = np.array(planet_photons).T.astype(np.float32) planet_photons = np.insert(planet_photons, obj=1, values=cam.phase_cal(ap.wvl_range[0]), axis=0) planet_photons[0] = (planet_photons[0] + abs_step) * sp.sample_time photons = np.concatenate((star_photons, planet_photons), axis=1) # cam.save_photontable(photonlist=photons, index=None, populate_subsidiaries=False) stem = cam.arange_into_stem(photons.T, (cam.array_size[1], cam.array_size[0])) stem = list(map(list, zip(*stem))) stem = cam.remove_close(stem) photons = cam.ungroup(stem) photons = photons[[0, 1, 3, 2]] # grid(cam.rebin_list(photons), show=False) cam.populate_photontable(photons=photons, finalise=False) cam.populate_photontable(photons=[], finalise=True) grid(cam.rebin_list(photons), show=False) # to look at the photontable obs = Photontable(iop.photonlist) print(obs.photonTable) # to plot an image of all the photons on the array image = obs.getPixelCountImage(integrationTime=None)['image'] quick2D(image, show=False, title='Const planet photons') plt.show(block=True)
fp_sampling = np.copy(sampling[cpx_sequence.shape[1] - 1, :]) # numpy arrays have some weird effects that make copying the array necessary # ======================================================================= # Plotting # ======================================================================= # White Light, Last Timestep if sp.show_wframe: # vlim = (np.min(spectralcube) * 10, np.max(spectralcube)) # setting z-axis limits img = np.sum(focal_plane[sp.numframes - 1], axis=0) # sum over wavelength quick2D( opx.extract_center(img), #focal_plane[sp.numframes-1]), title=f"White light image at timestep {sp.numframes} \n" # img f"AO={tp.use_ao}", # f"Grid Size = {sp.grid_size}, Beam Ratio = {sp.beam_ratio} ", # f"sampling = {sampling*1e6:.4f} (um/gridpt)", logZ=True, dx=fp_sampling[0], vlim=(1e-7, 1e-3)) # Plotting Spectra at last tstep if sp.show_spectra: tstep = sp.numframes - 1 view_spectra( focal_plane[sp.numframes - 1], title=f"Intensity per Spectral Bin at Timestep {tstep} \n" f" AO={tp.use_ao}", # f"Beam Ratio = {sp.beam_ratio:.4f}",# sampling = {sampling*1e6:.4f} [um/gridpt]", logZ=True, subplt_cols=sp.spectra_cols,
def deformable_mirror(wf, WFS_map, iter, previous_output=None, apodize=False, plane_name='', debug=False): """ combine different DM actuator commands into single map to send to prop_dm prop_dm needs an input map of n_actuators x n_actuators in units of actuator command height. quick_ao will handle the conversion to actuator command height, and the CDI probe must be scaled in cdi.probe_amp in params in units of m. Each subroutine is also responsible for creating a map of n_actuators x n_actuators spacing. prop_dm handles the resampling of this map onto the wavefront, including the influence function. Its some wizardry that happens in c, and presumably it is taken care of so you don't have to worry about it. In the call to proper.prop_dm, we apply the flag tp.fit_dm, which switches between two 'modes' of proper's DM surface fitting. If FALSE, the DM is driven to the heights specified by dm_map, and the influence function will act on these heights to define the final surface shape applied to the DM, which may differ substantially from the initial heights specified by dm_map. If TRUE, proper will iterate applying the influence function to the input heights, and adjust the heights until the difference between the influenced-map and input map meets some proper-defined convergence criterea. Setting tp.fit_dm=TRUE will obviously slow down the code, but will (likely) more accurately represent a well-calibrated DM response function. much of this code copied over from example from Proper manual on pg 94 :param wf: single wavefront :param WFS_map: wavefront sensor map, should be in units of phase delay :param previous_output: :param iter: the current index of iteration (which timestep this is) :param plane_name: name of plane (should be 'woofer' or 'tweeter' for best functionality) :return: nothing is returned, but the probe map has been applied to the DM via proper.prop_dm. DM plane post DM application can be saved via the sp.save_list functionality """ assert np.logical_xor(WFS_map is None, previous_output is None) # AO Actuator Count from DM Type if plane_name == 'tweeter' and hasattr(tp, 'act_tweeter'): nact = tp.act_tweeter elif plane_name == 'woofer' and hasattr(tp, 'act_woofer'): nact = tp.act_woofer else: nact = tp.ao_act # DM Coordinates nact_across_pupil = nact - 2 # number of full DM actuators across pupil (oversizing DM extent) dm_xc = ( nact / 2 ) # The location of the optical axis (center of the wavefront) on the DM in dm_yc = ( nact / 2 ) # actuator units. First actuator is centered on (0.0, 0.0). The 0.5 is a # parameter introduced/tuned by Rupert to remove weird errors (address this). # KD verified this needs to be here or else suffer weird errors 9/19 # TODO address/remove the 0.5 in DM x,y coordinates ############################ # Creating DM Surface Map ############################ d_beam = 2 * proper.prop_get_beamradius(wf) # beam diameter act_spacing = d_beam / nact_across_pupil # actuator spacing [m] ####### # AO ####### if previous_output is not None and WFS_map is None: dm_map = update_dm(previous_output) else: dm_map = quick_ao(wf, nact, WFS_map[wf.iw]) ######### # Waffle ######### if tp.satelite_speck['apply'] and plane_name is not 'woofer': waffle = make_speckle_kxy(tp.satelite_speck['xloc'], tp.satelite_speck['yloc'], tp.satelite_speck['amp'], tp.satelite_speck['phase']) waffle += make_speckle_kxy(tp.satelite_speck['xloc'], -tp.satelite_speck['yloc'], tp.satelite_speck['amp'], tp.satelite_speck['phase']) dm_map += waffle ####### # CDI ###### if cdi.use_cdi and plane_name == cdi.which_DM: theta = cdi.phase_series[iter] if not np.isnan(theta): # dprint(f"Applying CDI probe, lambda = {wfo.wsamples[iw]*1e9:.2f} nm") cdi.save_tseries(iter, datetime.datetime.now()) probe = config_probe(theta, nact, iw=wf.iw, ib=wf.ib, tstep=iter) dm_map = dm_map + probe # Add Probe to DM map ######################### # Applying Piston Error ######################### if tp.piston_error: mean_dm_map = np.mean(np.abs(dm_map)) var = 1e-4 # 1e-11 dm_map = dm_map + np.random.normal(0, var, (dm_map.shape[0], dm_map.shape[1])) ######################### # proper.prop_dm ######################### dmap = proper.prop_dm(wf, dm_map, dm_xc, dm_yc, act_spacing, FIT=tp.fit_dm) # if debug and wf.iw == 0 and wf.ib == 0 and iter == 0: dprint(plane_name) check_sampling(wf, iter, plane_name + ' DM pupil plane', getframeinfo(stack()[0][0]), units='mm') quick2D(WFS_map[wf.iw], title=f"WFS map after masking", zlabel='unwrapped phase (rad)', vlim=[-3 * np.pi, 3 * np.pi]) fig, ax = plt.subplots(1, 1) cax = ax.imshow(dm_map * 1e9, interpolation='none', origin='lower') plt.title(f'{plane_name} dm_map (actuator coordinates)') cb = plt.colorbar(cax) cb.set_label('nm') plt.show() post_ao = unwrap_phase( proper.prop_get_phase(wf)) * wf.lamda / (2 * np.pi) # quick2D(pre_ao_dist*1e9, title='unwrapped wavefront before DM', zlabel='nm', show=False) # , vlim=(-0.5e-7,0.5e-7)) # quick2D(np.abs(pre_ao_amp)**2, title='Pre-AO Intensity', show=False)#, vlim=(-0.5e-7,0.5e-7)) # quick2D(dmap, title='the phase map prop_dm is applying', zlabel='distance (m)', show=False)#, vlim=(-0.5e-7,0.5e-7)) # plt.figure() # plt.plot(pre_ao_dist[len(pre_ao_dist)//2], label=f'pre_ao 1D cut, row {len(pre_ao_dist)//2}') # plt.plot(2*dmap[len(dmap)//2], label=f'dmap 1D cut (x2), row {len(dmap)//2}') # plt.plot((pre_ao_dist + (2*dmap))[len(dmap)//2], label='difference') # plt.legend() # plt.xlim(sp.grid_size//2*np.array([1-sp.beam_ratio*1.1, 1+sp.beam_ratio*1.1])) # quick2D(pre_ao + (2*dmap), title='diff', zlabel='m', show=False, vlim=(-0.5e-7,0.5e-7)) # quick2D(post_ao, title='unwrapped wavefront after DM', zlabel='m', show=True, vlim=(-0.5e-7,0.5e-7)) # quick2D(np.abs(proper.prop_get_amplitude(wf))**2, title='wavefront after DM intensity', show=False) # quick2D(proper.prop_get_phase(wf), title='wavefront after DM in phase units', zlabel='Phase', # show=True) # colormap='sunlight', if apodize: hardmask_pupil(wf) return dmap
# grid(photons, title='Spectra with MKIDs', vlim=[0,800], cmap='YlGnBu_r') ## ======================================================================= # Plotting # ======================================================================= # White Light, Last Timestep if sp.show_wframe: if not mp.convert_photons: img = np.sum(focal_plane[sp.numframes - 1], axis=0) # sum over wavelength quick2D( opx.extract_center(img), # focal_plane[sp.numframes-1]), title=f"White light image at timestep {sp.numframes} \n" # img f"CDI={cdi.use_cdi}, Atmos={tp.use_atmos}, Aberrations={tp.use_aber}", # f"Grid Size = {sp.grid_size}, Beam Ratio = {sp.beam_ratio} ", # f"sampling = {sampling*1e6:.4f} (um/gridpt)", logZ=True, dx=fp_sampling[0], vlim=(None, None), show=False) # (1e-3, 1e-1) else: img = np.sum(photons[sp.numframes - 1], axis=0) quick2D( img, # focal_plane[sp.numframes-1]), title=f"MEC White Light Image \n", # img # f"AO={tp.use_ao}, CDI={cdi.use_cdi} ", # f"Grid Size = {sp.grid_size}, Beam Ratio = {sp.beam_ratio} ", # f"sampling = {sampling*1e6:.4f} (um/gridpt)", logZ=False, # dx=fp_sampling[0], zlabel='photon counts',
class vel(): def __init__(self, num_samp=3): self.median_val = 10 # self.multiplier = np.logspace(np.log10(0.1), np.log10(10), num_samp) self.multiplier = np.array([0]) self.vals = self.multiplier * self.median_val if __name__ == '__main__': metric_config = vel() for i, val in enumerate(metric_config.vals): atmp.vel = val name = f'{TESTDIR}/{val}' sim = RunMedis(name=name, product=product) observation = sim() if product == 'fields': grid(observation['fields'], show=False)#, vlim=(-2e-7,2e-7)) elif product == 'photons': grid(sim.cam.rebin_list(observation['photons']), show=False) # to look at the photontable obs = ObsFile(iop.photonlist) print(obs.photonTable) # to plot an image of all the photons on the array image = obs.getPixelCountImage(integrationTime=None)['image'] quick2D(image, show=False, title=f'{val}') plt.show(block=True)
# np.vstack((dist, np.zeros((4)))), # np.vstack((np.zeros((4)), dist)), # -np.vstack((dist, np.zeros((4)))), # -np.vstack((np.zeros((4)), dist)) # )).T # # ap.contrast = 10**np.repeat([-3.5,-4,-4.5,-5],4) atmp.cn_sq = 5e-11 # # ap.star_flux *= 20 # ap.contrast = np.repeat([4, 2, 1, 0.5],4)*1e-4 ap.companion = False # tp.use_atmos = False tp.use_ao = True ap.star_flux *= 0.1 # sp.debug = True sp.quick_detect = False name = f'{TESTDIR}' sim = RunMedis(name=name, product='fields') observation = sim() # grid(observation['fields'], show=False, nstd=5)#, vlim=(-2e-7,2e-7)) cam = Camera(usesave=sp.save_to_disk, product=product) # fields = [observation['fields'][0]]*100 # fields = np.repeat(observation['fields'], 100, 0) # dprint(fields.shape) observation = cam(observation['fields']) # grid(cam.rebinned_cube) Ic = np.sum(cam.rebinned_cube, axis=(0, 1)) np.save(os.path.join(iop.testdir, 'Ic.npy'), Ic) quick2D(Ic) Ic = np.load(os.path.join(iop.testdir, 'Ic.npy')) quick2D(Ic)