Source code for gemini_instruments.nifs.adclass

import math

from astrodata import astro_data_tag, astro_data_descriptor, returns_list, TagSet
from ..gemini import AstroDataGemini
from .lookup import constants_by_bias, config_dict, lnrs_mode_map

# NOTE: Temporary functions for test. gempy imports astrodata and
#       won't work with this implementation
from .. import gmu

[docs]class AstroDataNifs(AstroDataGemini): __keyword_dict = dict(array_section = 'DATASEC', camera = 'INSTRUME', central_wavelength = 'GRATWAVE', detector_section = 'DATASEC', disperser = 'GRATING', focal_plane_mask = 'APERTURE', observation_epoch = 'EPOCH') @staticmethod def _matches_data(source): return source[0].header.get('INSTRUME', '').upper() == 'NIFS' @astro_data_tag def _tag_instrument(self): return TagSet(['NIFS']) @astro_data_tag def _tag_dark(self): if self.phu.get('OBSTYPE') == 'DARK': return TagSet(['DARK', 'CAL'], blocks=['IMAGE', 'SPECT']) @astro_data_tag def _tag_image(self): if self.phu.get('FLIP') == 'In': return TagSet(['IMAGE']) @astro_data_tag def _tag_arc(self): if self.phu.get('OBSTYPE') == 'ARC': return TagSet(['ARC', 'CAL']) @astro_data_tag def _tag_ronchi(self): req = self.phu.get('OBSTYPE'), self.phu.get('APERTURE') if req == ('FLAT', 'Ronchi_Screen_G5615'): return TagSet(['RONCHI', 'CAL']) @astro_data_tag def _tag_spect(self): if self.phu.get('FLIP') == 'Out': return TagSet(['SPECT', 'IFU'])
[docs] @astro_data_descriptor def filter_name(self, stripID=False, pretty=False): """ Returns the name of the filter(s) used. The component ID can be removed with either 'stripID' or 'pretty'. If 'pretty' is True, filter positions such as 'Open', 'Dark', 'blank', and others are removed leaving only the relevant filters in the string. Parameters ---------- stripID : bool If True, removes the component ID and returns only the name of the filter. pretty : bool Same as for stripID. Pretty here does not do anything more. Returns ------- str The name of the filter with or without the component ID. """ filt = self.phu.get('FILTER') if stripID or pretty: filt = gmu.removeComponentID(filt) return 'blank' if filt == 'Blocked' else filt
def _from_biaspwr(self, constant_name): bias_volt = self.phu.get('BIASPWR') for bias, constants in constants_by_bias.items(): if abs(bias - bias_volt) < 0.1: return getattr(constants, constant_name, None) raise KeyError("The bias value for this image doesn't match any on the lookup table")
[docs] @returns_list @astro_data_descriptor def gain(self): """ Returns the gain used for the observation. A lookup table is uses to compare the bias value in the headers to the bias values associate with the various gain settings. Returns ------- float Gain used for the observation. """ return self._from_biaspwr("gain")
[docs] @astro_data_descriptor def gcal_lamp(self): """ Returns the name of the GCAL lamp being used, or "Off" if no lamp is in used. This applies to flats and arc observations when a lamp is used. For other types observation, None is returned. This overrides the gemini level descriptor, as NIFS has more lamp names than are accommodated by that descriptor function. Returns ------- lamps: <str> Name of the GCAL lamp, or "Off" """ lamps, shut = self.phu.get('GCALLAMP'), self.phu.get('GCALSHUT') if lamps is None: return None if shut and "CLOSED" in shut.upper(): return 'Off' elif lamps and "OPEN" in shut.upper(): return lamps
[docs] @astro_data_descriptor def non_linear_level(self): """ Returns the level at which the array becomes non-linear. The return units are ADUs. A lookup table is used. Whether the data has been corrected for non-linearity or not is taken into account. A list is returned unless called on a single-extension slice. Returns ------- int/list Level in ADU at which the non-linear regime starts. """ saturation_level = self.saturation_level() corrected = 'NONLINCR' in self.phu linear_limit = self._from_biaspwr("linearlimit" if corrected else "nonlinearlimit") if self.is_single: try: return int(saturation_level * linear_limit) except TypeError: return None else: return [int(linear_limit * s) if linear_limit and s else None for s in saturation_level]
[docs] @astro_data_descriptor def pixel_scale(self): """ Returns the pixel scale in arc seconds. A lookup table indexed on focal_plane_mask, disperser, and filter_name is used. Returns ------- lfloat Pixel scale in arcsec. """ fpm = self.focal_plane_mask() disp = self.disperser() filt = self.filter_name() return getattr(config_dict.get((fpm, disp, filt)), 'pixscale', None)
[docs] @astro_data_descriptor def read_mode(self): """ Returns the read mode for the observation. The read mode is directly associated with the LNRS header keyword value. Returns ------- str Read mode for the observation. """ # NOTE: The original read_mode descriptor obtains the bias voltage # value, but then it does NOTHING with it. I'll just skip it. return lnrs_mode_map.get(self.phu.get('LNRS'), 'Unknown')
[docs] @returns_list @astro_data_descriptor def read_noise(self): """ Returns the detector read noise, in electrons. A lookup table is used. The read noise depends on the gain setting and is affected by the number of coadds and non-destructive pairs. A list is returned unless called on a single-extension slice. Returns ------- list/float Detector read noise in electrons. """ rn = self._from_biaspwr("readnoise") try: return float(rn * math.sqrt(self.coadds()) / math.sqrt(self.phu.get('LNRS'))) except TypeError: return None
[docs] @returns_list @astro_data_descriptor def saturation_level(self): """ Returns the saturation level for the observation, in ADUs A lookup table is used to get the full well value based on the gain. A list is returned unless called on a single-extension slice. Returns ------- int/list Saturation level in ADUs. """ try: return int(self._from_biaspwr("well") * self.coadds()) except TypeError: return None