import logging
from collections import OrderedDict
import numpy as np
from astropy import units as u
from exorad.models.signal import CustomSignal
from exorad.models.signal import Signal
logger = logging.getLogger("exorad.noise")
[docs]
def frame_time(target, channel, out):
"""
Given the channel and channel descriptions, populates the output table with saturation and frame times
Parameters
-----------
channel: dict
channel description
target: Target
Target to investigate
out: QTable
output table
Returns
--------
QTable
output table populated
"""
name = channel["value"]
max_signal_in_pix = target.table["MaxSignal_inPixel"][
target.table["chName"] == name
]
out["saturation_time"] = (
channel["detector"]["well_depth"]["value"] / max_signal_in_pix
)
logger.debug("saturation time : {}".format(out["saturation_time"]))
if "frame_time" in channel["detector"].keys():
out["frameTime"] = channel["detector"]["frame_time"][
"value"
] * np.ones(out["saturation_time"].size)
else:
out["frameTime"] = (
channel["detector"]["f_well_depth"]["value"]
* np.min(out["saturation_time"])
* np.ones(out["saturation_time"].size)
)
logger.debug("frame time : {}".format(out["frameTime"]))
return out
[docs]
def multiaccum(channel, t_frame):
"""
Given the channel and time frame, returns the multiaccum estimation for read and shot gain
Parameters
-----------
channel: dict
channel description
t_frame: float
frame time
Returns
--------
float
read noise gain
float
shot noise gain
"""
nRead = np.floor(t_frame * channel["detector"]["freqNDR"]["value"])
if "multiaccumM" in channel["detector"]:
m = channel["detector"]["multiaccumM"]["value"]
logger.debug("multiaccum activated: m = {}".format(m))
tf = 0.0 * u.s
else:
m = 1
tf = 0.0 * u.s
if nRead < 2:
nRead = 2.0 # Force to CDS in nRead < 2
read_gain = 12.0 * (nRead - 1.0) / (nRead**2 + nRead) / m
shot_gain = (
6.0
* (nRead**2 + 1.0)
/ (nRead**2 + nRead)
/ 5.0
* (
1
- 5.0
/ 3.0
* (m**2 - 1)
/ m
/ (nRead**2 + 1)
* tf
/ (nRead - 1)
/ t_frame
)
)
logger.debug("read noise gain: {}".format(read_gain))
logger.debug("shot noise gain: {}".format(shot_gain))
return read_gain, shot_gain
[docs]
def photon_noise(table, channel, shot_gain, out):
"""
Given the channel and channel descriptions, populates the output table with photon noises
Parameters
-----------
channel: dict
channel description
target: Table
Target table to investigate
shot_gain: float
multiaccum factor for photon noise
out: QTable
output table
Returns
--------
QTable
output table populated
"""
name = channel["value"]
signals = [key for key in table.keys() if "signal" in key]
for key in signals:
noise_key = "{}_noise".format(key)
out[noise_key] = (
np.sqrt(
shot_gain
* table[key][table["chName"] == name]
* u.count
/ u.hr
).to(u.count / u.s)
* u.hr**0.5
)
logger.debug("{} : {}".format(noise_key, out[noise_key]))
return out
[docs]
def photon_noise_variance(table, out):
'''
"""
Given the target table return the sum of photon variances
Parameters
-----------
target: Table
Target table to investigate
out: QTable
output table
Returns
--------
Array
sum of photon variances
"""
'''
signals = [key for key in table.keys() if "signal" in key]
_photon_noise_variance = (
np.zeros(out["frameTime"].size) * (u.count / u.s) ** 2 * u.hr
)
for key in signals:
noise_key = "{}_noise".format(key)
_photon_noise_variance += out[noise_key] * out[noise_key]
return _photon_noise_variance
[docs]
def add_custom_noise(custom, wl, out):
if "data" in custom:
col_name = [
col for col in custom["data"].keys() if "Wavelength" not in col
]
custom_noise = Signal(
custom["data"]["Wavelength"], custom["data"][col_name]
)
custom_noise.spectral_rebin(wl)
out["{}_noise".format(col_name)] = custom_noise.data
logger.debug(
"{} added as custom noise :{}".format(col_name, custom_noise.data)
)
out["total_noise"] = np.sqrt(
out["total_noise"] * out["total_noise"]
+ custom_noise.data * custom_noise.data
)
if isinstance(custom, OrderedDict):
for contrib in custom:
custom_noise = (
custom[contrib]["value"]
* 1e-6
* np.ones(wl.size)
* u.hr**0.5
)
out[
"{}_noise".format(custom[contrib]["name"]["value"])
] = custom_noise
logger.debug(
"{} added as custom noise :{}".format(
custom[contrib]["name"]["value"], custom_noise
)
)
out["total_noise"] = np.sqrt(
out["total_noise"] * out["total_noise"]
+ custom_noise * custom_noise
)
else:
custom_noise = custom["value"] * 1e-6 * np.ones(wl.size) * u.hr**0.5
out["{}_noise".format(custom["name"]["value"])] = custom_noise
logger.debug(
"{} added as custom noise :{}".format(
custom["name"]["value"], custom_noise
)
)
out["total_noise"] = np.sqrt(
out["total_noise"] * out["total_noise"]
+ custom_noise * custom_noise
)
return out
[docs]
class Noise(CustomSignal):
"""
It's a Signal class with data having units of [hr^1/2]
"""
def __init__(self, wl_grid, data, time_grid=[0] * u.hr):
super().__init__(wl_grid, data, u.hr**0.5, time_grid)