Source code for gunagala.camera
"""
Cameras (stricly the image sensor subsystem, not including optics, optical filters, etc)
"""
import os
from astropy import units as u
from astropy.table import Table
from astropy.utils.data import get_pkg_data_filename
from gunagala.utils import ensure_unit
data_dir = 'data/performance_data'
[docs]class Camera:
"""
Class representing a camera.
Here 'camera' refers to the image sensor, associated electronics,
shutter, etc., but does not include any of the optical components
of the system.
Parameters
----------
bit_depth : int
Bits per pixel used by the camera analogue to digital
converters.
full_well : astropy.units.Quantity
Number of photo-electrons each pixel can receive before
saturating.
gain : astropy.units.Quantity
Number of photo-electrons corresponding to one ADU in the
digital data.
bias : astropy.units.Quantity
Bias level of image sensor, in ADU / pixel units. Used when
determining saturation level.
readout_time : astropy.units.Quantity
Time required to read the data from the image sensor.
pixel_size : astropy.units.Quantity
Pixel pitch. Square pixels are assumed.
Resolution : astropy.units.Quantity
Two element Quantity containing the number of pixels across
the image sensor in both horizontal & vertical directions.
read_noise astropy.units.Quantity
Intrinsic noise of image sensor and readout electronics, in
electrons/pixel units.
dark_current : astropy.units.Quantity
Rate of accumlation of dark signal, in electrons/second/pixel
units.
QE_filename : str
Name of a file containing quantum efficieny as a function of
wavelength data. Must be in a format readable by
`astropy.table.Table.read()` and use column names `Wavelength`
and `QE`. If the data file does not provide units nm and
dimensionless unscaled will be assumed.
minimum_exposure : astropy.units.Quantity
Length of the shortest exposure that the camera is able to
take.
Attributes
----------
bit_depth : int
Same as parameters
full_well : astropy.units.Quantity
Same as parameters
gain : astropy.units.Quantity
Same as parameters
bias : astropy.units.Quantity
Same as parameters
readout_time : astropy.units.Quantity
Same as parameters
pixel_size : astropy.units.Quantity
Same as parameters
resolution : astropy.units.Quantity
Same as parameters
read_noise : astropy.units.Quantity
Same as parameters
dark_current : astropy.units.Quantity
Same as parameters
minimum_exposure : astropy.units.Quantity
Same as parameters
saturation_level : astropy.units.Quantity
Lowest of `full_well` and `2**bit_depth - 1 - bias`
max_noise : astropy.units.Quantity
Poisson + readout noise corresponding to `saturation_level`
wavelenghts : astropy.units.Quantity
Sequence of wavelengths from the QE data
QE : astropy.units.Quantity
Sequence of quantum efficiency values from the QE data.
"""
def __init__(self, bit_depth, full_well, gain, bias, readout_time, pixel_size, resolution, read_noise,
dark_current, QE_filename, minimum_exposure):
self.bit_depth = int(bit_depth)
self.full_well = ensure_unit(full_well, u.electron / u.pixel)
self.gain = ensure_unit(gain, u.electron / u.adu)
self.bias = ensure_unit(bias, u.adu / u.pixel)
self.readout_time = ensure_unit(readout_time, u.second)
self.pixel_size = ensure_unit(pixel_size, u.micron / u.pixel)
self.resolution = ensure_unit(resolution, u.pixel)
self.read_noise = ensure_unit(read_noise, u.electron / u.pixel)
self.dark_current = ensure_unit(dark_current, u.electron / (u.second * u.pixel))
self.minimum_exposure = ensure_unit(minimum_exposure, u.second)
# Calculate a saturation level corresponding to the lower of the 'analogue' (full well) and 'digital'
# (ADC) limit, in electrons.
self.saturation_level = min(self.full_well, ((2**self.bit_depth - 1) * u.adu / u.pixel - self.bias) * self.gain)
# Calculate the noise at the saturation level
self.max_noise = (self.saturation_level * u.electron / u.pixel + self.read_noise**2)**0.5
QE_data = Table.read(get_pkg_data_filename(os.path.join(data_dir, QE_filename)))
if not QE_data['Wavelength'].unit:
QE_data['Wavelength'].unit = u.nm
self.wavelengths = QE_data['Wavelength'].quantity.to(u.nm)
if not QE_data['QE'].unit:
QE_data['QE'].unit = u.electron / u.photon
self.QE = QE_data['QE'].quantity.to(u.electron / u.photon)