def save_fits(data, defocus_distance, center, choose): ''' Save a fits with a simple header. Parameters ---------- data : numpy array Image to save defocus_distance : float Information about the defocus distance to insert in the header center : tuple, array Invormation about the coordinate to insert in the header choose : int is a variable that determines the star used ''' log.info(hist()) name = ['Kolmogorov', 'Speckle_Sum', 'Simple_seeing'] choose = choose-1 hdul = fits.PrimaryHDU(data) #save the file hdr = hdul.header hdr['RA'] = (center[0], "Right Ascension in decimal hours" ) hdr['DEC'] = (center[1], "Declination in decimal degrees") hdr['IMGTYPE'] = 'object' hdr['IMAGETYP'] = 'object' hdr['BZERO'] = 32768 hdr['DEFOCUS'] = (defocus_distance, "[mm] Distance from focal plane") hdul.scale('int16', bzero=32768) hdul.writeto(f'try_{name[choose]}.fits', overwrite = True)
def atmospheric_attenuation(magnitudo, photo_filter, AirMass): ''' This function takes the magnitudo of a star and gives back the same magnitudo attenuated by the atmosphere. It download the extinction coefficients calling data_loader Parameters ---------- magnitudo : array (float elements) An element of magnitudo is the magnitudo of a star in a specific range (photo_filter) photo_filter : list (string elements) It is a list where a element is a string that identifies a filter, e.g. "U","R","I" AirMass : float The AirMass provide the thickness of the atmosphere crossed by the light Output ------ magnitudo : array The array contains the elements attenuated ''' log.debug(hist()) sub = DATA["Extinction_coefficient"] extinction_coefficient = [sub[k] for k in photo_filter if k in sub] for i in range(len(magnitudo)-1): #if magnitudo[i] == 0: # magnitudo[i] = 0 #else: # magnitudo[i] = magnitudo[i]-AirMass*extinction_coefficient[i] magnitudo[i] = magnitudo[i]+AirMass*extinction_coefficient[i] return magnitudo
def magnitudo_to_photons(magnitudo, photo_filter): ''' This function convertes the magnitudo of a star in number of photons/second. It download informations calling data_loader Parameters ---------- magnitudo : array (float elements) An element of magnitudo is the magnitudo of a star in a specific range (photo_filter) photo_filter : list (string elements) It is a list where a element is a string that identifies a filter, e.g. "U","R","I" Output ------ photons : array The elements of the array are the number of photons for a specific range ''' log.debug(hist()) sub = DATA["Central_wavelenght"] central_wavelenght = [sub[k] for k in photo_filter if k in sub] number = magnitudo for i in range(len(magnitudo)-1): if magnitudo[i] == 0: number[i] = 0 else: exp = 6.74-0.4*magnitudo[i] number[i] = (10**exp)#/(central_wavelenght[i]) return number
def photons_to_electrons(photons, photo_filter): ''' This function takes the number of photons/sec and converts them in number of electron/sec on the CCD. It calls data_loader to download the quantum efficiency of the CCD Parameters ---------- photons : array The elements of the array are the number of photons for a specific range photo_filter : list (string elements) It is a list where a element is a string that identifies a filter, e.g. "U","R","I" Output electron : array The elements of the array are the number of electrons for a specific range ''' log.debug(hist()) sub = DATA["Quantum_efficiency"] quantum_efficiency = [sub[k] for k in photo_filter if k in sub] electrons = photons for i in range(len(photons)-1): electrons[i] = photons[i]*quantum_efficiency[i] return electrons
def saturation_controll(image): ''' The value of a pixel can't overcome the maximum value of a 16bit memory (65535) and it is called saturated. This function will controll that no pixel would surpass this value and, if it does, this function will reset its value to 65535 Parameter --------- image : numpy ndarray It is the image to controll Output ------ image : numpy ndarray The image controlled and adjusted ''' log.info(hist()) size = image.shape for i in range(size[0]): for j in range(size[1]): if image[i, j] >= 65535: image[i, j] = 65535 return image
def physical_CCD(CCD_structure): ''' Generates a CCD with a given structure Parameters ---------- CCD_structure : array The varies entries must have the measure on the CCD [0] : int, float; the x length of the CCD in mm [1] : int, float; the y length of the CCD in mm [2] : float ; the length of a pixel in mm Outputs ------- CCD : numpy ndarray The image of the CCD x : numpy ndarray The x variable on the CCD y : numpy ndarray The y variable on the CCD ''' log.info(hist()) len_y = int(CCD_structure[0]/CCD_structure[2])//2 len_x = int(CCD_structure[1]/CCD_structure[2])//2 x, y = np.mgrid[-len_x:len_x,-len_y:len_y] r = np.sqrt(x**2+y**2) CCD = np.piecewise(r, [r], [0]) return CCD
def intensity_aberration(aperture, scale, modes, pupil): '''Creates the amplitude atmospheric aberration. Parameters ---------- aperture : int The aperture of the telescope in mm scale : int The scale of the image generated modes : int Number of zernikle terms to use pupil : numpy ndarray The image of the aperture Output ------ zernike : numpy ndarray The image of the aberration overlapped with the image of the aperture ''' log.info(hist()) nx_size= aperture//scale zernike = [] zernike_array = ZA(modes, nx_size) for i in range(modes): zernike.append(reshape_aber(zernike_array[i], scale, pupil)) return zernike
def defocus(pupil, defocus_distance, r, wavelenght): ''' Takes the image and created the FT at some distance Parameters ---------- pupil : numpy ndarray The image of the aperture to transform defocus_distance : float The distance of the defocus in mm in range(-2.5,2.5) r : numpy ndarray The radial variable on the pupil wavelenght : float The center wavelength in mm Output ------ image : numpy ndarray The FT of the pupil at defocus_distance from the focus plane, AKA the PSF ''' log.info(hist()) phaseAngle = 1j*defocus_distance*10*np.sqrt((2*np.pi/wavelenght)**2-r**2+0j) #unnecessary 0j but keeping it for complex reasons kernel = np.exp(phaseAngle) defocusPupil = pupil * kernel defocusPSFA = fft.fftshift(fft.fft2(defocusPupil)) image = np.abs(defocusPSFA) return image
def reshape_aber(image, scale, pupil): ''' Adapt the aberration to the pupil size Parameters ---------- image : int The aperture of the telescope in mm scale : int The scale of the image generated pupil : numpy ndarray The image of the aperture Output ------ image : numpy ndarray The image of the aberration with the right shape to be combinated with the aperture ''' log.info(hist()) image = np.repeat(np.repeat(image, scale, axis = 0), scale, axis = 1) units= int((np.shape(pupil)[0]-np.shape(image)[0])/2) image = np.pad(image,(units), mode = 'constant') return image
def VEGA_to_AB(magnitudo, photo_filter): ''' This function takes the magnitudo of a star in the VEGA system and converts it in the AB system. It download the conversion table calling data_loader Parameters ---------- magnitudo : array (float elements) An element of magnitudo is the magnitudo of a star in a specific range (photo_filter) photo_filter : list (string elements) It is a list where a element is a string that identifies a filter, e.g. "U","R","I" Output ------ magnitudo : array The array contains the elements converted ''' log.debug(hist(photo_filter)) sub = DATA["Convertion_table"] convertion_table = [sub[k] for k in photo_filter if k in sub] for i in range(len(magnitudo)-1): if magnitudo[i]== 0: magnitudo[i]= 40 else: magnitudo[i] = magnitudo[i] - convertion_table[i] magnitudo[i] = magnitudo[i] - convertion_table[i] return magnitudo
def radius_sky_portion(CCD_structure): ''' This function uses the information on the CCD to estimate the radius of the portion of the sky visualized Parameters ---------- CCD_structure : array (float elements) This object contains the information about the measure of the CCD [0] : the length of the CCD on the x axis in mm [1] : the length of the CCD on the y axis in mm [2] : the length of a single pixel in mm Output ------ radius : astropy.units.quantity.Quantity it is the radius in an astropy undestandable format ''' log.debug(hist()) pixel_on_x = CCD_structure[0]/CCD_structure[2] pixel_on_y = CCD_structure[1]/CCD_structure[2] diagonal_diameter = np.sqrt((pixel_on_x)**2+(pixel_on_y)**2) radius_on_pixel = diagonal_diameter/2 radius = radius_on_pixel*CCD_structure[3]/60 radius = (radius/60)*u.deg return radius
def read_out_noise(image, amount, gain=1.0): ''' This function provides the errors introduced with the readout of the CCD Parameters ---------- image : numpy ndarray #can be changed with the only shape The image of the CCD, used by the function to extract the shape amount : float, int The central value of the distribution of the errors, it should be related with the background (the sqrt) gain : float, optional The value of the gain used by the CCD Output ------ noise : numpy ndarray The image of the CCD with the readout noise generated ''' log.info(hist()) shape = image.shape noise = np.random.normal(scale=amount / gain, size=shape) return noise
def converter_to_pixel(CCD_resolution, focal_lenght, quantity_to_convert): #TO REVISIONATE '''Convert a quantity on the aperture to pixels on CCD ''' log.info(hist()) converter_rad_arc = 180*3600/np.pi quantity_to_convert = quantity_to_convert quantity_angle = np.arctan(quantity_to_convert/focal_lenght) * converter_rad_arc Q_on_CCD = quantity_angle / CCD_resolution return Q_on_CCD
def sensitivity_variations(image, vignetting=True, dust=True): ''' The sensivity isn't constant, but can vary over the CCD, tipically with a gaussian trend and can be worsen by the presence of the dust. This function provides for this, creating the flat frame Parameters ---------- image : numpy ndarray #can be changed with the only shape nect The image of the CCD, used by the function to extract the shape vignetting : bool, optional If True, the gaussian figure is created on a image with the shape of the CCD dust : bool, optional If True, there will be added the donut generated by the dust on the CCD image Output ------ sensitivity :numpy ndarray The image of the CCD with a big gaussuan curve to simulate the variability of the sensivity ''' log.info(hist()) sensitivity = np.zeros_like(image) + 1.0 shape = np.array(sensitivity.shape) if dust or vignetting: # I don't know why, but y,x not x,y y, x = np.indices(sensitivity.shape) if vignetting: #TODO, centro gaussiana da spostare # Generate very wide gaussian centered on the center of the image, # multiply the sensitivity by it. #narrowing = np.random.randint(5,10) vign_model = Gaussian2D(amplitude=1, x_mean=shape[0] / 2, y_mean=shape[1] / 2, x_stddev=2 * (shape.max()), y_stddev=2 * (shape.max())) vign_im = vign_model(x, y) sensitivity *= vign_im if dust: dust_im = add_donuts(image, number=20) dust_im = dust_im / dust_im.max() sensitivity *= dust_im return sensitivity
def load_measure(): ''' Load the basic data of the telescope and the CCD from a JSON file Outputs ------- telescope_structure : list a list that contains the information about the telescope telescope_structure[0] = f_l : int It is the focal length of the telescope in mm telescope_structure[1] = ape : int It is the aperture of the telescope in mm telescope_structure[2] = obs : int It is the central obstruction of the telescope in mm telescope_structure[3] = wav : float It is the central wavelength of the sensible spectrum of the CCD in mm ccd_structure : array a array that contains the information about the CCD ccd_structure[0] = C_x : float It is the length of the CCD along the x axis in mm ccd_structure[0] = C_y : float It is the length of the CCD along the y axis in mm ccd_structure[0] = pix : float It is the lenght of a single pixel in mm ccd_data : list a list that cointains information about the errors generated in the CCD ccd_data[0] = gain : float the gain of the CCD ccd_data[1] = read_out_electrons : float the electrons that generate the read out noise ''' log.info(hist()) #filename = "Antola_data.json" #filename = "San_Pedro_data.json" filename = "San_Pedro_data_CCD2.json" f = open(filename, "r") data = json.load(f) Telescope = data['Telescope'] f_l = Telescope['focal_lenght'] ape = Telescope['aperture'] obs = Telescope['obstruction'] wav = Telescope['wavelenght'] CCD = data['CCD'] C_x = CCD['CCD_x'] C_y = CCD['CCD_y'] pix = CCD['pixels'] gain = CCD['gain'] read_out_electrons = CCD["read_out_electrons"] f.close() telescope_structure = (f_l, ape, obs, wav) ccd_structure = [C_x, C_y, pix] ccd_data = (gain, read_out_electrons) return telescope_structure, ccd_structure, ccd_data
def Data_structure(): ''' This function simply creates the data structure used in query ''' log.debug(hist()) Coord_x = [] Coord_y = [] Flux_tot = [] data = [Coord_x, Coord_y, Flux_tot] return data
def sky_background_aperture(focal_lenght, aperture, obstruction, trellis=True, atmosphere=False): ''' Calls telescope, intensity_aberration and phase_aberration to create the optical figure at the aperure Parameters ---------- focal_lenght : int The focal length of the telescope in mm aperture : int The aperture of the telescope in mm obstruction : int The central obstraction of the telescope in mm trellis : bool, optional If trellis == True the structure that hold on the obstruction is drawn, else the structure is not drawn atmosphere : bool, optional If atmosphere == True intensity_aberration and phase_aberration are called, else they are not Output ------ pupil : numpy ndarray The image of the aperture with all the aberrations r : numpy ndarray The radial variable on the pupil ''' log.info(hist()) pupil, r = telescope(focal_lenght, aperture, obstruction, trellis) if atmosphere: #zer = intensity_aberration(aperture, scale, modes, pupil) #zernike = zer[0] #for i in range(modes-1): # a = np.random.random() # zernike += (a/5)*zer[i] phase = phase_aberration(aperture, scale, D, r0, L0, pupil) #Small aperture, long exposure, the kolmogorov turbulance on small scale are no #phase = np.sqrt(np.abs(phase))**2 kernel = pupil * phase pupil = pupil * np.exp(1j*kernel+0j) return pupil, r
def Coordinator(coord, center, CCD_structure, catalog): ''' The function takes the coordinates of a star and uses the WCS keywords to give back the position on the CCD in pixel Parameters ---------- coord : SkyCoord It is the coordinates of the star already elaborated by astropy center : array It must contain the position of the center of the CCD and it is used to obtain the distance of the star from it CCD_res : float It is the resolution of the CCD in arcsec/pixel Outputs ------- x : float It is the position of the star on the grid of the CCD along the axis x y : float It is the position of the star on the grid of the CCD along the axis y ''' log.info(hist()) if catalog == 'Simbad': offset_x = CCD_structure[0]/CCD_structure[2] offset_y = CCD_structure[1]/CCD_structure[2] w = wcs.WCS(naxis=2) w.wcs.crpix = [1, 1] w.wcs.crval = [center[0], center[1]] w.wcs.ctype = ["RA", "DEC"] x,y = wcs.utils.skycoord_to_pixel(coord, w) x = ((x*3600)/CCD_structure[3]) + offset_x/2 #(deg*arc/sec*deg)*arc/sec*pixel+offset y = ((y*3600)/CCD_structure[3]) + offset_y/2 elif catalog == 'Gaia': offset_x = CCD_structure[0]/CCD_structure[2] offset_y = CCD_structure[1]/CCD_structure[2] w = wcs.WCS(naxis=2) w.wcs.crpix = [1, 1] w.wcs.crval = [center[0], center[1]] w.wcs.ctype = ["RA", "DEC"] x,y = wcs.utils.skycoord_to_pixel(coord, w) x = ((x*3600)/CCD_structure[3]) + offset_x/2 #(deg*arc/sec*deg)*arc/sec*pixel+offset y = ((y*3600)/CCD_structure[3]) + offset_y/2 return y, x
def make_cosmic_rays(image, number, strength=10000): ''' It can appens that during an acquisition of an image some pixel are "saturated" by the cosmic rays, this function provides for a CCD image with this effect Parameters ---------- image : numpy ndarray #can be changed with the only shape The image of the CCD, used by the function to extract the shape number : int This number is the number of cosmic ray within a single image strenght : int, optional This is the intensity of a cosmic ray on a pixel Output ------ cosmic_image :numpy ndarray The image of the CCD with the pixel overflowed by the cosmic rays generated ''' log.info(hist()) cosmic_image = np.zeros_like(image) # Yes, the order below is correct. The x axis is the column, which # is the second index. max_y, max_x = cosmic_image.shape # Get the smallest dimension to ensure the cosmic rays are within the image maximum_pos = np.min(cosmic_image.shape) # These will be center points of the cosmic rays, which we place away from # the edges to ensure they are visible. xy_cosmic = np.random.randint(0.1 * maximum_pos, 0.9 * maximum_pos, size=[number, 2]) cosmic_length = 5 # pixels, a little big cosmic_width = 2 theta_cosmic = 2 * np.pi * np.random.rand() apertures = EllipticalAperture(xy_cosmic, cosmic_length, cosmic_width, theta_cosmic) masks = apertures.to_mask(method='center') for mask in masks: cosmic_image += strength * mask.to_image(shape=cosmic_image.shape) return cosmic_image
def telescope_on_CCD(CCD_resolution, binning, telescope_structure, defocus_distance, trellis=True, atmosphere=False): ''' This function calls sky_backgroud_aperture, defocus and image_processing to create the sample on the PSF on the CCD Parameters ---------- CCD_resolution : float The resolution on the CCD in arcsec/pixel telescope_structure : list telescope_structure[0] = focal_lenght : int The focal length of the telescope in mm telescope_structure[1] = aperture : int The aperture of the telescope in mm telescope_structure[2] = obstruction : int The central obstraction of the telescope in mm telescope_structure[3] = wavelenght : float The center wavelength in mm defocus_distance : float The distance of the defocus in mm in range(-2.5,2.5) trellis : bool, optional If trellis == True the structure that hold on the obstruction is drawn, else the structure is not drawn atmosphere : bool, optional If atmosphere == True intensity_aberration and phase_aberration are called, else they are not Output ------ image : numpy ndarray The image of the PSF ''' log.info(hist()) #units = converter_to_pixel(CCD_resolution, focal_lenght, 1) #convert 1mm on the aperture in pixel on CCD units = CCD_resolution//0.12 #empiric value image, r = sky_background_aperture(telescope_structure[0], telescope_structure[1], telescope_structure[2], trellis, atmosphere) #creates the aperture image = defocus(image, defocus_distance, r, telescope_structure[3]) #creates the image on the screen image = image_processing(image, binning, telescope_structure[1], atmosphere) return image
def dark_current(image, current, exposure_time, gain=1.0, hot_pixels=False): ''' This function creates a matrix with the shape of the CCD with the errors introduced by the dark current with a poissonian distribution. It also provide for the presence of hot pixels in the CCD Parameters ---------- image : numpy ndarray #can be changed with the only shape The image of the CCD, used by the function to extract the shape current : float This is the value of the dark current for 1 second exposure_time : int The number of second used for obtainig the CCD's image gain : float, optional The value of the gain used in the CCD, more the gain, more the errors hot_pixel : bool, optional This flag allows to choose if there will by the hot pixels or not if True there will be hot pixels Output ------ dark_bias : numpy ndarray The image of the CCD with the dark current noise generated ''' log.info(hist()) base_current = current * exposure_time / gain dark_bias = np.random.poisson(base_current, size=image.shape) if hot_pixels: '''set the probability of 0.01% of a pixel to be hot''' y_max, x_max = dark_bias.shape numb_hot = int(0.0001 * x_max * y_max) hot_x = np.random.randint(0, x_max, size=numb_hot) hot_y = np.random.randint(0, y_max, size=numb_hot) hot_current = 10000 * current dark_bias[[hot_y, hot_x]] = hot_current * exposure_time #/gain return dark_bias
def image_processing(image, m, aperture, atmosphere): ''' This function takes an image and some parameters to obtain the same image cut and strechetd to fit the CCD. Also, this function takes only the essentioal information thus reducing the total weight of the final image Parameters ---------- image : numpy ndarray The image, usually the PSF m : int The binnig, used to re-sum the PSF pixels aperture : int The aperture of the telescope in mm atmosphere : bool To the long exposure Output ------ new_image : numpy ndarray The image cleaned with only the good parts and with the right measure ''' log.info(hist()) image = (np.abs(image))**2 size = image.shape if atmosphere: m = m*4 new_size = (size[0]//m, size[1]//m) new_image = np.zeros(new_size) if m==1: new_image = image elif m>=2: for i in range(new_size[0]): for j in range(new_size[1]): new_image[[i],[j]] = sum_image(image, i, j, m) else: print('binning problem, PSF binnig ignored') new_image = image new_image = (new_image/(np.sum(new_image)))*0.2 new_image = rotate(new_image, angle=45) return new_image
def phase_aberration(aperture, scale, D, r0, L0, pupil): ''' Creates an image with the Kolmogorov algorithm with the phase atmospheric aberration. This function works but usually is not used because the telescope is limited by the seeing. Parameters ---------- aperture : int The aperture of the telescope in mm scale : int The scale of the image generated D : int #inutile e rindondante con aperture, da cambiare e togliere r0 : float The Fried parameter of the 'seeing' L0 : float, int The outer scale of the 'seeing' pupil : numpy ndarray The image of the aperture Output ------ phase_screen : numpy ndarray The image of the turbolence overlapped with the image of the aperture ''' log.info(hist()) nx_size= aperture//scale plx_scale = D/nx_size phase_screen = PhaseScreenKolmogorov(nx_size, plx_scale, r0, L0) phase_screen.add_row() phase_screen = phase_screen.scrn phase_screen = reshape_aber(phase_screen, scale, pupil) return phase_screen
def magnitudo_to_electrons(magnitudo, photo_filters, AirMass, exposure_time, Controll): ''' This functions calls VEGA_to_AB, atmospheric_attenuation, magnitudo_to_photons, photons_to_electrons to obtain the total electrons generated in a CCD by the light of a star Parameters ---------- magnitudo : array (float elements) An element of magnitudo is the magnitudo of a star in a specific range (photo_filter) photo_filter : list (string elements) It is a list where a element is a string that identifies a filter, e.g. "U","R","I" AirMass : float The AirMass provide the thickness of the atmosphere crossed by the light exposure_time : int It is the exposure time used to obtain the image, it is in seconds Output ------ tot : float It is the total number of electrons generated by the star on the CCD ''' log.debug(hist()) magnitudo = VEGA_to_AB(magnitudo, photo_filters) magnitudo = atmospheric_attenuation(magnitudo, photo_filters, 1) photons = magnitudo_to_photons(magnitudo, photo_filters) electrons = photons_to_electrons(photons, photo_filters) if Controll: for n, i in enumerate(photo_filters): if i == "V": if electrons[n] <= 3e-4: electrons[n] = electrons[-1] tot = (sum(electrons)-electrons[-1])*exposure_time else: tot = sum(electrons)*exposure_time return tot
def sky_brightness(plate_scale, x_pix, y_pix, photo_filters, exposure_time, moon_phase=3): log.info(hist()) ''' moon_phase = int, optional moon_phase can go to 0 (new moon) to 3 (full moon) with intermedian phases 1 (7 day from new moon) and 2 (10 day from new moon) ''' sub = DATA["Sky_brightness"] for n, i in enumerate(photo_filters): if i == "g": photo_filters[n] = "V" sky_brightness = [sub[k] for k in photo_filters if k in sub] x_arcsec = plate_scale * x_pix y_arcsec = plate_scale * y_pix Area = x_arcsec * y_arcsec magnitudo_ab_sky = [] magnitudo_ab_sky.append(sky_brightness[0][moon_phase] - 2.5*np.log10(Area)) photons = magnitudo_to_photons(magnitudo_ab_sky, photo_filters) electrons = photons_to_electrons(photons, photo_filters) tot = sum(electrons)*exposure_time return tot
def bias(image, value, realistic=False): ''' This function creates a matrix with the shape of the CCD with the bias and some bad colums Parameters ---------- image : numpy ndarray #can be changed with the only shape The image of the CCD, used by the function to extract the shape value: int It is the bias level to add at the CCD realistic : bool, optional If True there will be add some bad columns at the CCD Output ------ bias_image : numpy ndarray The image of the CCD with the bais generated ''' log.info(hist()) bias_image = np.zeros_like(image) + value if realistic: shape = image.shape number_of_columns = np.random.randint(1, 6) columns = np.random.randint(0, shape[1], size=number_of_columns) col_pattern = np.random.randint(0, int( 0.1 * value), size=shape[0]) #add a little pseudo-random noise for c in columns: bias_image[:, c] = value + col_pattern return bias_image
def make_one_donut(center, diameter=10, amplitude=0.25): ''' This fuction is pretty eavy, it uses 3 mathematical fuction creates the image of a single donut to by added to the flat image Parameters ---------- center : numpy array center[0] : the position along the x axis of the center of the donut center[1] : the position along the y axis of the center of the donut diameter : int, float Can be interpreted as the diameter in pixel of the donut. In reality it is 2sigma of the Gaussian and RickerWavelet (ex MexicaHat) functions used to simulate the donut amplitude : float, optional The peak intensity of the aforementioned functions Output ------ Const2D(amplitude=1) + (mh - gauss) : numpy array It is a image of the donut created ''' log.info(hist()) sigma = diameter / 2 mh = RickerWavelet2D(amplitude=amplitude, x_0=center[0], y_0=center[1], sigma=sigma) gauss = Gaussian2D(amplitude=amplitude, x_mean=center[0], y_mean=center[1], x_stddev=sigma, y_stddev=sigma) return Const2D(amplitude=1) + (mh - gauss)
def telescope(focal_lenght, aperture, obstruction, trellis=True): ''' Creates the figures of the telescope Parameters ---------- focal_lenght : int The focal length of the telescope in mm aperture : int The aperture of the telescope in mm obstruction : int The central obstraction of the telescope in mm trellis : bool, optional If trellis == True the structure that hold on the obstruction is drawn, else the structure is not drawn Output ------ pupil : numpy ndarray The image of the aperture r : numpy ndarray The radial variable on the pupil ''' log.info(hist()) x,y = np.mgrid[-aperture/2:aperture/2, -aperture/2:aperture/2] # creates the 2D grid for the 2D function *4 r = np.sqrt(x**2+y**2) pupil = np.piecewise(r, [r < aperture/2, r > aperture/2, r < obstruction/2], [1, 0, 0]) #creates the aperture if trellis: structure_x = np.piecewise(x, [x, x>1, x<0], [0,1,1]) #creates the structure that keep the obstruction structure_y = np.piecewise(y, [y, y>1, y<0], [0,1,1]) pupil = pupil*(structure_x*structure_y) return pupil, r
def synthetic_ccd(shape): ''' Given a shape this function will creates an empty image with that shape Parameters ---------- shape: tuple shape[0] : int, the shape of the CCD along the x axis in number of pixel shape[1] : int, the shape of the CCD along the y axis in number of pixel Output ------ ccd_image : numpy ndarray The empy image with the choosen shape ''' log.info(hist()) ccd_image = np.zeros(shape) return ccd_image
def main(): ''' This is the main function of the system, it coordinates the Telescope_mod, the ccd_mod and the Query_mod. First it calls load_measure to obtain some data for Telescope_mod and Query_mod, the uses Telescope_mod with some additional parameters in input to create the PSF with the right measure for the telescope and the CCD. At this point the function calls again the Telescope_mod to creats an empty image of the CCD, and the Query_mod to obtain information about the position and the flux of the stars in a specific reagion of the sky, the center of that is an imput from the operator. The function uses the information of Query_mod to "light_up" some pixel on the CCD's image in position where shoud be the stars and then convolve this updated CCD with the calculated PSF from Telescope_mod obtainig the CCD with the stars drawn. If the value of realistic is True, the main calls the ccd_mod to creates the bias frame, the dark frame, the read out noise, the backround noise and the flat frame and then combines all the frames to obtain a more realistic image on the CCD. ''' log.info(hist()) binning = 2 #photo_filters = ['U', 'B', 'V', 'R', 'I'] photo_filters = ['V'] default_coordinates = "07 59 08.445 +15 24 42.00" default_defocus = 0.0 #mm default_exptime = 60 default_seeing = 3 default_method = 3 # gaussian approx default_catalog = 1 coordinates = input(f'Coordinates. Default: {default_coordinates}. ') or default_coordinates defocus_distance = float(input(f'Defocus distance [mm]. Default: {default_defocus}. ') or default_defocus) exposure_time = float(input(f'Exptime [s]. Default: {default_exptime}. ') or default_exptime) seeing = float(input(f'Seeing [arcsec]. Default: {default_seeing}. ') or default_seeing) choose = int(input(f'Method. Kolmogorov (1), spekles (2), gaussian approximation(3). Default: {default_method}. ') or default_method) catalogue = default_catalog # int(input(f'Catalog. Gaia(1) or Simbad(2). Default Catalog: {default_catalog}') or default_catalog ) if catalogue == 1: catalogue = 'Gaia' log.warning('This catalog uses its own passband, simulated results can be different from the real ones') else: catalogue = 'Simbad' telescope_structure, ccd_structure, ccd_data = load_measure() #Standard data for the noise formation realistic = True dark = 1.04 bias_level = 0 #760 # 2000 gain = ccd_data[0] read_noise_electrons = ccd_data[1] ccd_structure[2] = ccd_structure[2] * binning ccd_structure.append(Tm.plate_scale(ccd_structure[2], telescope_structure[0])) #calculate the resolution of the CCD arcsec/pixel if choose == 1: CCD_sample = Tm.telescope_on_CCD(ccd_structure[3], binning, telescope_structure, defocus_distance, True, True) else: CCD_sample = Tm.telescope_on_CCD(ccd_structure[3], binning, telescope_structure, defocus_distance, True, False) #generates the sample of a star with the right measure CCD = Tm.physical_CCD(ccd_structure) #generate the CCD size = CCD.shape if choose == 2: CCD_seeing = seeing/(ccd_structure[3]*2) number_of_spekle = 100 * exposure_time seeing_image = Tm.main_spekle(number_of_spekle, CCD_seeing) seeing_image = seeing_image/number_of_spekle elif choose == 3: CCD_seeing = seeing/(ccd_structure[3]*2) seeing_image = Tm.seeing(CCD_seeing) sky, center = Qm.query(coordinates, photo_filters, ccd_structure, exposure_time, catalogue) #call a function that gives back positions, fluxs of the stars and a data for the header sky_counts = Qm.sky_brightness(ccd_structure[3], size[0], size[1], photo_filters, exposure_time, 3) photons_collection_area = (np.pi/400)*(telescope_structure[1]**2-telescope_structure[2]**2) if choose == 1: multiplier = 2 * gain * photons_collection_area *0.05 else: multiplier = 1 * gain * photons_collection_area *0.05 #photons_collection_area * gain * binning**2 #* 200 #I don't know where 200 cames from I'm investigating #the flux is in ph cm^-2 so it has to be multiplied for the effective area of the aperure in cm^2 for i in range(len(sky[0])): if sky[0][i] >=0 and sky[0][i] <=size[0]: if sky[1][i] >=0 and sky[1][i] <=size[1]: CCD[int(sky[0][i])][int(sky[1][i])] = sky[2][i] * multiplier CCD = signal.fftconvolve(CCD, CCD_sample, mode='same') #convolve the position with the sample if choose == 2 or choose == 3: CCD = signal.fftconvolve(CCD, seeing_image, mode='same') lines = False dust = False gaussian_vignetting = False if realistic: flat = ccd_mod.sensitivity_variations(CCD, gaussian_vignetting, dust) if bias_level == 0: bias_only = 0 else: bias_only = ccd_mod.bias(CCD, bias_level, lines) noise_only = ccd_mod.read_out_noise(CCD, read_noise_electrons, gain) dark_only = ccd_mod.dark_current(CCD, dark, exposure_time, gain) sky_only = ccd_mod.sky_background(CCD, sky_counts, gain) #cosmic_rays = ccd_mod.make_cosmic_rays(CCD, np.random.randint(10,30)) CCD = bias_only + noise_only + dark_only + flat * (sky_only + CCD) CCD = ccd_mod.saturation_controll(CCD) save_fits(CCD, defocus_distance, center, choose) #save the fits