From 270390aceab54ed3b02a7510e1aef096c46e33dd Mon Sep 17 00:00:00 2001 From: rfetick <r.fetick@gmail.com> Date: Tue, 28 May 2019 15:16:51 +0200 Subject: [PATCH] Simplify Instrument class --- paompy/config.py | 3 - paompy/instrument.py | 137 +++++++++++++++++++------------------------ 2 files changed, 59 insertions(+), 81 deletions(-) diff --git a/paompy/config.py b/paompy/config.py index f35f7a0..862bccf 100644 --- a/paompy/config.py +++ b/paompy/config.py @@ -12,8 +12,5 @@ import sys # Value to compute finite differences _EPSILON = np.sqrt(sys.float_info.epsilon) -# Default pixel resolution when creating objects -_DEFAULT_RES = 30.*1e-6 # [m/pix] - # Codes for colored console print _PRINT_COLOR = {"yellow":"\x1b[6;33;1m", "default":"\x1b[0m"} diff --git a/paompy/instrument.py b/paompy/instrument.py index d5f2807..0cefcae 100644 --- a/paompy/instrument.py +++ b/paompy/instrument.py @@ -7,55 +7,6 @@ Created on Mon May 27 17:30:51 2019 """ from paompy.utils import circarr, airy, RAD2ARCSEC -from paompy.config import _DEFAULT_RES - -#%% DETECTOR CLASS AND ITS SUBCLASSES - -class Detector(object): - """Represents a detector - - Attributes - ---------- - resolution : float - Pixel scale [meter] - Npix : tuple, list, numpy.ndarray - Number of pixels on X and Y axis - gainADU : float - Detector gain [electron/ADU] - RON : float - Read-Out-Noise [electron] - - """ - - def __init__(self,Npix,resolution=_DEFAULT_RES,gainADU=1.,RON=0.): - self.resolution_pixel = resolution - self.Npix = Npix - self.gainADU = gainADU - self.RON = RON - self.binning = 1 - - def __repr__(self): - s = "PAOMPY Detector\n" - s += "---------------\n" - s += "Pixels : (%u,%u)\n" % (self.Npix[0],self.Npix[1]) - s += "Resolution: %u um\n" % round(self.resolution*1e6) - s += "Binning : %u\n" % self.binning - s += "Gain ADU : %.2f e-/ADU\n" % self.gainADU - s += "RON : %.2f e-" % self.RON - return s - - @property - def resolution(self): - return self.resolution_pixel * self.binning - - -ZIMPOL_DETECTOR = Detector((1024,1024),30*1e-6,gainADU=10.5,RON=20.) - -# Equivalent detector resolution after image reduction pipeline -MUSE_DETECTOR = Detector((200,200),237.14745*1e-6,gainADU=5.,RON=15.) - - - #%% INSTRUMENT CLASS AND ITS SUBCLASSES @@ -65,38 +16,66 @@ class Instrument(object): Attributes ---------- D : float - Entrance pupil diameter [meter] - detector : Detector - Camera at the focal plane + Entrance aperture diameter [meter] + occ : float + Aperture occultation ratio + resolution_rad : float + Resolution [rad] filters : dict Dictionary of available filters as tuples (central wvl, width) [meter] - AO_Nact : int - Linear number of actuators + Nact : int + Linear number of AO actuators + gainADU : float + Detector gain [e-/ADU] + RON : float + Detector read out noise [e-] + binning : int + Pixel binning factor (default=1) """ - def __init__(self,D,detector=None,occ=0.): + def __init__(self,D=None,occ=0.,res=None,Nact=0,gain=1.,ron=0.): + + if D is None: + raise ValueError("Please enter keyword `D` to set Instrument's aperture diameter") + if res is None: + raise ValueError("Please enter keyword `res` to set instrument resolution in rad") + + if D <= 0: + raise ValueError("Keyword `D` must be strictly positive") + if res <= 0: + raise ValueError("Keyword `res` must be strictly positive") + self.D = D self.occ = occ # occultation ratio - self.detector = detector self.filters = {} - self.AO_Nact = 0 - self.focal_length = None - self.name = "" + self.Nact = Nact + + self._resolution_rad = res + self.gain = gain + self.ron = ron + self.binning = 1 def __repr__(self): - s = self.name+" OpticalSystem\n" - s += "-------------------------\n" - s += "Diameter: %.2g m (occ=%u%%)\n" % (self.D,self.occ*100) - s += "AO_Nact : %u\n" % self.AO_Nact - s += "Focal : %s m\n" % str(self.focal_length) - s += "Filters : %u" % len(self.filters) + s = "PAOMPY Instrument\n" + s += "----------------------------\n" + s += "Diameter : %.2f m (occ=%u%%)\n" % (self.D,self.occ*100) + s += "Resolution : %.2f mas (binning=%u)\n" % (self.resolution_mas,self.binning) + s += "Nact : %u\n" % self.Nact + K = tuple(self.filters.keys()) + s += "Filters : " # %u" % len(self.filters) + for k in K: + s += "%s " % k + s += "\n" + s += "Detector : (gain=%.1f e-/ADU) (RON=%.1f e-)"%(self.gain,self.ron) return s + @property + def resolution_rad(self): + return self._resolution_rad * self.binning + @property def resolution_mas(self): - if self.focal_length is None: - raise ValueError("Cannot compute `resolution_mas` if `focal_length` is not set") - return self.detector.resolution/self.focal_length * RAD2ARCSEC * 1e3 + return self.resolution_rad * RAD2ARCSEC * 1e3 def pupil(self,Npix,wvl=None,samp=None): """Returns the 2D array of the pupil transmission function""" @@ -106,19 +85,11 @@ class Instrument(object): def samp(self,wvl): """Returns sampling value for the given wavelength""" - if self.detector is None: - raise ValueError("Cannot compute sampling if `detector` is not defined") - if self.focal_length is None: - raise ValueError("Cannot compute sampling if `focal_length` is not defined") - return wvl*self.focal_length/(self.detector.resolution*self.D) + return wvl/(self.resolution_rad*self.D) def wvl(self,samp): """Returns wavelength for the given sampling""" - if self.detector is None: - raise ValueError("Cannot compute wavelength if `detector` is not defined") - if self.focal_length is None: - raise ValueError("Cannot compute wavelength if `focal_length` is not defined") - return samp*(self.detector.resolution*self.D)/self.focal_length + return samp*(self.resolution_rad*self.D) def PSFdl(self,Npix,wvl): @@ -133,3 +104,13 @@ class Instrument(object): """ return airy(Npix,self.samp(wvl),self.occ) + + + + +ZIMPOL = Instrument(D=8.,occ=0.14,res=30*1e-6/1768.,gain=10.5,ron=20.,Nact=40) +ZIMPOL.filters["V"] = (554*1e-9, 80.6*1e-9) +ZIMPOL.filters["N_R"] = (645.9*1e-9, 56.7*1e-9) + + +MUSE = Instrument(D=8.,occ=0.14,res=237.15*1e-6/1980.,gain=5.,ron=15.,Nact=39) -- GitLab