-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
5 changed files
with
460 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,239 @@ | ||
# This file is currently part of obs_lsst but is written to allow it | ||
# to be migrated to the astro_metadata_translator package at a later date. | ||
# | ||
# This product includes software developed by the LSST Project | ||
# (http://www.lsst.org). | ||
# See the LICENSE file in this directory for details of code ownership. | ||
# | ||
# Use of this source code is governed by a 3-clause BSD-style | ||
# license that can be found in the LICENSE file. | ||
|
||
"""Metadata translation code for LSST BNL TestStand 3 headers""" | ||
|
||
__all__ = ("LsstTS3Translator", ) | ||
|
||
import logging | ||
import re | ||
import os.path | ||
|
||
import astropy.units as u | ||
from astropy.time import Time | ||
|
||
from astro_metadata_translator import cache_translation, StubTranslator | ||
|
||
log = logging.getLogger(__name__) | ||
|
||
# Define the group name for TS8 globally so that it can be used | ||
# in multiple places. There is only a single sensor so define a | ||
# raft for consistency with other LSST cameras. | ||
_DETECTOR_GROUP_NAME = "RXX" | ||
_DETECTOR_NAME = "S00" | ||
|
||
|
||
class LsstTS3Translator(StubTranslator): | ||
"""Metadata translator for LSST BNL Test Stand 3 data. | ||
""" | ||
|
||
name = "LSST-TS3" | ||
"""Name of this translation class""" | ||
|
||
_const_map = { | ||
# TS3 is not attached to a telescope so many translations are null. | ||
"telescope": "LSST", | ||
"location": None, | ||
"boresight_rotation_coord": None, | ||
"boresight_rotation_angle": None, | ||
"boresight_airmass": None, | ||
"tracking_radec": None, | ||
"altaz_begin": None, | ||
"object": "UNKNOWN", | ||
"relative_humidity": None, | ||
"temperature": None, | ||
"pressure": None, | ||
"detector_group": _DETECTOR_GROUP_NAME, | ||
"detector_name": _DETECTOR_NAME, # Single sensor | ||
"detector_num": 0, | ||
} | ||
|
||
_trivial_map = { | ||
"detector_serial": "LSST_NUM", | ||
"physical_filter": "FILTER", | ||
"exposure_time": ("EXPTIME", dict(unit=u.s)), | ||
} | ||
|
||
DETECTOR_GROUP_NAME = _DETECTOR_GROUP_NAME | ||
"""Fixed name of detector group.""" | ||
|
||
DETECTOR_NAME = _DETECTOR_NAME | ||
"""Fixed name of single sensor.""" | ||
|
||
@classmethod | ||
def can_translate(cls, header, filename=None): | ||
"""Indicate whether this translation class can translate the | ||
supplied header. | ||
There is no usable ``INSTRUME`` header in TS3 data. Instead we use | ||
the ``TSTAND`` header. | ||
Parameters | ||
---------- | ||
header : `dict`-like | ||
Header to convert to standardized form. | ||
filename : `str`, optional | ||
Name of file being translated. | ||
Returns | ||
------- | ||
can : `bool` | ||
`True` if the header is recognized by this class. `False` | ||
otherwise. | ||
""" | ||
return cls.can_translate_with_options(header, {"TSTAND": "BNL-TS3-2-Janeway"}, filename=filename) | ||
|
||
@staticmethod | ||
def compute_detector_exposure_id(exposure_id, detector_num): | ||
"""Compute the detector exposure ID from detector number and | ||
exposure ID. | ||
This is a helper method to allow code working outside the translator | ||
infrastructure to use the same algorithm. | ||
Parameters | ||
---------- | ||
exposure_id : `int` | ||
Unique exposure ID. | ||
detector_num : `int` | ||
Detector number. | ||
Returns | ||
------- | ||
detector_exposure_id : `int` | ||
The calculated ID. | ||
""" | ||
if detector_num != 0: | ||
log.warning("Unexpected non-zero detector number for TS3") | ||
return exposure_id | ||
|
||
@staticmethod | ||
def compute_exposure_id(dateobs, seqnum=0): | ||
"""Helper method to calculate the TS8 exposure_id. | ||
Parameters | ||
---------- | ||
dateobs : `str` | ||
Date of observation in FITS ISO format. | ||
seqnum : `int`, unused | ||
Sequence number. Ignored. | ||
Returns | ||
------- | ||
exposure_id : `int` | ||
Exposure ID. | ||
""" | ||
# There is worry that seconds are too coarse so use 10th of second | ||
# and read the first 21 characters. | ||
exposure_id = re.sub(r"\D", "", dateobs[:21]) | ||
return int(exposure_id) | ||
|
||
@cache_translation | ||
def to_instrument(self): | ||
"""Calculate the instrument name. | ||
Returns | ||
------- | ||
instrume : `str` | ||
Name of the test stand and manufacturer. | ||
For example: "TS3-E2V" | ||
""" | ||
manu = self._header["CCD_MANU"] | ||
self._used_these_cards("CCD_MANU") | ||
return f"TS3-{manu}" | ||
|
||
@cache_translation | ||
def to_datetime_begin(self): | ||
# Docstring will be inherited. Property defined in properties.py | ||
self._used_these_cards("MJD-OBS") | ||
return Time(self._header["MJD-OBS"], scale="utc", format="mjd") | ||
|
||
@cache_translation | ||
def to_datetime_end(self): | ||
# Docstring will be inherited. Property defined in properties.py | ||
return self.to_datetime_begin() + self.to_exposure_time() | ||
|
||
@cache_translation | ||
def to_dark_time(self): | ||
"""Calculate the dark time. | ||
If a DARKTIME header is not found, the value is assumed to be | ||
identical to the exposure time. | ||
Returns | ||
------- | ||
dark : `astropy.units.Quantity` | ||
The dark time in seconds. | ||
""" | ||
if "DARKTIME" in self._header: | ||
darktime = self._header("DARKTIME")*u.s | ||
else: | ||
log.warning("Unable to determine dark time. Setting from exposure time.") | ||
darktime = self.to_exposure_time() | ||
return darktime | ||
|
||
@cache_translation | ||
def to_detector_exposure_id(self): | ||
# Docstring will be inherited. Property defined in properties.py | ||
exposure_id = self.to_exposure_id() | ||
num = self.to_detector_num() | ||
return self.compute_detector_exposure_id(exposure_id, num) | ||
|
||
def to_exposure_id(self): | ||
"""Generate a unique exposure ID number | ||
Note that SEQNUM is not unique for a given day in TS8 data | ||
so instead we convert the ISO date of observation directly to an | ||
integer. | ||
Returns | ||
------- | ||
exposure_id : `int` | ||
Unique exposure number. | ||
""" | ||
iso = self._header["DATE-OBS"] | ||
self._used_these_cards("DATE-OBS") | ||
|
||
return self.compute_exposure_id(iso) | ||
|
||
# For now assume that visit IDs and exposure IDs are identical | ||
to_visit_id = to_exposure_id | ||
|
||
@cache_translation | ||
def to_science_program(self): | ||
"""Calculate the science program information. | ||
There is no header recording this in TS3 data so instead return | ||
the observing day in YYYY-MM-DD format. | ||
Returns | ||
------- | ||
run : `str` | ||
Observing day in YYYY-MM-DD format. | ||
""" | ||
# Get a copy so that we can edit the default formatting | ||
date = self.to_datetime_begin().copy() | ||
date.format = "iso" | ||
date.out_subfmt = "date" # YYYY-MM-DD format | ||
return str(date) | ||
|
||
@cache_translation | ||
def to_observation_id(self): | ||
# Docstring will be inherited. Property defined in properties.py | ||
filename = self._header["FILENAME"] | ||
self._used_these_cards("FILENAME") | ||
return os.path.splitext(filename)[0] | ||
|
||
@cache_translation | ||
def to_observation_type(self): | ||
# Docstring will be inherited. Property defined in properties.py | ||
obstype = self._header["IMGTYPE"] | ||
self._used_these_cards("IMGTYPE") | ||
return obstype.lower() |
91 changes: 91 additions & 0 deletions
91
tests/headers/ts3-E2V-CCD250-411_lambda_flat_1000_025_20181115075559.yaml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
!!python/object/apply:collections.OrderedDict | ||
- - [SIMPLE, true] | ||
- [BITPIX, 16] | ||
- [NAXIS, 2] | ||
- [EXTEND, true] | ||
- [ORIGIN, BNL] | ||
- [DATE, '2018-11-15T12:55:59.113'] | ||
- [DATE-OBS, '2018-11-15T12:55:11.149'] | ||
- [MJD-OBS, 58437.538323483896] | ||
- [IMAGETAG, CCS] | ||
- [TSTAND, BNL-TS3-2-Janeway] | ||
- [INSTRUME, ArchonCCDController] | ||
- [CONTROLL, ArchonCCDController] | ||
- [CCD_MANU, E2V] | ||
- [CCD_TYPE, E2V11093-12-01] | ||
- [CCD_SERN, E2V-CCD250-411] | ||
- [LSST_NUM, E2V-CCD250-411] | ||
- [TESTTYPE, LAMBDA] | ||
- [IMGTYPE, FLAT] | ||
- [SEQNUM, 25] | ||
- [TEMP_SET, -95.0] | ||
- [CCDTEMP, -94.6311] | ||
- [CCDBSS, -50.0] | ||
- [MONDIODE, -29.45297597062094] | ||
- [MONOWL, 1000.008] | ||
- [PIXRATE, -1.0] | ||
- [FILTER, 550CutOn] | ||
- [FILTPOS, 3] | ||
- [EXPTIME, 44.631] | ||
- [CTLRCFG, CCD250_20161109_RCLR4_e2v.acf] | ||
- [FILENAME, E2V-CCD250-411_lambda_flat_1000_025_20181115075559.fits] | ||
- [DETSIZE, '[1:4096,1:4004]'] | ||
- [BINX, 1] | ||
- [BINY, 1] | ||
- [HEADVER, 1] | ||
- [CCDGAIN, 3.0] | ||
- [CCDNOISE, 1.0] | ||
- [CFGFILE, CCD250_20161109_RCLR4_e2v.acf] | ||
- [CTLRID, '30584592850333780'] | ||
- [CTLRRV, 3] | ||
- [CTLRTYP, 1] | ||
- [CTLRVER, 1.0.762] | ||
- [MONOCH-WAVELENG, 1000.008] | ||
- [MONOCH-SLIT_A, 210.0] | ||
- [MONOCH-SLIT_B, 210.0] | ||
- [MONOCH-SLIT_C, 0.0] | ||
- [MONOCH-BANDPASS, 0.0] | ||
- [MONOCH-FILT_1, 3.0] | ||
- [MONOCH-MSTEPS, 1200.0] | ||
- [MONOCH-GRATING, 1.0] | ||
- [AMP0-IDN, 'KEITHLEY-INSTRUMENTS-INC.,MODEL-6487'] | ||
- [AMP0-AZERO, F] | ||
- [AMP0-COUNT, 2048] | ||
- [AMP0-MEAS_NPLC, 1] | ||
- [AMP2-IDN, 'KEITHLEY-INSTRUMENTS-INC.,MODEL-6487'] | ||
- [AMP2-VOLTAGE, -50.0] | ||
- [AMP2-CURRENT, -37607.009999999995] | ||
- [AMP2-ZERO_CHECK, 'off'] | ||
- [CHECKSUM, 6SI36PF26PF26PF2] | ||
- [DATASUM, '3400510793'] | ||
- [XTENSION, IMAGE] | ||
- [NAXIS1, 572] | ||
- [NAXIS2, 2048] | ||
- [PCOUNT, 0] | ||
- [GCOUNT, 1] | ||
- [CHANNEL, 1] | ||
- [EXTNAME, Segment10] | ||
- [DATASEC, '[11:522,1:2002]'] | ||
- [DETSEC, '[512:1,4004:2003]'] | ||
- [BIASSEC, '[523:572,1:2002]'] | ||
- [CCDSUM, 1 1] | ||
- [AVERAGE, 8421.654815048383] | ||
- [AVGBIAS, 1473.132128338105] | ||
- [STDVBIAS, 2.8196067949998382] | ||
- [STDEV, 465.74800104878386] | ||
- [LTV1, 512.0] | ||
- [LTV2, 4004.0] | ||
- [LTM1_1, -1.0] | ||
- [LTM2_2, -1.0] | ||
- [WCSNAME, PRIMARY] | ||
- [CTYPE1, CCD_X] | ||
- [CTYPE2, CCD_Y] | ||
- [CD1_1, -1.0] | ||
- [CD1_2, 0.0] | ||
- [CD2_1, 0.0] | ||
- [CD2_2, -1.0] | ||
- [CRPIX1, 573.0] | ||
- [CRPIX2, 4097.0] | ||
- [BSCALE, 1.0] | ||
- [BZERO, 32768] | ||
|
Oops, something went wrong.