Coverage for tests/test_distortion.py : 25%

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
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
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
38DataFileName = "data/distortionData.pickle" # path relative to the tests directory
41class HscDistortionTestCase(lsst.utils.tests.TestCase):
42 """Testing HscDistortion implementation
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
49 data
51 If SAVE_DATA is true then save newly created data and then fail the
52 test in order to prevent anyone from committing the test with SAVE_DATA
53 true!
55 Otherwise create new data and compare to the saved data
56 """
57 newData = self.makeDistortionData()
58 dataPath = os.path.join(os.path.dirname(__file__), DataFileName)
60 if SAVE_DATA:
61 with open(dataPath, "wb") as dataFile:
62 pickle.dump(newData, dataFile, protocol=2)
63 self.fail("Saved new data to %r; please set SAVE_DATA back to False" % dataPath)
65 if not os.path.exists(dataPath):
66 self.fail("Cannot find saved data %r; set SAVE_DATA = True and run again to save new data" %
67 dataPath)
69 fieldAngleToFocalPlaneTolerance = transformRegistry["hsc"].ConfigClass().tolerance
71 with open(dataPath, "rb") as dataFile:
72 savedData = pickle.load(dataFile)
73 maxRoundTripFocalPlaneError = 0
74 maxRoundTripPixPosError = 0
75 for detectorName, ccdData in newData.items():
76 savedCcdData = savedData[detectorName]
77 self.assertEqual(ccdData.serial, savedCcdData.serial)
78 for pixPosKey, cornerData in ccdData.cornerDict.items():
79 savedCornerData = savedCcdData.cornerDict[pixPosKey]
80 self.assertEqual(cornerData.pixPos, savedCornerData.pixPos)
81 self.assertPairsAlmostEqual(cornerData.focalPlane, savedCornerData.focalPlane)
82 self.assertPairsAlmostEqual(cornerData.fieldAngle, savedCornerData.fieldAngle)
83 maxRoundTripFocalPlaneError = max(
84 maxRoundTripFocalPlaneError,
85 math.hypot(*(cornerData.focalPlaneRoundTrip - cornerData.focalPlane))
86 )
87 self.assertPairsAlmostEqual(cornerData.focalPlaneRoundTrip, cornerData.focalPlane,
88 maxDiff=fieldAngleToFocalPlaneTolerance)
89 maxRoundTripPixPosError = max(maxRoundTripPixPosError,
90 math.hypot(*(cornerData.pixPosRoundTrip - cornerData.pixPos)))
91 self.assertPairsAlmostEqual(cornerData.pixPosRoundTrip, cornerData.pixPos)
92 print("maxRoundTripFocalPlaneError =", maxRoundTripFocalPlaneError)
93 print("maxRoundTripPixPosError =", maxRoundTripPixPosError)
95 def makeDistortionData(self):
96 """Make distortion data
98 The data format is a dict of detector name: ccdData, where
99 ccdData is a Struct containing these fields:
100 - serial: detector.getSerial
101 - cornerDict: a dict of pixPosKey, cornerData, where:
102 - pixPosKey: self.asKey(pixPos) where pixPos is pixel position
103 - cornerData is Struct contains these fields (all of
104 type lsst.geom.Point2D):
105 - pixPos: pixel position
106 - focalPlane: focal plane position computed from pixPos
107 - fieldAngle: fieldAngle position computed from focalPlane
108 - focalPlaneRoundTrip: focal plane position computed from
109 fieldAngle
110 - pixPosRoundTrip: pixel position computed from focalPlane
111 """
112 camera = HscMapper(root=".", calibRoot=".").camera
113 focalPlaneToFieldAngle = camera.getTransformMap().getTransform(FOCAL_PLANE, FIELD_ANGLE)
114 data = {} # dict of detector name: CcdData
115 for detector in camera:
116 # for each corner of each CCD:
117 # - get pixel position
118 # - convert to focal plane coordinates using the detector and
119 # record it
120 # - convert to field angle (this is the conversion that uses
121 # HscDistortion) and record it
122 # - convert back to focal plane (testing inverse direction of
123 # HscDistortion) and record it
124 # - convert back to pixel position and record it; pixel <-> focal
125 # plane is affine so there is no reason to doubt the inverse
126 # transform, but there is no harm
127 pixelsToFocalPlane = detector.getTransform(PIXELS, FOCAL_PLANE)
128 cornerDict = {}
129 for pixPos in detector.getCorners(PIXELS):
130 pixPos = pixPos
131 focalPlane = pixelsToFocalPlane.applyForward(pixPos)
132 fieldAngle = focalPlaneToFieldAngle.applyForward(focalPlane)
133 focalPlaneRoundTrip = focalPlaneToFieldAngle.applyInverse(fieldAngle)
134 pixPosRoundTrip = pixelsToFocalPlane.applyInverse(focalPlane)
135 cornerDict[self.toKey(pixPos)] = Struct(
136 pixPos = pixPos,
137 focalPlane = focalPlane,
138 fieldAngle = fieldAngle,
139 focalPlaneRoundTrip = focalPlaneRoundTrip,
140 pixPosRoundTrip = pixPosRoundTrip,
141 )
143 data[detector.getName()] = Struct(serial=detector.getSerial(), cornerDict=cornerDict)
145 return data
147 def toKey(self, pixPos):
148 return "(%0.1f, %0.1f)" % tuple(pixPos)
151class TestMemory(lsst.utils.tests.MemoryTestCase):
152 def setUp(self):
153 HscMapper.clearCache()
154 lsst.utils.tests.MemoryTestCase.setUp(self)
157def setup_module(module):
158 lsst.utils.tests.init()
161if __name__ == "__main__": 161 ↛ 162line 161 didn't jump to line 162, because the condition on line 161 was never true
162 lsst.utils.tests.init()
163 unittest.main()