Source code for furst.detectors._detectors
import dataclasses
import astropy.units as u
import named_arrays as na
import optika
import furst
__all__ = [
"Detector",
]
[docs]
@dataclasses.dataclass(eq=False, repr=False)
class Detector(
optika.mixins.Rollable,
optika.mixins.Yawable,
optika.mixins.Pitchable,
optika.mixins.Translatable,
furst.abc.AbstractRowlandComponent,
):
"""
A model of FURST's imaging sensor and camera.
These are manufactured by Marshall Space Flight Center
for use with FURST.
Examples
--------
Plot the surface of the detector and the Rowland circle.
.. jupyter-execute::
import numpy as np
import matplotlib.pyplot as plt
import astropy.units as u
import astropy.visualization
import named_arrays as na
import optika
import furst
# Define the Rowland circle
rowland_radius = 1000 * u.mm
a = na.linspace(0, 360, axis="angle", num=1001) * u.deg
rowland_circle = rowland_radius * na.Cartesian3dVectorArray(
x=np.sin(a),
z=np.cos(a),
)
# Define the grating model
detector = furst.detectors.Detector(
width_pixel=15 * u.um,
axis_pixel=na.Cartesian2dVectorArray(
x="detector_x",
y="detector_y",
),
num_pixel=4096,
material=optika.materials.Mirror(),
rowland_radius=rowland_radius,
rowland_azimuth=10 * u.deg,
)
# Plot the grating surface and the Rowland circle
with astropy.visualization.quantity_support():
fig, ax = plt.subplots()
na.plt.plot(
rowland_circle,
ax=ax,
components=("z", "x"),
color="black",
linestyle="dashed",
zorder=-10,
)
detector.surface.plot(
ax=ax,
components=("z", "x"),
color="tab:orange",
)
ax.set_aspect("equal")
ax.set_xlim(960 * u.mm, 1020 * u.mm)
ax.set_ylim(120 * u.mm, 220 * u.mm)
"""
name: str = "detector"
"""
The human-readable name of this detector.
"""
manufacturer: str = ""
"""
The company that manufactured this detector.
"""
model_number: str = ""
"""
The model number of this detector.
"""
serial_number: str = ""
"""
The unique serial number associated with this detector.
"""
width_pixel: u.Quantity | na.AbstractCartesian2dVectorArray = 0 * u.mm
"""
The physical width of a pixel for this detector.
"""
axis_pixel: None | na.Cartesian2dVectorArray[str, str] = None
"""
The name of each axis of the pixel array.
"""
num_pixel: int | na.Cartesian2dVectorArray[int, int] = 0
"""
The number of pixels along each axis of the pixel array.
"""
num_pixel_overscan: int = 0
"""
The number of overscan columns for each tap.
"""
num_pixel_blank: int = 0
"""
The number of blank columns for each tap.
"""
material: None | optika.sensors.materials.AbstractSensorMaterial = None
"""
A model of the light-sensitive material of this detector.
"""
rowland_radius: u.Quantity | na.AbstractScalar = 0 * u.mm
"""
The distance from the center of the Rowland circle to
the center of the detector.
"""
rowland_azimuth: u.Quantity | na.AbstractScalar = 0 * u.deg
"""
The azimuth of the center of the detector
on the Rowland circle, relative to the optic axis
of the instrument.
"""
translation: u.Quantity | na.AbstractCartesian3dVectorArray = 0 * u.mm
"""
Physical offset from the optic's nominal position on the
Rowland circle.
"""
pitch: u.Quantity | na.AbstractScalar = 0 * u.deg
"""
The angle of rotation about the long axis of the detector.
"""
yaw: u.Quantity | na.AbstractScalar = 0 * u.deg
"""
The angle of rotation about the short axis of the detector.
"""
roll: u.Quantity | na.AbstractScalar = 0 * u.deg
"""
The angle of rotation about the vector normal to
the surface of the detector.
"""
temperature: u.Quantity | na.ScalarArray = 0 * u.K
"""
The operating temperature of this detector.
"""
gain: u.Quantity | na.ScalarArray = 0 * u.electron / u.DN
"""
The ratio between the number of electrons measured by the sensor
and the data number reported by the ADC
"""
readout_noise: u.Quantity | na.ScalarArray = 0 * u.DN
"""
The standard deviation of the noise introduced by the readout
electronics.
"""
dark_current: u.Quantity | na.ScalarArray = 0 * u.electron / u.s
"""
The amount of dark signal measured by the detector at the current
temperature.
"""
charge_diffusion: u.Quantity | na.AbstractScalar = 0 * u.um
"""
The standard deviation of the charge diffusion kernel.
"""
timedelta_transfer: u.Quantity | na.AbstractScalar = 0 * u.s
"""
The time required to transfer an image from the light-sensitive
area of the detector to the masked area.
"""
timedelta_readout: u.Quantity | na.AbstractScalar = 0 * u.s
"""
The time required to digitize an image collected by the sensor.
"""
timedelta_exposure: u.Quantity | na.AbstractScalar = 0 * u.s
"""
The current exposure time of this detector.
"""
timedelta_exposure_min: u.Quantity | na.AbstractScalar = 0 * u.s
"""
The minimum exposure time allowed by this detector.
"""
timedelta_exposure_max: u.Quantity | na.AbstractScalar = 0 * u.s
"""
The maximum exposure time allowed by this detector.
"""
bits_adc: int = 0
"""
The number of bits collected by the analog-to-digital converter
on this detector.
"""
@property
def surface(self) -> optika.sensors.ImagingSensor:
return optika.sensors.ImagingSensor(
name=self.name,
width_pixel=self.width_pixel,
axis_pixel=self.axis_pixel,
num_pixel=self.num_pixel,
timedelta_exposure=self.timedelta_exposure,
material=self.material,
transformation=self.transformation,
)