Bugfix: calculate extended target signal using spectral radiance values
This commit is contained in:
parent
1043a559b8
commit
e5b6e3b700
@ -9,7 +9,7 @@ class IRadiant(ABC):
|
||||
in the beam.
|
||||
"""
|
||||
@abstractmethod
|
||||
def calcSignal(self) -> Tuple[SpectralQty, str, float]:
|
||||
def calcSignal(self) -> Tuple[SpectralQty, float]:
|
||||
"""
|
||||
Calculate the signal coming from the component
|
||||
|
||||
@ -17,8 +17,6 @@ class IRadiant(ABC):
|
||||
-------
|
||||
signal : SpectralQty
|
||||
The emitted, reflected or transmitted signal
|
||||
size : str
|
||||
The size of the target.
|
||||
obstruction : float
|
||||
The obstruction factor as A_ob / A_ap.
|
||||
"""
|
||||
|
@ -51,7 +51,7 @@ class AOpticalComponent(IRadiant):
|
||||
self.__obstructor_temp = obstructor_temp
|
||||
self.__obstructor_emissivity = obstructor_emissivity
|
||||
|
||||
def calcSignal(self) -> Tuple[SpectralQty, str, float]:
|
||||
def calcSignal(self) -> Tuple[SpectralQty, float]:
|
||||
"""
|
||||
Calculate the spectral flux density of the target's signal
|
||||
|
||||
@ -59,17 +59,15 @@ class AOpticalComponent(IRadiant):
|
||||
-------
|
||||
signal : SpectralQty
|
||||
The spectral flux density of the target's signal
|
||||
size : str
|
||||
The size of the target.
|
||||
obstruction : float
|
||||
The obstruction factor as A_ob / A_ap.
|
||||
"""
|
||||
signal, size, obstruction = self.__parent.calcSignal()
|
||||
signal, obstruction = self.__parent.calcSignal()
|
||||
logger.info("Calculating signal for class '" + self.__class__.__name__ + "'.")
|
||||
signal = self._propagate(signal) * (1 - self.__obstruction)
|
||||
obstruction = obstruction + self.__obstruction
|
||||
logger.debug(signal)
|
||||
return signal, size, obstruction
|
||||
return signal, obstruction
|
||||
|
||||
def calcBackground(self) -> SpectralQty:
|
||||
"""
|
||||
|
@ -420,8 +420,14 @@ class Imager(ASensor):
|
||||
self.__pixel_size.to(u.m) ** 2 / u.pix) / (4 * self.__f_number ** 2 + 1) * (1 * u.sr)
|
||||
# Calculate the incoming photon current of the target
|
||||
logger.info("Calculating the signal photon current.")
|
||||
signal, size, obstruction = self._parent.calcSignal()
|
||||
signal_photon_current = signal * np.pi * (self.__common_conf.d_aperture() / 2) ** 2
|
||||
signal, obstruction = self._parent.calcSignal()
|
||||
size = "extended" if signal.qty.unit.is_equivalent(u.W / (u.m ** 2 * u.nm * u.sr)) else "point"
|
||||
if size == "point":
|
||||
signal_photon_current = signal * np.pi * (self.__common_conf.d_aperture() / 2) ** 2
|
||||
else:
|
||||
signal_photon_current = signal * np.pi * self.__pixel_size.to(u.m) ** 2 / (
|
||||
4 * self.__f_number ** 2 + 1) * (1 * u.sr)
|
||||
print(signal_photon_current)
|
||||
# Calculate the electron current of the background and thereby handling the photon energy as lambda-function
|
||||
background_current = (
|
||||
background_photon_current / (lambda wl: (const.h * const.c / wl).to(u.W * u.s) / u.photon) *
|
||||
@ -499,31 +505,30 @@ class Imager(ASensor):
|
||||
return "pixel -> well_capacity: " + mes
|
||||
|
||||
# Check photometric aperture
|
||||
if conf.astroscene.target.size == "point":
|
||||
if not hasattr(sensor, "photometric_aperture"):
|
||||
setattr(sensor, "photometric_aperture", Entry(shape=Entry(val="circle"),
|
||||
contained_energy=Entry(val="FWHM")))
|
||||
if hasattr(sensor.photometric_aperture, "contained_pixels"):
|
||||
mes = sensor.photometric_aperture.contained_pixels.check_quantity("val", u.pix)
|
||||
if mes is not None:
|
||||
return "photometric_aperture -> contained_pixels: " + mes
|
||||
else:
|
||||
if not hasattr(sensor.photometric_aperture, "shape"):
|
||||
return "Missing container 'shape'."
|
||||
mes = sensor.photometric_aperture.shape.check_selection("val", ["square", "circle"])
|
||||
if mes is not None:
|
||||
return "photometric_aperture -> shape: " + mes
|
||||
if not hasattr(sensor.photometric_aperture, "contained_energy"):
|
||||
return "Missing container 'contained_energy'."
|
||||
mes = sensor.photometric_aperture.contained_energy.check_float("val")
|
||||
if mes is not None:
|
||||
if conf.common.psf().lower() == "airy":
|
||||
mes = sensor.photometric_aperture.contained_energy.check_selection("val",
|
||||
["peak", "FWHM", "fwhm",
|
||||
"min"])
|
||||
if mes is not None:
|
||||
return "photometric_aperture -> contained_energy: " + mes
|
||||
else:
|
||||
mes = sensor.photometric_aperture.contained_energy.check_selection("val", ["FWHM", "fwhm"])
|
||||
if mes is not None:
|
||||
return "photometric_aperture -> contained_energy: " + mes
|
||||
if not hasattr(sensor, "photometric_aperture"):
|
||||
setattr(sensor, "photometric_aperture", Entry(shape=Entry(val="circle"),
|
||||
contained_energy=Entry(val="FWHM")))
|
||||
if hasattr(sensor.photometric_aperture, "contained_pixels"):
|
||||
mes = sensor.photometric_aperture.contained_pixels.check_quantity("val", u.pix)
|
||||
if mes is not None:
|
||||
return "photometric_aperture -> contained_pixels: " + mes
|
||||
else:
|
||||
if not hasattr(sensor.photometric_aperture, "shape"):
|
||||
return "Missing container 'shape'."
|
||||
mes = sensor.photometric_aperture.shape.check_selection("val", ["square", "circle"])
|
||||
if mes is not None:
|
||||
return "photometric_aperture -> shape: " + mes
|
||||
if not hasattr(sensor.photometric_aperture, "contained_energy"):
|
||||
return "Missing container 'contained_energy'."
|
||||
mes = sensor.photometric_aperture.contained_energy.check_float("val")
|
||||
if mes is not None:
|
||||
if conf.common.psf().lower() == "airy":
|
||||
mes = sensor.photometric_aperture.contained_energy.check_selection("val",
|
||||
["peak", "FWHM", "fwhm",
|
||||
"min"])
|
||||
if mes is not None:
|
||||
return "photometric_aperture -> contained_energy: " + mes
|
||||
else:
|
||||
mes = sensor.photometric_aperture.contained_energy.check_selection("val", ["FWHM", "fwhm"])
|
||||
if mes is not None:
|
||||
return "photometric_aperture -> contained_energy: " + mes
|
||||
|
@ -15,7 +15,7 @@ class ATarget(IRadiant):
|
||||
|
||||
@abstractmethod
|
||||
@u.quantity_input(wl_bins="length")
|
||||
def __init__(self, sfd: SpectralQty, wl_bins: u.Quantity, size: str = "Point"):
|
||||
def __init__(self, sfd: SpectralQty, wl_bins: u.Quantity):
|
||||
"""
|
||||
Initialize a new target
|
||||
|
||||
@ -25,12 +25,9 @@ class ATarget(IRadiant):
|
||||
The spectral flux density of the target
|
||||
wl_bins : length-Quantity
|
||||
The bins to be used for evaluating spectral quantities.
|
||||
size : str
|
||||
The size of the target. Can be either point or extended.
|
||||
"""
|
||||
self.__sfd = sfd
|
||||
self.__wl_bins = wl_bins
|
||||
self.__size = size
|
||||
|
||||
def calcBackground(self) -> SpectralQty:
|
||||
"""
|
||||
@ -46,7 +43,7 @@ class ATarget(IRadiant):
|
||||
logger.debug(background)
|
||||
return background
|
||||
|
||||
def calcSignal(self) -> Tuple[SpectralQty, str, float]:
|
||||
def calcSignal(self) -> Tuple[SpectralQty, float]:
|
||||
"""
|
||||
Calculate the spectral flux density of the target's signal
|
||||
|
||||
@ -54,14 +51,12 @@ class ATarget(IRadiant):
|
||||
-------
|
||||
signal : SpectralQty
|
||||
The spectral flux density of the target's signal
|
||||
size : str
|
||||
The size of the target.
|
||||
obstruction : float
|
||||
The obstruction factor as A_ob / A_ap.
|
||||
"""
|
||||
logger.info("Calculating signal for class '" + self.__class__.__name__ + "'.")
|
||||
logger.debug(self.__sfd)
|
||||
return self.__sfd, self.__size, 0.0
|
||||
return self.__sfd, 0.0
|
||||
|
||||
@staticmethod
|
||||
@abstractmethod
|
||||
|
@ -24,9 +24,9 @@ class BlackBodyTarget(ATarget):
|
||||
M=dict(wl=4800 * u.nm, sfd=2.07e-14 * u.W / (u.m ** 2 * u.nm)),
|
||||
N=dict(wl=10200 * u.nm, sfd=1.23e-15 * u.W / (u.m ** 2 * u.nm)))
|
||||
|
||||
@u.quantity_input(wl_bins='length', temp=[u.Kelvin, u.Celsius], mag=u.mag)
|
||||
def __init__(self, wl_bins: u.Quantity, temp: u.Quantity = 5778 * u.K,
|
||||
mag: u.Quantity = 0 * u.mag, band: str = "V", size: str = "Point"):
|
||||
@u.quantity_input(wl_bins='length', temp=[u.Kelvin, u.Celsius], mag=[u.mag, u.mag / u.sr])
|
||||
def __init__(self, wl_bins: u.Quantity, temp: u.Quantity = 5778 * u.K, mag: u.Quantity = 0 * u.mag,
|
||||
band: str = "V"):
|
||||
"""
|
||||
Initialize a new black body point source
|
||||
|
||||
@ -36,12 +36,11 @@ class BlackBodyTarget(ATarget):
|
||||
Wavelengths used for binning
|
||||
temp : Quantity in Kelvin / Celsius
|
||||
Temperature of the black body
|
||||
mag : Quantity in mag
|
||||
Desired apparent magnitude of the point source
|
||||
mag : Quantity in mag or mag / sr
|
||||
Desired apparent magnitude of the black body source. If the magnitude is given in mag / sr or an equivalent
|
||||
unit, an extended source will be assumed.
|
||||
band : str
|
||||
Band used for fitting the planck curve to a star of 0th magnitude. Can be one of [U, B, V, R, I, J, H, K].
|
||||
size : str
|
||||
The size of the target. Can be either point or extended
|
||||
|
||||
Returns
|
||||
-------
|
||||
@ -53,12 +52,18 @@ class BlackBodyTarget(ATarget):
|
||||
|
||||
# Calculate the correction factor for a star of 0th magnitude using the spectral flux density
|
||||
# for the central wavelength of the given band
|
||||
factor = self._band[band.upper()]["sfd"] / (bb(self._band[band.upper()]["wl"]) * u.sr) * u.sr
|
||||
if mag.unit.is_equivalent(u.mag / u.sr):
|
||||
solid_angle_unit = (u.mag / mag.unit)
|
||||
mag = mag * solid_angle_unit
|
||||
factor = self._band[band.upper()]["sfd"] / (bb(self._band[band.upper()]["wl"]) * (
|
||||
solid_angle_unit.to(u.sr) * u.sr))
|
||||
else:
|
||||
factor = self._band[band.upper()]["sfd"] / (bb(self._band[band.upper()]["wl"]) * u.sr) * u.sr
|
||||
# Calculate spectral flux density for the given wavelengths and scale it for a star of the given magnitude
|
||||
sfd = bb(wl_bins) * factor * 10 ** (- 2 / 5 * mag / u.mag) # / 1.195 * 1.16 # scaling for AETC validation
|
||||
|
||||
# Initialize super class
|
||||
super().__init__(SpectralQty(wl_bins, sfd), wl_bins, size)
|
||||
super().__init__(SpectralQty(wl_bins, sfd), wl_bins)
|
||||
|
||||
@staticmethod
|
||||
def check_config(conf: Entry) -> Union[None, str]:
|
||||
@ -80,10 +85,9 @@ class BlackBodyTarget(ATarget):
|
||||
return mes
|
||||
mes = conf.check_quantity("mag", u.mag)
|
||||
if mes is not None:
|
||||
return mes
|
||||
mes = conf.check_quantity("mag", u.mag / u.sr)
|
||||
if mes is not None:
|
||||
return mes
|
||||
mes = conf.check_selection("band", ["U", "B", "V", "R", "I", "J", "H", "K", "L", "M", "N"])
|
||||
if mes is not None:
|
||||
return mes
|
||||
mes = conf.check_selection("size", ["point", "extended"])
|
||||
if mes is not None:
|
||||
return mes
|
||||
|
@ -11,7 +11,7 @@ class FileTarget(ATarget):
|
||||
"""
|
||||
|
||||
@u.quantity_input(wl_bins="length")
|
||||
def __init__(self, file: str, wl_bins: u.Quantity, size: str = "Point"):
|
||||
def __init__(self, file: str, wl_bins: u.Quantity):
|
||||
"""
|
||||
Initialize a new target from a file containing the spectral flux density values
|
||||
|
||||
@ -24,13 +24,11 @@ class FileTarget(ATarget):
|
||||
will be read from the column headers or otherwise assumed to be *nm* and *W / m^2 / nm*.
|
||||
wl_bins : length-Quantity
|
||||
Wavelengths used for binning
|
||||
size : str
|
||||
The size of the target. Can be either point or extended.
|
||||
"""
|
||||
# Create spectral quantity from file
|
||||
sfd = SpectralQty.fromFile(file, u.nm, u.W / (u.m ** 2 * u.nm))
|
||||
# Initialize the super class
|
||||
super().__init__(sfd, wl_bins, size)
|
||||
super().__init__(sfd, wl_bins)
|
||||
|
||||
@staticmethod
|
||||
def check_config(conf: Entry) -> Union[None, str]:
|
||||
@ -50,6 +48,3 @@ class FileTarget(ATarget):
|
||||
mes = conf.check_file("file")
|
||||
if mes is not None:
|
||||
return mes
|
||||
mes = conf.check_selection("size", ["point", "extended"])
|
||||
if mes is not None:
|
||||
return mes
|
||||
|
Loading…
Reference in New Issue
Block a user