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# 

2# LSST Data Management System 

3# 

4# Copyright 2008-2016 AURA/LSST. 

5# 

6# This product includes software developed by the 

7# LSST Project (http://www.lsst.org/). 

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 LSST License Statement and 

20# the GNU General Public License along with this program. If not, 

21# see <https://www.lsstcorp.org/LegalNotices/>. 

22# 

23import math 

24import os.path 

25import unittest 

26import pickle 

27 

28import lsst.utils.tests 

29from lsst.obs.hsc import HscMapper 

30from lsst.pipe.base import Struct 

31from lsst.afw.geom import transformRegistry 

32from lsst.afw.cameraGeom import FOCAL_PLANE, FIELD_ANGLE, PIXELS 

33 

34# Set SAVE_DATA True to save new distortion data; this will make the test fail, 

35# to remind you to set it False before committing the code. 

36SAVE_DATA = False 

37 

38DataFileName = "data/distortionData.pickle" # path relative to the tests directory 

39 

40 

41class HscDistortionTestCase(lsst.utils.tests.TestCase): 

42 """Testing HscDistortion implementation 

43 

44 HscDistortion is based on the HSC package "distEst". We 

45 test that it produces the same results. 

46 """ 

47 def testDistortion(self): 

48 """Test that the distortion data matches the saved data or create new data 

49 

50 If SAVE_DATA is true then save newly created data and then fail the test 

51 in order to prevent anyone from committing the test with SAVE_DATA true! 

52 

53 Otherwise create new data and compare to the saved data 

54 """ 

55 newData = self.makeDistortionData() 

56 dataPath = os.path.join(os.path.dirname(__file__), DataFileName) 

57 

58 if SAVE_DATA: 

59 with open(dataPath, "wb") as dataFile: 

60 pickle.dump(newData, dataFile, protocol=2) 

61 self.fail("Saved new data to %r; please set SAVE_DATA back to False" % dataPath) 

62 

63 if not os.path.exists(dataPath): 

64 self.fail("Cannot find saved data %r; set SAVE_DATA = True and run again to save new data" % 

65 dataPath) 

66 

67 fieldAngleToFocalPlaneTolerance = transformRegistry["hsc"].ConfigClass().tolerance 

68 

69 with open(dataPath, "rb") as dataFile: 

70 savedData = pickle.load(dataFile) 

71 maxRoundTripFocalPlaneError = 0 

72 maxRoundTripPixPosError = 0 

73 for detectorName, ccdData in newData.items(): 

74 savedCcdData = savedData[detectorName] 

75 self.assertEqual(ccdData.serial, savedCcdData.serial) 

76 for pixPosKey, cornerData in ccdData.cornerDict.items(): 

77 savedCornerData = savedCcdData.cornerDict[pixPosKey] 

78 self.assertEqual(cornerData.pixPos, savedCornerData.pixPos) 

79 self.assertPairsAlmostEqual(cornerData.focalPlane, savedCornerData.focalPlane) 

80 self.assertPairsAlmostEqual(cornerData.fieldAngle, savedCornerData.fieldAngle) 

81 maxRoundTripFocalPlaneError = max( 

82 maxRoundTripFocalPlaneError, 

83 math.hypot(*(cornerData.focalPlaneRoundTrip - cornerData.focalPlane)) 

84 ) 

85 self.assertPairsAlmostEqual(cornerData.focalPlaneRoundTrip, cornerData.focalPlane, 

86 maxDiff=fieldAngleToFocalPlaneTolerance) 

87 maxRoundTripPixPosError = max(maxRoundTripPixPosError, 

88 math.hypot(*(cornerData.pixPosRoundTrip - cornerData.pixPos))) 

89 self.assertPairsAlmostEqual(cornerData.pixPosRoundTrip, cornerData.pixPos) 

90 print("maxRoundTripFocalPlaneError =", maxRoundTripFocalPlaneError) 

91 print("maxRoundTripPixPosError =", maxRoundTripPixPosError) 

92 

93 def makeDistortionData(self): 

94 """Make distortion data 

95 

96 The data format is a dict of detector name: ccdData, where 

97 ccdData is a Struct containing these fields: 

98 - serial: detector.getSerial 

99 - cornerDict: a dict of pixPosKey, cornerData, where: 

100 - pixPosKey: self.asKey(pixPos) where pixPos is pixel position 

101 - cornerData is Struct contains these fields (all of type lsst.geom.Point2D): 

102 - pixPos: pixel position 

103 - focalPlane: focal plane position computed from pixPos 

104 - fieldAngle: fieldAngle position computed from focalPlane 

105 - focalPlaneRoundTrip: focal plane position computed from fieldAngle 

106 - pixPosRoundTrip: pixel position computed from focalPlane 

107 """ 

108 camera = HscMapper(root=".", calibRoot=".").camera 

109 focalPlaneToFieldAngle = camera.getTransformMap().getTransform(FOCAL_PLANE, FIELD_ANGLE) 

110 data = {} # dict of detector name: CcdData 

111 for detector in camera: 

112 # for each corner of each CCD: 

113 # - get pixel position 

114 # - convert to focal plane coordinates using the detector and record it 

115 # - convert to field angle (this is the conversion that uses HscDistortion) and record it 

116 # - convert back to focal plane (testing inverse direction of HscDistortion) and record it 

117 # - convert back to pixel position and record it; pixel <-> focal plane is affine 

118 # so there is no reason to doubt the inverse transform, but there is no harm 

119 pixelsToFocalPlane = detector.getTransform(PIXELS, FOCAL_PLANE) 

120 cornerDict = {} 

121 for pixPos in detector.getCorners(PIXELS): 

122 pixPos = pixPos 

123 focalPlane = pixelsToFocalPlane.applyForward(pixPos) 

124 fieldAngle = focalPlaneToFieldAngle.applyForward(focalPlane) 

125 focalPlaneRoundTrip = focalPlaneToFieldAngle.applyInverse(fieldAngle) 

126 pixPosRoundTrip = pixelsToFocalPlane.applyInverse(focalPlane) 

127 cornerDict[self.toKey(pixPos)] = Struct( 

128 pixPos = pixPos, 

129 focalPlane = focalPlane, 

130 fieldAngle = fieldAngle, 

131 focalPlaneRoundTrip = focalPlaneRoundTrip, 

132 pixPosRoundTrip = pixPosRoundTrip, 

133 ) 

134 

135 data[detector.getName()] = Struct(serial=detector.getSerial(), cornerDict=cornerDict) 

136 

137 return data 

138 

139 def toKey(self, pixPos): 

140 return "(%0.1f, %0.1f)" % tuple(pixPos) 

141 

142 

143class TestMemory(lsst.utils.tests.MemoryTestCase): 

144 def setUp(self): 

145 HscMapper.clearCache() 

146 lsst.utils.tests.MemoryTestCase.setUp(self) 

147 

148 

149def setup_module(module): 

150 lsst.utils.tests.init() 

151 

152 

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

154 lsst.utils.tests.init() 

155 unittest.main()