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

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 

22import os 

23import sys 

24import unittest 

25 

26import lsst.utils.tests 

27from lsst.utils import getPackageDir 

28from lsst.daf.persistence import Butler 

29from lsst.afw.cameraGeom import Camera, Detector 

30from lsst.afw.image import ImageFitsReader 

31import lsst.obs.base.yamlCamera as yamlCamera 

32 

33from lsst.obs.lsst.assembly import fixAmpGeometry 

34from lsst.obs.lsst.utils import readRawFile 

35 

36PACKAGE_DIR = getPackageDir("obs_lsst") 

37TESTDIR = os.path.dirname(__file__) 

38LATISS_DATA_ROOT = os.path.join(PACKAGE_DIR, "data", "input", "latiss") 

39BAD_OVERSCAN_GEN2_DATA_ID = {'dayObs': '2018-09-20', 'seqNum': 65, 'detector': 0} 

40BAD_OVERSCAN_FILENAME = "raw/2018-09-20/3018092000065-det000.fits" 

41LOCAL_DATA_ROOT = os.path.join(TESTDIR, "data") 

42 

43 

44class RawAssemblyTestCase(lsst.utils.tests.TestCase): 

45 

46 def setUp(self): 

47 # A snapshot of LATISS that has incorrect overscan regions for this 

48 # data ID 

49 self.cameraBroken = Camera.readFits(os.path.join(LOCAL_DATA_ROOT, "camera-bad-overscan.fits")) 

50 # A snapshot of the Detector for this file after we've read it in with 

51 # code that fixes the overscans. 

52 self.detectorFixed = Detector.readFits(os.path.join(LOCAL_DATA_ROOT, "detector-fixed-assembled.fits")) 

53 self.assertEqual(self.cameraBroken[0].getName(), self.detectorFixed.getName()) 

54 

55 def assertAmpRawBBoxesEqual(self, amp1, amp2): 

56 """Check that Raw bounding boxes match between amps. 

57 

58 Parameters 

59 ---------- 

60 amp1 : `~lsst.afw.cameraGeom.Amplifier` 

61 First amplifier. 

62 amp2 : `~lsst.afw.cameraGeom.Amplifier` 

63 Second amplifier. 

64 """ 

65 self.assertEqual(amp1.getRawBBox(), amp2.getRawBBox()) 

66 self.assertEqual(amp1.getRawHorizontalOverscanBBox(), amp2.getRawHorizontalOverscanBBox()) 

67 self.assertEqual(amp1.getRawVerticalOverscanBBox(), amp2.getRawVerticalOverscanBBox()) 

68 

69 def assertAmpRawBBoxesFlippablyEqual(self, amp1, amp2): 

70 """Check that amp1 can be self-consistently transformed to match amp2. 

71 

72 This method compares amplifier bounding boxes by confirming 

73 that they represent the same segment of the detector image. 

74 If the offsets or amplifier flips differ between the 

75 amplifiers, this method will pass even if the raw bounding 

76 boxes returned by the amplifier accessors are not equal. 

77 

78 Parameters 

79 ---------- 

80 amp1 : `~lsst.afw.cameraGeom.Amplifier` 

81 Amplifier to transform. 

82 amp2 : `~lsst.afw.cameraGeom.Amplifier` 

83 Reference amplifier. 

84 

85 """ 

86 xFlip = amp1.getRawFlipX() ^ amp2.getRawFlipX() 

87 yFlip = amp1.getRawFlipY() ^ amp2.getRawFlipY() 

88 XYOffset = amp1.getRawXYOffset() - amp2.getRawXYOffset() 

89 

90 testRawBox = amp1.getRawBBox() 

91 testHOSBox = amp1.getRawHorizontalOverscanBBox() 

92 testVOSBox = amp1.getRawVerticalOverscanBBox() 

93 

94 if xFlip: 

95 size = amp1.getRawBBox().getWidth() 

96 testRawBox.flipLR(size) 

97 testHOSBox.flipLR(size) 

98 testVOSBox.flipLR(size) 

99 if yFlip: 

100 size = amp1.getRawBBox().getHeight() 

101 testRawBox.flipTB(size) 

102 testHOSBox.flipTB(size) 

103 testVOSBox.flipTB(size) 

104 

105 testRawBox.shift(XYOffset) 

106 testHOSBox.shift(XYOffset) 

107 testVOSBox.shift(XYOffset) 

108 

109 self.assertEqual(testRawBox, amp2.getRawBBox()) 

110 self.assertEqual(testHOSBox, amp2.getRawHorizontalOverscanBBox()) 

111 self.assertEqual(testVOSBox, amp2.getRawVerticalOverscanBBox()) 

112 

113 def testGen2GetBadOverscan(self): 

114 """Test that we can use the Gen2 Butler to read a file with overscan 

115 regions that disagree with cameraGeom, and that the detector attached 

116 to it has its overscan regions corrected. 

117 

118 This is essentially just a regression test, and an incomplete one at 

119 that: the fixed Detector snapshot that we're comparing to was generated 

120 by the same code we're calling here. And because the LATISS 

121 associated by the Butler we use in this test may in the future be 

122 corrected to have the right overscan regions, we may end up just 

123 testing a simpler case than we intended. We'll use a snapshot of 

124 the incorrect Camera in other tests to get coverage of that case. 

125 """ 

126 butler = Butler(LATISS_DATA_ROOT) 

127 raw = butler.get("raw", dataId=BAD_OVERSCAN_GEN2_DATA_ID) 

128 for amp1, amp2 in zip(self.detectorFixed, raw.getDetector()): 

129 with self.subTest(amp=amp1.getName()): 

130 self.assertEqual(amp1.getName(), amp2.getName()) 

131 self.assertAmpRawBBoxesEqual(amp1, amp2) 

132 

133 def testFixBadOverscans(self): 

134 """Test the low-level code for repairing cameraGeom overscan regions 

135 that disagree with raw files. 

136 """ 

137 testFile = os.path.join(LATISS_DATA_ROOT, BAD_OVERSCAN_FILENAME) 

138 

139 for i, (ampBad, ampGood) in enumerate(zip(self.cameraBroken[0], self.detectorFixed)): 

140 with self.subTest(amp=ampBad.getName()): 

141 self.assertEqual(ampBad.getName(), ampGood.getName()) 

142 hdu = i + 1 

143 reader = ImageFitsReader(testFile, hdu=hdu) 

144 metadata = reader.readMetadata() 

145 image = reader.read() 

146 self.assertEqual(ampGood.getRawBBox().getDimensions(), image.getBBox().getDimensions()) 

147 self.assertNotEqual(ampBad.getRawBBox().getDimensions(), image.getBBox().getDimensions()) 

148 newAmp, modified = fixAmpGeometry(ampBad, image.getBBox(), metadata) 

149 self.assertTrue(modified) 

150 self.assertNotEqual(newAmp.getRawBBox().getDimensions(), ampBad.getRawBBox().getDimensions()) 

151 self.assertAmpRawBBoxesFlippablyEqual(newAmp, ampGood) 

152 

153 

154class ReadRawFileTestCase(lsst.utils.tests.TestCase): 

155 def testReadRawLatissFile(self): 

156 fileName = os.path.join(LATISS_DATA_ROOT, BAD_OVERSCAN_FILENAME) 

157 policy = os.path.join(PACKAGE_DIR, "policy", "latiss.yaml") 

158 camera = yamlCamera.makeCamera(policy) 

159 exposure = readRawFile(fileName, camera[0], dataId={"file": fileName}) 

160 self.assertIsInstance(exposure, lsst.afw.image.Exposure) 

161 md = exposure.getMetadata() 

162 self.assertIn("INSTRUME", md) 

163 

164 

165def setup_module(module): 

166 lsst.utils.tests.init() 

167 

168 

169if __name__ == "__main__": 169 ↛ 170line 169 didn't jump to line 170, because the condition on line 169 was never true

170 setup_module(sys.modules[__name__]) 

171 unittest.main()