From 14a7abca7453ef7731fbd86c78151f62e489db6e Mon Sep 17 00:00:00 2001 From: LukasK13 Date: Sat, 16 May 2020 15:52:27 +0200 Subject: [PATCH] Calculate sensitivity --- esbo_etc/classes/Config.py | 9 ++++++--- esbo_etc/classes/sensor/ASensor.py | 27 +++++++++++++++++++++++++-- esbo_etc/classes/sensor/Imager.py | 28 ++++++++++++++++++++++++++++ esbo_etc/esbo-etc.py | 9 ++++++--- 4 files changed, 65 insertions(+), 8 deletions(-) diff --git a/esbo_etc/classes/Config.py b/esbo_etc/classes/Config.py index adca288..5b4e4c2 100644 --- a/esbo_etc/classes/Config.py +++ b/esbo_etc/classes/Config.py @@ -142,10 +142,10 @@ class Configuration(object): if hasattr(self.conf.common, "exposure_time"): mes = self.conf.common.exposure_time.check_quantity("val", u.s) mes is not None and error("Configuration check: common -> exposure_time: " + mes) - elif hasattr(self.conf.common, "snr"): + if hasattr(self.conf.common, "snr"): mes = self.conf.common.snr.check_quantity("val", u.dimensionless_unscaled) mes is not None and error("Configuration check: common -> snr: " + mes) - else: + if not (hasattr(self.conf.common, "exposure_time") or hasattr(self.conf.common, "snr")): error("Configuration check: common: Expected one of the containers 'exposure_time' or 'snr' but got none.") # Check astroscene @@ -162,7 +162,10 @@ class Configuration(object): dir(tg), 1)[0] + "'?") mes = getattr(tg, self.conf.astroscene.target.type).check_config(self.conf.astroscene.target) mes is not None and error("Configuration check: astroscene -> target: " + mes) - + if hasattr(self.conf.common, "exposure_time") and hasattr(self.conf.common, "snr"): + if self.conf.astroscene.target.type.lower() != "blackbodytarget": + error("Configuration check: astroscene -> target: Sensitivity calculation only possible for " + + "a target of the type 'BlackBodyTarget'.") mes = self.__check_optical_components(self.conf.astroscene) mes is not None and error("Configuration check: astroscene -> " + mes) diff --git a/esbo_etc/classes/sensor/ASensor.py b/esbo_etc/classes/sensor/ASensor.py index 60f72e7..5654f8b 100644 --- a/esbo_etc/classes/sensor/ASensor.py +++ b/esbo_etc/classes/sensor/ASensor.py @@ -39,13 +39,14 @@ class ASensor: pass @abstractmethod - def getExpTime(self, snr: float): + @u.quantity_input(snr=u.dimensionless_unscaled) + def getExpTime(self, snr: u.Quantity): """ Calculate the necessary exposure time in order to achieve the given SNR. Parameters ---------- - snr : float + snr : Quantity The SNR for which the necessary exposure time shall be calculated. Returns @@ -55,6 +56,28 @@ class ASensor: """ pass + @abstractmethod + @u.quantity_input(exp_time="time", snr=u.dimensionless_unscaled, target_brightness=u.mag) + def getSensitivity(self, exp_time: u.Quantity, snr: u.Quantity, target_brightness: u.Quantity): + """ + Calculate the sensitivity of the telescope detector combination. + + Parameters + ---------- + exp_time : Quantity + The exposure time in seconds. + snr : Quantity + The SNR for which the sensitivity time shall be calculated. + target_brightness : Quantity + The target brightness in magnitudes. + + Returns + ------- + sensitivity: Quantity + The sensitivity as limiting apparent star magnitude in mag. + """ + pass + @staticmethod def check_config(sensor: Entry, conf: Entry) -> Union[None, str]: """ diff --git a/esbo_etc/classes/sensor/Imager.py b/esbo_etc/classes/sensor/Imager.py index 71dbf14..13728b0 100644 --- a/esbo_etc/classes/sensor/Imager.py +++ b/esbo_etc/classes/sensor/Imager.py @@ -149,6 +149,34 @@ class Imager(ASensor): self.__calcSNR(signal_current * exp_time, background_current * exp_time, read_noise, dark_current * exp_time) return exp_time + @u.quantity_input(exp_time="time", snr=u.dimensionless_unscaled, target_brightness=u.mag) + def getSensitivity(self, exp_time: u.Quantity, snr: u.Quantity, target_brightness: u.Quantity): + """ + Calculate the sensitivity of the telescope detector combination. + + Parameters + ---------- + exp_time : Quantity + The exposure time in seconds. + snr : Quantity + The SNR for which the sensitivity time shall be calculated. + target_brightness : Quantity + The target brightness in magnitudes. + + Returns + ------- + sensitivity: Quantity + The sensitivity as limiting apparent star magnitude in mag. + """ + # Calculate the electron currents + signal_current, background_current, read_noise, dark_current = self.__exposePixels() + # Fix the physical units of the SNR + snr = snr * u.electron ** 0.5 + signal_current_lim = snr * (snr + np.sqrt( + snr ** 2 + 4 * (exp_time * (background_current.sum() + dark_current.sum()) + (read_noise ** 2).sum()))) / ( + 2 * exp_time) + return target_brightness - 2.5 * np.log10(signal_current_lim / signal_current.sum()) * u.mag + @u.quantity_input(signal=u.electron, background=u.electron, read_noise=u.electron ** 0.5, dark=u.electron) def __calcSNR(self, signal: u.Quantity, background: u.Quantity, read_noise: u.Quantity, dark: u.Quantity) -> u.dimensionless_unscaled: diff --git a/esbo_etc/esbo-etc.py b/esbo_etc/esbo-etc.py index 2a58942..26eaa7a 100644 --- a/esbo_etc/esbo-etc.py +++ b/esbo_etc/esbo-etc.py @@ -23,9 +23,12 @@ if __name__ == "__main__": parent = oc_factory.fromConfigBatch(conf) sensor_factory = etc.SensorFactory(parent, conf.common) imager = sensor_factory.create(conf.instrument.sensor) - if hasattr(conf.common, "exposure_time"): + if hasattr(conf.common, "exposure_time") and hasattr(conf.common, "snr"): + sensitivity = imager.getSensitivity(conf.common.exposure_time(), conf.common.snr(), conf.astroscene.target.mag) + print("The limiting apparent magnitude is: %.2f mag." % sensitivity.value) + elif hasattr(conf.common, "exposure_time"): snr = imager.getSNR(conf.common.exposure_time()) - print("The SNR is: %.2f" % snr) + print("The SNR is: %.2f." % snr.value) elif hasattr(conf.common, "snr"): exp_time = imager.getExpTime(conf.common.snr()) - print("The necessary exposure time is: %.2f s" % exp_time.value) + print("The necessary exposure time is: %.2f s." % exp_time.value)