Dark Current#
Dark current is the charge that accumulates in a sensor with no light on it. This report compares the model of the dark current given by the datasheet of the CCD230-42 with the dark current of an ESIS camera measured by the MSFC sounding rocket team on 2017-07-12.
The datasheet model#
The datasheet gives the typical dark current at a temperature \(T\), in kelvin, as
where \(Q_d\) is the dark current and \(Q_{do}\) is the dark current at 293 K, along with a typical dark current of 0.2 electron / pixel / s at 248 K, which sets the scale. The datasheet only claims this model from 230 K to 300 K, and warns that below 230 K other sources of dark current, which depend less steeply on temperature, may dominate.
[1]:
import numpy as np
import matplotlib.pyplot as plt
import astropy.units as u
import astropy.visualization
import named_arrays as na
import msfc_ccd
msfc_ccd.TeledyneCCD230.dark_current() evaluates the model over a range
of temperatures.
[2]:
sensor = msfc_ccd.TeledyneCCD230()
temperature = na.linspace(200, 300, axis="temperature", num=101) * u.K
dark_current = sensor.dark_current(temperature)
Measuring the dark current#
A two-second dark accumulates far less dark current than the readout noise,
so the rate is the slope of the signal against the exposure time across darks
of different lengths, averaged over every active pixel of each tap outside
the masked rows.
msfc_ccd.dark.current() computes it.
The samples included with this package are a two-second and a twelve-second
dark from the ESIS channel 1 camera, gathered eight minutes apart.
[3]:
axis_time = "time"
darks = msfc_ccd.fits.open(
path=na.ScalarArray(
ndarray=np.array([
msfc_ccd.samples.path_dark_2s_esis1,
msfc_ccd.samples.path_dark_12s_esis1,
]),
axes=axis_time,
),
).taps
rate = msfc_ccd.dark.current(darks, axis_time).outputs
rate.to(u.DN / u.s).ndarray
[3]:
The model is in electrons, so the rate needs the gain of each tap.
The pair of flats included with this package comes from the same camera on
the same day, and msfc_ccd.gain.photon_transfer() measures the gain
from them.
These flats are bright enough that the gain reads 1 to 3 percent high,
which is small next to the uncertainty of a rate from two darks.
[4]:
flats = msfc_ccd.fits.open(
path=na.ScalarArray(
ndarray=np.array([
msfc_ccd.samples.path_led_esis1,
msfc_ccd.samples.path_led_esis1_next,
]),
axes=axis_time,
),
).taps
gain = msfc_ccd.gain.photon_transfer(flats, axis_time).outputs
rate_electrons = (rate * gain).to(u.electron / u.s)
rate_electrons.ndarray
[4]:
The FITS headers do not record the temperature of the CCD. The housekeeping log of the test recorded the holder of this camera’s CCD at \(-50.3^\circ\text{C}\), or 222.9 K, throughout the dark set, steady to within \(0.4^\circ\text{C}\).
[5]:
temperature_holder = (-50.3 * u.deg_C).to(u.K, equivalencies=u.temperature())
sensor.dark_current(temperature_holder)
[5]:
Plot the model against temperature, with the measured rate of each tap at the temperature of the holder.
[6]:
with astropy.visualization.quantity_support():
fig, ax = plt.subplots(constrained_layout=True)
na.plt.plot(
temperature,
dark_current,
ax=ax,
label="datasheet model",
)
ax.axvspan(
temperature.ndarray.min(),
230 * u.K,
color="gray",
alpha=0.2,
label="outside the range of the model",
)
ax.scatter(
np.full(rate_electrons.size, temperature_holder.value) * u.K,
rate_electrons.ndarray.ravel(),
color="tab:red",
zorder=3,
label="measured, each tap",
)
ax.set_yscale("log")
ax.set_xlabel(f"temperature ({ax.get_xlabel()})")
ax.set_ylabel(f"dark current ({ax.get_ylabel()})")
ax2 = ax.secondary_xaxis(
"top",
functions=(lambda t: t - 273.15, lambda t: t + 273.15),
)
ax2.set_xlabel("temperature (deg C)")
ax.legend();
Two images give only a rough rate, since the uncertainty in the bias of each image is about a fifth of the signal accumulated between them. Averaging many images at each of five exposure lengths from the same test gives 0.020 to 0.025 DN / s across the four taps, or 0.05 to 0.06 electron / pixel / s with the gain of this camera from the photon transfer curve at low signal, 2.45 to 2.49 electron / DN. That is 20 to 27 times the model at the temperature of the holder.
The holder is not the CCD itself, which may run a few kelvin warmer. But even at 230 K, the lower limit of the model and 7 K above the holder, the model gives 0.009 electron / pixel / s, still 5 to 7 times below the measurement.
So the datasheet model does not describe the ESIS cameras at the temperatures they run at, as the datasheet itself warns. Use the measured dark current instead.