Coverage for python/astro_metadata_translator/translators/megaprime.py : 33%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1# This file is part of astro_metadata_translator.
2#
3# Developed for the LSST Data Management System.
4# This product includes software developed by the LSST Project
5# (http://www.lsst.org).
6# See the LICENSE file at the top-level directory of this distribution
7# for details of code ownership.
8#
9# Use of this source code is governed by a 3-clause BSD-style
10# license that can be found in the LICENSE file.
12"""Metadata translation code for CFHT MegaPrime FITS headers"""
14__all__ = ("MegaPrimeTranslator", )
16import re
17import posixpath
18from astropy.coordinates import EarthLocation, Angle
19import astropy.units as u
21from ..translator import cache_translation, CORRECTIONS_RESOURCE_ROOT
22from .fits import FitsTranslator
23from .helpers import tracking_from_degree_headers, altaz_from_degree_headers
25filters = {'u.MP9301': 'u',
26 'u.MP9302': 'u2',
27 'g.MP9401': 'g',
28 'g.MP9402': 'g2',
29 'r.MP9601': 'r',
30 'r.MP9602': 'r2',
31 'i.MP9701': 'i',
32 'i.MP9702': 'i2',
33 'i.MP9703': 'i3',
34 'z.MP9801': 'z',
35 'z.MP9901': 'z2',
36 }
39class MegaPrimeTranslator(FitsTranslator):
40 """Metadata translator for CFHT MegaPrime standard headers.
41 """
43 name = "MegaPrime"
44 """Name of this translation class"""
46 supported_instrument = "MegaPrime"
47 """Supports the MegaPrime instrument."""
49 default_resource_root = posixpath.join(CORRECTIONS_RESOURCE_ROOT, "CFHT")
50 """Default resource path root to use to locate header correction files."""
52 # CFHT Megacam has no rotator, and the instrument angle on sky is set to
53 # +Y=N, +X=W which we define as a 0 degree rotation.
54 _const_map = {"boresight_rotation_angle": Angle(0*u.deg),
55 "boresight_rotation_coord": "sky",
56 "detector_group": None}
58 _trivial_map = {"physical_filter": "FILTER",
59 "dark_time": ("DARKTIME", dict(unit=u.s)),
60 "exposure_time": ("EXPTIME", dict(unit=u.s)),
61 "observation_id": "OBSID",
62 "object": "OBJECT",
63 "science_program": "RUNID",
64 "exposure_id": "EXPNUM",
65 "visit_id": "EXPNUM",
66 "detector_serial": "CCDNAME",
67 "relative_humidity": ["RELHUMID", "HUMIDITY"],
68 "temperature": (["TEMPERAT", "AIRTEMP"], dict(unit=u.deg_C)),
69 "boresight_airmass": ["AIRMASS", "BORE-AIRMASS"]}
71 @cache_translation
72 def to_datetime_begin(self):
73 # Docstring will be inherited. Property defined in properties.py
74 # We know it is UTC
75 value = self._from_fits_date_string(self._header["DATE-OBS"],
76 time_str=self._header["UTC-OBS"], scale="utc")
77 self._used_these_cards("DATE-OBS", "UTC-OBS")
78 return value
80 @cache_translation
81 def to_datetime_end(self):
82 # Docstring will be inherited. Property defined in properties.py
83 # Older files are missing UTCEND
84 if self.is_key_ok("UTCEND"):
85 # We know it is UTC
86 value = self._from_fits_date_string(self._header["DATE-OBS"],
87 time_str=self._header["UTCEND"], scale="utc")
88 self._used_these_cards("DATE-OBS", "UTCEND")
89 else:
90 # Take a guess by adding on the exposure time
91 value = self.to_datetime_begin() + self.to_exposure_time()
92 return value
94 @cache_translation
95 def to_location(self):
96 """Calculate the observatory location.
98 Returns
99 -------
100 location : `astropy.coordinates.EarthLocation`
101 An object representing the location of the telescope.
102 """
103 # Height is not in some MegaPrime files. Use the value from
104 # EarthLocation.of_site("CFHT")
105 # Some data uses OBS-LONG, OBS-LAT, other data uses LONGITUD and
106 # LATITUDE
107 for long_key, lat_key in (("LONGITUD", "LATITUDE"), ("OBS-LONG", "OBS-LAT")):
108 if self.are_keys_ok([long_key, lat_key]):
109 value = EarthLocation.from_geodetic(self._header[long_key], self._header[lat_key], 4215.0)
110 self._used_these_cards(long_key, lat_key)
111 break
112 else:
113 value = EarthLocation.of_site("CFHT")
114 return value
116 @cache_translation
117 def to_detector_name(self):
118 # Docstring will be inherited. Property defined in properties.py
119 if self.is_key_ok("EXTNAME"):
120 name = self._header["EXTNAME"]
121 # Only valid name has form "ccdNN"
122 if re.match(r"ccd\d+$", name):
123 self._used_these_cards("EXTNAME")
124 return name
126 # Dummy value, intended for PHU (need something to get filename)
127 return "ccd99"
129 @cache_translation
130 def to_detector_num(self):
131 name = self.to_detector_name()
132 return int(name[3:])
134 @cache_translation
135 def to_observation_type(self):
136 """Calculate the observation type.
138 Returns
139 -------
140 typ : `str`
141 Observation type. Normalized to standard set.
142 """
143 obstype = self._header["OBSTYPE"].strip().lower()
144 self._used_these_cards("OBSTYPE")
145 if obstype == "object":
146 return "science"
147 return obstype
149 @cache_translation
150 def to_tracking_radec(self):
151 """Calculate the tracking RA/Dec for this observation.
153 Currently will be `None` for geocentric apparent coordinates.
154 Additionally, can be `None` for non-science observations.
156 The method supports multiple versions of header defining tracking
157 coordinates.
159 Returns
160 -------
161 coords : `astropy.coordinates.SkyCoord`
162 The tracking coordinates.
163 """
164 radecsys = ("RADECSYS", "OBJRADEC", "RADESYS")
165 radecpairs = (("RA_DEG", "DEC_DEG"), ("BORE-RA", "BORE-DEC"))
166 return tracking_from_degree_headers(self, radecsys, radecpairs)
168 @cache_translation
169 def to_altaz_begin(self):
170 # Docstring will be inherited. Property defined in properties.py
171 return altaz_from_degree_headers(self, (("TELALT", "TELAZ"), ("BORE-ALT", "BORE-AZ")),
172 self.to_datetime_begin())
174 @cache_translation
175 def to_detector_exposure_id(self):
176 # Docstring will be inherited. Property defined in properties.py
177 return self.to_exposure_id() * 36 + self.to_detector_num()
179 @cache_translation
180 def to_pressure(self):
181 # Docstring will be inherited. Property defined in properties.py
182 # Can be either AIRPRESS in Pa or PRESSURE in mbar
183 for key, unit in (("PRESSURE", u.hPa), ("AIRPRESS", u.Pa)):
184 if self.is_key_ok(key):
185 return self.quantity_from_card(key, unit)
186 else:
187 raise KeyError("Could not find pressure keywords in header")