Hide keyboard shortcuts

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 obs_decam. 

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 COPYRIGHT file at the top-level directory of this distribution 

7# for details of code ownership. 

8# 

9# This program is free software: you can redistribute it and/or modify 

10# it under the terms of the GNU General Public License as published by 

11# the Free Software Foundation, either version 3 of the License, or 

12# (at your option) any later version. 

13# 

14# This program is distributed in the hope that it will be useful, 

15# but WITHOUT ANY WARRANTY; without even the implied warranty of 

16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 

17# GNU General Public License for more details. 

18# 

19# You should have received a copy of the GNU General Public License 

20# along with this program. If not, see <http://www.gnu.org/licenses/>. 

21 

22"""Gen3 Butler Formatters for Dark Energy Camera raw data. 

23""" 

24 

25import astro_metadata_translator 

26 

27import lsst.afw.fits 

28import lsst.afw.image 

29import lsst.log 

30from lsst.obs.base import FitsRawFormatterBase 

31 

32from . import DarkEnergyCamera 

33 

34__all__ = ("DarkEnergyCameraRawFormatter", "DarkEnergyCameraCPCalibFormatter") 

35 

36 

37# The mapping of detector id to HDU in raw files for "most" DECam data. 

38# We try this first before scaning the HDUs manually. 

39detector_to_hdu = {25: 1, 26: 2, 27: 3, 32: 4, 33: 5, 34: 6, 19: 7, 20: 8, 13: 9, 

40 14: 10, 8: 11, 4: 12, 39: 13, 40: 14, 45: 15, 46: 16, 51: 17, 56: 18, 21: 19, 

41 22: 20, 23: 21, 24: 22, 17: 23, 18: 24, 15: 25, 16: 26, 9: 27, 10: 28, 11: 29, 

42 12: 30, 5: 31, 6: 32, 7: 33, 1: 34, 2: 35, 3: 36, 35: 37, 36: 38, 37: 39, 

43 38: 40, 28: 41, 29: 42, 30: 43, 31: 44, 41: 45, 42: 46, 43: 47, 44: 48, 49: 49, 

44 50: 50, 47: 51, 48: 52, 52: 53, 53: 54, 54: 55, 55: 56, 57: 57, 58: 58, 59: 59, 

45 60: 60, 61: 61, 62: 62, 72: 63, 71: 64, 64: 65, 63: 66, 73: 67, 74: 68, 70: 69, 

46 69: 70 

47 } 

48 

49 

50class DarkEnergyCameraRawFormatter(FitsRawFormatterBase): 

51 translatorClass = astro_metadata_translator.DecamTranslator 

52 filterDefinitions = DarkEnergyCamera.filterDefinitions 

53 

54 def getDetector(self, id): 

55 return DarkEnergyCamera().getCamera()[id] 

56 

57 def _scanHdus(self, filename, detectorId): 

58 """Scan through a file for the HDU containing data from one detector. 

59 

60 Parameters 

61 ---------- 

62 filename : `str` 

63 The file to search through. 

64 detectorId : `int` 

65 The detector id to search for. 

66 

67 Returns 

68 ------- 

69 index : `int` 

70 The index of the HDU with the requested data. 

71 metadata: `lsst.daf.base.PropertyList` 

72 The metadata read from the header for that detector id. 

73 

74 Raises 

75 ------ 

76 ValueError 

77 Raised if detectorId is not found in any of the file HDUs 

78 """ 

79 log = lsst.log.Log.getLogger("DarkEnergyCameraRawFormatter") 

80 log.debug("Did not find detector=%s at expected HDU=%s in %s: scanning through all HDUs.", 

81 detectorId, detector_to_hdu[detectorId], filename) 

82 

83 fitsData = lsst.afw.fits.Fits(filename, 'r') 

84 # NOTE: The primary header (HDU=0) does not contain detector data. 

85 for i in range(1, fitsData.countHdus()): 

86 fitsData.setHdu(i) 

87 metadata = fitsData.readMetadata() 

88 if metadata['CCDNUM'] == detectorId: 

89 return i, metadata 

90 else: 

91 raise ValueError(f"Did not find detectorId={detectorId} as CCDNUM in any HDU of {filename}.") 

92 

93 def _determineHDU(self, detectorId): 

94 """Determine the correct HDU number for a given detector id. 

95 

96 Parameters 

97 ---------- 

98 detectorId : `int` 

99 The detector id to search for. 

100 

101 Returns 

102 ------- 

103 index : `int` 

104 The index of the HDU with the requested data. 

105 metadata : `lsst.daf.base.PropertyList` 

106 The metadata read from the header for that detector id. 

107 

108 Raises 

109 ------ 

110 ValueError 

111 Raised if detectorId is not found in any of the file HDUs 

112 """ 

113 filename = self.fileDescriptor.location.path 

114 try: 

115 index = detector_to_hdu[detectorId] 

116 metadata = lsst.afw.image.readMetadata(filename, index) 

117 if metadata['CCDNUM'] != detectorId: 

118 # the detector->HDU mapping is different in this file: try scanning 

119 return self._scanHdus(filename, detectorId) 

120 else: 

121 fitsData = lsst.afw.fits.Fits(filename, 'r') 

122 fitsData.setHdu(index) 

123 return index, metadata 

124 except lsst.afw.fits.FitsError: 

125 # if the file doesn't contain all the HDUs of "normal" files, try scanning 

126 return self._scanHdus(filename, detectorId) 

127 

128 def readMetadata(self): 

129 index, metadata = self._determineHDU(self.dataId['detector']) 

130 astro_metadata_translator.fix_header(metadata) 

131 return metadata 

132 

133 def readImage(self): 

134 index, metadata = self._determineHDU(self.dataId['detector']) 

135 return lsst.afw.image.ImageI(self.fileDescriptor.location.path, index) 

136 

137 

138class DarkEnergyCameraCPCalibFormatter(DarkEnergyCameraRawFormatter): 

139 """DECam Community Pipeline calibrations (bias, dark, flat, fringe) are 

140 multi-extension FITS files with detector=index+1. 

141 """ 

142 

143 def _determineHDU(self, detectorId): 

144 """The HDU to read is the same as the detector number.""" 

145 filename = self.fileDescriptor.location.path 

146 metadata = lsst.afw.image.readMetadata(filename, detectorId) 

147 if metadata['CCDNUM'] != detectorId: 

148 msg = f"Found CCDNUM={metadata['CCDNUM']} instead of {detectorId} in {filename} HDU={detectorId}." 

149 raise ValueError(msg) 

150 return detectorId, metadata 

151 

152 def readImage(self): 

153 index, metadata = self._determineHDU(self.dataId['detector']) 

154 return lsst.afw.image.ImageF(self.fileDescriptor.location.path, index)