22Photodiode storage class.
25__all__ = [
"PhotodiodeCalib"]
28from astropy.table
import Table
29from astropy.stats
import sigma_clipped_stats
35 """Independent current measurements from photodiode for linearity
40 timeSamples : `list` or `numpy.ndarray`
41 List of samples the photodiode was measured at.
42 currentSamples : `list` or `numpy.ndarray`
43 List of current measurements at each time sample.
44 log : `logging.Logger`, optional
45 Log to write messages to. If `None` a default logger will be used.
47 Additional parameters. These will be passed to the parent
48 constructor with the exception of:
50 ``"integrationMethod"``
51 Name of the algorithm to use to integrate the current
52 samples. Allowed values are ``DIRECT_SUM``,
53 ``TRIMMED_SUM``, ``CHARGE_SUM``, ``MEAN`` (`str`).
55 Scale factor to apply to the current samples for the
56 ``CHARGE_SUM`` integration method. A typical value
57 would be `-1`, to flip the sign of the integrated charge.
60 _OBSTYPE =
'PHOTODIODE'
61 _SCHEMA =
'Photodiode'
64 def __init__(self, timeSamples=None, currentSamples=None, **kwargs):
65 if timeSamples
is not None and currentSamples
is not None:
66 if len(timeSamples) != len(currentSamples):
67 raise RuntimeError(f
"Inconsitent vector lengths: {len(timeSamples)} vs {len(currentSamples)}")
77 if 'integrationMethod' in kwargs:
82 if 'currentScale' in kwargs:
87 if 'day_obs' in kwargs:
89 if 'seq_num' in kwargs:
96 """Construct a PhotodiodeCalib from a dictionary of properties.
101 Dictionary of properties.
105 calib : `lsst.ip.isr.PhotodiodeCalib`
106 Constructed photodiode data.
111 Raised if the supplied dictionary is for a different
116 if calib._OBSTYPE != dictionary[
'metadata'][
'OBSTYPE']:
117 raise RuntimeError(f
"Incorrect photodiode supplied. Expected {calib._OBSTYPE}, "
118 f
"found {dictionary['metadata']['OBSTYPE']}")
120 calib.setMetadata(dictionary[
'metadata'])
122 calib.timeSamples = np.array(dictionary[
'timeSamples']).ravel()
123 calib.currentSamples = np.array(dictionary[
'currentSamples']).ravel()
124 calib.integrationMethod = dictionary.get(
'integrationMethod',
"DIRECT_SUM")
126 calib.updateMetadata()
130 """Return a dictionary containing the photodiode properties.
132 The dictionary should be able to be round-tripped through.
138 Dictionary of properties.
154 """Construct calibration from a list of tables.
156 This method uses the `fromDict` method to create the
157 calibration after constructing an appropriate dictionary from
162 tableList : `list` [`astropy.table.Table`]
163 List of tables to use to construct the crosstalk
168 calib : `lsst.ip.isr.PhotodiodeCalib`
169 The calibration defined in the tables.
171 dataTable = tableList[0]
172 metadata = dataTable.meta
177 for key
in (
"SIMPLE",
"BITPIX",
"NAXIS",
"EXTEND"):
181 instrument = metadata.pop(
"INSTRUME",
None)
182 location = metadata.pop(
"LOCATN",
"NO_LOCATION")
184 if instrument ==
"Electrometer_index_201" and location ==
"AuxTel":
185 metadata[
"INSTRUME"] =
"LATISS"
186 elif location ==
"MainTel" and instrument
in (
"Electrometer_index_101",
187 "Electrometer_index_102",
188 "Electrometer_index_103"):
189 metadata[
"INSTRUME"] =
"LSSTCam"
193 metadata[
"INSTRUME"] = instrument
196 inDict[
'metadata'] = metadata
198 if 'OBSTYPE' not in metadata:
199 inDict[
'metadata'][
'OBSTYPE'] = cls.
_OBSTYPE
200 inDict[
'integrationMethod'] = metadata.pop(
'INTEGRATION_METHOD',
'DIRECT_SUM')
205 for key
in (
'TIME',
'Elapsed Time',
'RNUM'):
206 if key
in dataTable.columns:
207 inDict[
'timeSamples'] = dataTable[key]
208 for key
in (
'CURRENT',
'Signal', ):
209 if key
in dataTable.columns:
210 inDict[
'currentSamples'] = dataTable[key]
215 """Construct a list of tables containing the information in this
218 The list of tables should create an identical calibration
219 after being passed to this class's fromTable method.
223 tableList : `list` [`astropy.table.Table`]
224 List of tables containing the photodiode calibration
231 outMeta = {k: v
for k, v
in inMeta.items()
if v
is not None}
232 outMeta.update({k:
"" for k, v
in inMeta.items()
if v
is None})
234 catalog.meta = outMeta
240 """Construct a PhotodiodeCalib by reading the simple column format.
245 File to read samples from.
249 calib : `lsst.ip.isr.PhotodiodeCalib`
250 The calibration defined in the file.
254 rawData = np.loadtxt(filename, dtype=[(
'time',
'float'), (
'current',
'float')])
256 basename = os.path.basename(filename)
257 cleaned = os.path.splitext(basename)[0]
258 _, _, day_obs, seq_num = cleaned.split(
"_")
260 return cls(timeSamples=rawData[
'time'], currentSamples=rawData[
'current'],
261 day_obs=int(day_obs), seq_num=int(seq_num))
264 """Integrate the current.
268 exposureTime : `float`, optional
269 Image exposure time. Required if integrationMethod is ``MEAN``.
274 Raised if the integration method is not known.
276 Raised if the exposure time is not set and method is MEAN.
285 if exposureTime
is None:
286 raise ValueError(
"Exposure time must be provided if integration method is MEAN.")
289 raise RuntimeError(f
"Unknown integration method {self.integrationMethod}")
294 This uses numpy's trapezoidal integrator.
299 Total charge measured.
304 """Integrate points with a baseline level subtracted.
306 This uses numpy's trapezoidal integrator.
311 Total charge measured.
315 lsst.eotask.gen3.eoPtc
320 currentThreshold = (max(cs) - min(cs))/5.0 + min(cs)
321 lowValueIndices = np.where(cs < currentThreshold)
322 baseline = sigma_clipped_stats(cs[lowValueIndices])[0]
323 return np.trapezoid(cs - baseline, self.
timeSamples)
326 """For this method, the values in .currentSamples are actually the
327 integrated charge values as measured by the ammeter for each
328 sampling interval. We need to do a baseline subtraction,
329 based on the charge values when the LED is off, then sum up
330 the corrected signals.
335 Total charge measured.
351 dy = np.max(current) - np.min(current)
352 signal, = np.where(current > dy/20. + np.min(current))
354 imax = signal[-1] + 2
355 bg = np.concatenate([np.arange(0, imin), np.arange(imax, len(current))])
356 bg_current = np.sum(charge[bg])/np.sum(dt[bg])
358 return np.sum(charge - bg_current*dt)
362 Take the mean of the photodiode trace, and multiply by exposure time.
364 The current scale is also used.
368 exposureTime : `float`
369 Exposure time in sections.
373 return mean * exposureTime
updateMetadata(self, camera=None, detector=None, filterName=None, setCalibId=False, setCalibInfo=False, setDate=False, **kwargs)
integrate(self, exposureTime=None)
fromDict(cls, dictionary)
readTwoColumnPhotodiodeData(cls, filename)
fromTable(cls, tableList, **kwargs)
__init__(self, timeSamples=None, currentSamples=None, **kwargs)
integrateMean(self, exposureTime)
integrateTrimmedSum(self)