LSST Applications g013ef56533+2edba5e46d,g083dd6704c+a047e97985,g199a45376c+0ba108daf9,g1fd858c14a+d594c53611,g210f2d0738+d003cec0be,g25ecf2a47a+204e5a9ad5,g262e1987ae+16552835ac,g29ae962dfc+867dbde878,g2cef7863aa+aef1011c0b,g30d7c61c20+990af31dd2,g35bb328faa+8c5ae1fdc5,g3fd5ace14f+c8e8c50746,g47891489e3+f459a6810c,g53246c7159+8c5ae1fdc5,g54cd7ddccb+890c8e1e5d,g5b326b94bb+01d4acd216,g64539dfbff+d003cec0be,g67b6fd64d1+f459a6810c,g6ebf1fc0d4+8c5ae1fdc5,g74acd417e5+4b21cac47b,g786e29fd12+668abc6043,g87389fa792+8856018cbb,g89139ef638+f459a6810c,g8d7436a09f+b19788b7f5,g8ea07a8fe4+81eaaadc04,g90f42f885a+34c0557caf,g97be763408+3bd86eedd0,g98a1a72a9c+67cf96cfb3,g98df359435+688a06938e,gbf99507273+8c5ae1fdc5,gc2a301910b+d003cec0be,gca7fc764a6+f459a6810c,gd7ef33dd92+f459a6810c,gdab6d2f7ff+4b21cac47b,ge410e46f29+f459a6810c,ge41e95a9f2+d003cec0be,geaed405ab2+e3b4b2a692,gf9a733ac38+8c5ae1fdc5,w.2025.43
LSST Data Management Base Package
Loading...
Searching...
No Matches
testUtils.py
Go to the documentation of this file.
1# This file is part of afw.
2#
3# Developed for the LSST Data Management System.
4# This product includes software developed by the LSST Project
5# (https://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 <https://www.gnu.org/licenses/>.
21
22__all__ = ["DetectorWrapper", "CameraWrapper"]
23
24import os
25
26import numpy as np
27
28import lsst.utils
29import lsst.geom
30import lsst.afw.geom as afwGeom
31from lsst.utils.tests import inTestCase
32from ._cameraGeom import CameraSys, PIXELS, TAN_PIXELS, FIELD_ANGLE, FOCAL_PLANE, ACTUAL_PIXELS, Orientation
33from ._cameraGeom import Amplifier, ReadoutCorner
34from ._camera import Camera
35from ._cameraGeom import DetectorType
36from .cameraConfig import DetectorConfig, CameraConfig
37from ._cameraFactory import makeCameraFromAmpLists
38from ._makePixelToTanPixel import makePixelToTanPixel
39from ._transformConfig import TransformMapConfig
40
41
43 """A Detector and the data used to construct it
44
45 Intended for use with unit tests, thus saves a copy of all input parameters.
46 Does not support setting details of amplifiers.
47
48 Parameters
49 ----------
50 name : `str` (optional)
51 Detector name.
52 id : `int` (optional)
53 Detector ID.
54 detType : `lsst.afw.cameraGeom.DetectorType` (optional)
55 Detector type.
56 serial : `str` (optional)
57 Serial "number".
58 bbox : `lsst.geom.Box2I` (optional)
59 Bounding box; defaults to (0, 0), (1024x1024).
60 numAmps : `int` (optional)
61 Number of amplifiers.
62 pixelSize : `lsst.geom.Point2D` (optional)
63 Pixel size (mm).
64 ampExtent : `lsst.geom.Extent2I` (optional)
65 Dimensions of amplifier image bbox.
66 orientation : `lsst.afw.cameraGeom.Orientation` (optional)
67 Orientation of CCC in focal plane.
68 plateScale : `float` (optional)
69 Plate scale in arcsec/mm; 20.0 is for LSST.
70 radialDistortion : `float` (optional)
71 Radial distortion, in mm/rad^2.
72 The r^3 coefficient of the radial distortion polynomial
73 that converts FIELD_ANGLE in radians to FOCAL_PLANE in mm;
74 0.925 is the value Dave Monet measured for lsstSim data
75 crosstalk : `iterable` (optional)
76 Crosstalk coefficient matrix. If None, then no crosstalk correction
77 can be performed.
78 modFunc : `callable` (optional)
79 A function that can modify attributes just before constructing the
80 detector; modFunc receives one argument: a DetectorWrapper with all
81 attributes except detector set.
82 physicalType : `str` (optional)
83 The physical type of the device, e.g. CCD, E2V, HgCdTe
84 """
85
86 def __init__(self,
87 name="detector 1",
88 id=1,
89 detType=DetectorType.SCIENCE,
90 serial="xkcd722",
91 bbox=None, # do not use mutable objects as defaults
92 numAmps=3,
93 pixelSize=(0.02, 0.02),
94 ampExtent=(5, 6),
95 orientation=Orientation(),
96 plateScale=20.0,
97 radialDistortion=0.925,
98 crosstalk=None,
99 modFunc=None,
100 physicalType="CCD",
101 cameraBuilder=None
102 ):
103 # note that (0., 0.) for the reference position is the center of the
104 # first pixel
105 self.name = name
106 self.id = int(id)
107 self.type = detType
108 self.serial = serial
109 if bbox is None:
110 bbox = lsst.geom.Box2I(lsst.geom.Point2I(0, 0), lsst.geom.Extent2I(1024, 1048))
111 self.bbox = bbox
112 self.pixelSize = lsst.geom.Extent2D(*pixelSize)
113 self.ampExtent = lsst.geom.Extent2I(*ampExtent)
114 self.plateScale = float(plateScale)
115 self.orientation = orientation
116 self.radialDistortion = float(radialDistortion)
117
118 # compute TAN_PIXELS transform
119 pScaleRad = lsst.geom.arcsecToRad(self.plateScale)
120 radialDistortCoeffs = [0.0, 1.0/pScaleRad,
121 0.0, self.radialDistortion/pScaleRad]
122 focalPlaneToField = afwGeom.makeRadialTransform(radialDistortCoeffs)
123 pixelToTanPixel = makePixelToTanPixel(
124 bbox=self.bbox,
125 orientation=self.orientation,
126 focalPlaneToField=focalPlaneToField,
127 pixelSizeMm=self.pixelSize,
128 )
129 tanPixelSys = CameraSys(TAN_PIXELS, self.name)
130 actualPixelSys = CameraSys(ACTUAL_PIXELS, self.name)
131 self.transMap = {
132 FOCAL_PLANE: self.orientation.makePixelFpTransform(self.pixelSize),
133 tanPixelSys: pixelToTanPixel,
134 actualPixelSys: afwGeom.makeRadialTransform([0, 0.95, 0.01]),
135 }
136 if crosstalk is None:
137 crosstalk = [[0.0 for _ in range(numAmps)] for _ in range(numAmps)]
138 self.crosstalk = crosstalk
139 self.physicalType = physicalType
140 if cameraBuilder is None:
141 cameraBuilder = Camera.Builder("CameraForDetectorWrapper")
142 self.ampList = []
143 for i in range(numAmps):
144 ampBuilder = Amplifier.Builder()
145 ampName = f"amp {i + 1}"
146 ampBuilder.setName(ampName)
147 ampBuilder.setBBox(lsst.geom.Box2I(lsst.geom.Point2I(-1, 1), self.ampExtent))
148 ampBuilder.setGain(1.71234e3)
149 ampBuilder.setReadNoise(0.521237e2)
150 ampBuilder.setReadoutCorner(ReadoutCorner.LL)
151 self.ampList.append(ampBuilder)
152 if modFunc:
153 modFunc(self)
154 detectorBuilder = cameraBuilder.add(self.name, self.id)
155 detectorBuilder.setType(self.type)
156 detectorBuilder.setSerial(self.serial)
157 detectorBuilder.setPhysicalType(self.physicalType)
158 detectorBuilder.setBBox(self.bbox)
159 detectorBuilder.setOrientation(self.orientation)
160 detectorBuilder.setPixelSize(self.pixelSize)
161 detectorBuilder.setTransformFromPixelsTo(tanPixelSys, self.transMap[tanPixelSys])
162 detectorBuilder.setTransformFromPixelsTo(actualPixelSys, self.transMap[actualPixelSys])
163 detectorBuilder.setCrosstalk(np.array(self.crosstalk, dtype=np.float32))
164 for ampBuilder in self.ampList:
165 detectorBuilder.append(ampBuilder)
166 camera = cameraBuilder.finish()
167 self.detector = camera[self.name]
168
169
171 """A simple Camera and the data used to construct it
172
173 Intended for use with unit tests, thus saves some interesting information.
174
175 Parameters
176 ----------
177 plateScale : `float`
178 Plate scale in arcsec/mm; 20.0 is for LSST.
179 radialDistortion : `float`
180 Radial distortion, in mm/rad^2.
181 The r^3 coefficient of the radial distortion polynomial
182 that converts FIELD_ANGLE in radians to FOCAL_PLANE in mm;
183 0.925 is the value Dave Monet measured for lsstSim data.
184 isLsstLike : `bool`.
185 Make repository products with one raw image per amplifier (True)
186 or with one raw image per detector (False).
187 focalPlaneParity : `bool`
188 If `True`, the X axis is flipped between the FOCAL_PLANE and
189 FIELD_ANGLE coordinate systems.
190 """
191
192 def __init__(self, plateScale=20.0, radialDistortion=0.925, isLsstLike=False, focalPlaneParity=False):
193 afwDir = lsst.utils.getPackageDir("afw")
194 self._afwTestDataDir = os.path.join(afwDir, "python", "lsst", "afw",
195 "cameraGeom", "testData")
196
197 # Info to store for unit tests
198 self.plateScale = float(plateScale)
199 self.radialDistortion = float(radialDistortion)
202 self.ampDataDict = {} # ampData[Dict]: raw dictionaries of test data fields
203
204 # ampList[Dict]: actual cameraGeom.Amplifier objects
206 isLsstLike, focalPlaneParity=focalPlaneParity)
208 self.camConfig, self.ampListDict)
209
210 @property
211 def nDetectors(self):
212 """Return the number of detectors"""
213 return len(self.detectorNameList)
214
215 def makeDetectorConfigs(self, detFile):
216 """Construct a list of DetectorConfig, one per detector
217 """
218 detectors = []
219 self.detectorNameList = []
220 self.detectorIdList = []
221 with open(detFile) as fh:
222 names = fh.readline().rstrip().lstrip("#").split("|")
223 for line in fh:
224 els = line.rstrip().split("|")
225 detectorProps = dict([(name, el)
226 for name, el in zip(names, els)])
227 detectors.append(detectorProps)
228 detectorConfigs = []
229 for i, detector in enumerate(detectors):
230 detectorId = (i + 1) * 10 # to avoid simple 0, 1, 2...
231 detectorName = detector['name']
232 detConfig = DetectorConfig()
233 detConfig.name = detectorName
234 detConfig.id = detectorId
235 detConfig.bbox_x0 = 0
236 detConfig.bbox_y0 = 0
237 detConfig.bbox_x1 = int(detector['npix_x']) - 1
238 detConfig.bbox_y1 = int(detector['npix_y']) - 1
239 detConfig.serial = str(detector['serial'])
240 detConfig.detectorType = int(detector['detectorType'])
241 detConfig.offset_x = float(detector['x'])
242 detConfig.offset_y = float(detector['y'])
243 detConfig.offset_z = float(detector['z'])
244 detConfig.refpos_x = float(detector['refPixPos_x'])
245 detConfig.refpos_y = float(detector['refPixPos_y'])
246 detConfig.yawDeg = float(detector['yaw'])
247 detConfig.pitchDeg = float(detector['pitch'])
248 detConfig.rollDeg = float(detector['roll'])
249 detConfig.pixelSize_x = float(detector['pixelSize'])
250 detConfig.pixelSize_y = float(detector['pixelSize'])
251 detConfig.transposeDetector = False
252 detConfig.transformDict.nativeSys = PIXELS.getSysName()
253 detectorConfigs.append(detConfig)
254 self.detectorNameList.append(detectorName)
255 self.detectorIdList.append(detectorId)
256 return detectorConfigs
257
258 def makeAmpLists(self, ampFile, isLsstLike=False):
259 """Construct a dict of list of Amplifer, one list per detector.
260
261 Parameters
262 ----------
263 ampFile : `str`
264 Path to amplifier data file.
265 isLsstLike : `bool`
266 If True then there is one raw image per amplifier;
267 if False then there is one raw image per detector.
268 """
269 readoutMap = {
270 'LL': ReadoutCorner.LL,
271 'LR': ReadoutCorner.LR,
272 'UR': ReadoutCorner.UR,
273 'UL': ReadoutCorner.UL,
274 }
275 ampDataList = []
276 with open(ampFile) as fh:
277 names = fh.readline().rstrip().lstrip("#").split("|")
278 for line in fh:
279 els = line.rstrip().split("|")
280 ampProps = dict([(name, el) for name, el in zip(names, els)])
281 ampDataList.append(ampProps)
282 ampListDict = {}
283 self.ampDataDict = {}
284 for ampData in ampDataList:
285 if ampData['ccd_name'] in ampListDict:
286 ampList = ampListDict[ampData['ccd_name']]
287 self.ampDataDict[ampData['ccd_name']]['namps'] += 1
288 else:
289 ampList = []
290 ampListDict[ampData['ccd_name']] = ampList
291 self.ampDataDict[ampData['ccd_name']] = {'namps': 1, 'linInfo': {}}
292 builder = Amplifier.Builder()
293 bbox = lsst.geom.Box2I(lsst.geom.Point2I(int(ampData['trimmed_xmin']),
294 int(ampData['trimmed_ymin'])),
295 lsst.geom.Point2I(int(ampData['trimmed_xmax']),
296 int(ampData['trimmed_ymax'])))
297 rawBbox = lsst.geom.Box2I(lsst.geom.Point2I(int(ampData['raw_xmin']),
298 int(ampData['raw_ymin'])),
299 lsst.geom.Point2I(int(ampData['raw_xmax']),
300 int(ampData['raw_ymax'])))
301 rawDataBbox = lsst.geom.Box2I(
302 lsst.geom.Point2I(int(ampData['raw_data_xmin']),
303 int(ampData['raw_data_ymin'])),
304 lsst.geom.Point2I(int(ampData['raw_data_xmax']),
305 int(ampData['raw_data_ymax'])))
306 rawHOverscanBbox = lsst.geom.Box2I(
307 lsst.geom.Point2I(int(ampData['hoscan_xmin']),
308 int(ampData['hoscan_ymin'])),
309 lsst.geom.Point2I(int(ampData['hoscan_xmax']),
310 int(ampData['hoscan_ymax'])))
311 rawVOverscanBbox = lsst.geom.Box2I(
312 lsst.geom.Point2I(int(ampData['voscan_xmin']),
313 int(ampData['voscan_ymin'])),
314 lsst.geom.Point2I(int(ampData['voscan_xmax']),
315 int(ampData['voscan_ymax'])))
316 rawPrescanBbox = lsst.geom.Box2I(
317 lsst.geom.Point2I(int(ampData['pscan_xmin']),
318 int(ampData['pscan_ymin'])),
319 lsst.geom.Point2I(int(ampData['pscan_xmax']),
320 int(ampData['pscan_ymax'])))
321 xoffset = int(ampData['x_offset'])
322 yoffset = int(ampData['y_offset'])
323 flipx = bool(int(ampData['flipx']))
324 flipy = bool(int(ampData['flipy']))
325 readcorner = 'LL'
326 if not isLsstLike:
327 offext = lsst.geom.Extent2I(xoffset, yoffset)
328 if flipx:
329 xExt = rawBbox.getDimensions().getX()
330 rawBbox.flipLR(xExt)
331 rawDataBbox.flipLR(xExt)
332 rawHOverscanBbox.flipLR(xExt)
333 rawVOverscanBbox.flipLR(xExt)
334 rawPrescanBbox.flipLR(xExt)
335 if flipy:
336 yExt = rawBbox.getDimensions().getY()
337 rawBbox.flipTB(yExt)
338 rawDataBbox.flipTB(yExt)
339 rawHOverscanBbox.flipTB(yExt)
340 rawVOverscanBbox.flipTB(yExt)
341 rawPrescanBbox.flipTB(yExt)
342 if not flipx and not flipy:
343 readcorner = 'LL'
344 elif flipx and not flipy:
345 readcorner = 'LR'
346 elif flipx and flipy:
347 readcorner = 'UR'
348 elif not flipx and flipy:
349 readcorner = 'UL'
350 else:
351 raise RuntimeError("Couldn't find read corner")
352
353 flipx = False
354 flipy = False
355 rawBbox.shift(offext)
356 rawDataBbox.shift(offext)
357 rawHOverscanBbox.shift(offext)
358 rawVOverscanBbox.shift(offext)
359 rawPrescanBbox.shift(offext)
360 xoffset = 0
361 yoffset = 0
362 offset = lsst.geom.Extent2I(xoffset, yoffset)
363 builder.setBBox(bbox)
364 builder.setRawXYOffset(offset)
365 builder.setName(str(ampData['name']))
366 builder.setReadoutCorner(readoutMap[readcorner])
367 builder.setGain(float(ampData['gain']))
368 builder.setReadNoise(float(ampData['readnoise']))
369 linCoeffs = np.array([float(ampData['lin_coeffs']), ], dtype=float)
370 builder.setLinearityCoeffs(linCoeffs)
371 builder.setLinearityType(str(ampData['lin_type']))
372 builder.setRawFlipX(flipx)
373 builder.setRawFlipY(flipy)
374 builder.setRawBBox(rawBbox)
375 builder.setRawDataBBox(rawDataBbox)
376 builder.setRawHorizontalOverscanBBox(rawHOverscanBbox)
377 builder.setRawVerticalOverscanBBox(rawVOverscanBbox)
378 builder.setRawPrescanBBox(rawPrescanBbox)
379 builder.setLinearityThreshold(float(ampData['lin_thresh']))
380 builder.setLinearityMaximum(float(ampData['lin_max']))
381 builder.setLinearityUnits(str(ampData['lin_units']))
382 self.ampDataDict[ampData['ccd_name']]['linInfo'][ampData['name']] = \
383 {'lincoeffs': linCoeffs, 'lintype': str(ampData['lin_type']),
384 'linthresh': float(ampData['lin_thresh']), 'linmax': float(ampData['lin_max']),
385 'linunits': str(ampData['lin_units'])}
386 ampList.append(builder)
387 return ampListDict
388
389 def makeTestRepositoryItems(self, isLsstLike=False, focalPlaneParity=False):
390 """Make camera config and amp catalog dictionary, using default
391 detector and amp files.
392
393 Parameters
394 ----------
395 isLsstLike : `bool`
396 If True then there is one raw image per amplifier;
397 if False then there is one raw image per detector.
398 focalPlaneParity : `bool`
399 If `True`, the X axis is flipped between the FOCAL_PLANE and
400 FIELD_ANGLE coordinate systems.
401 """
402 detFile = os.path.join(self._afwTestDataDir, "testCameraDetectors.dat")
403 detectorConfigs = self.makeDetectorConfigs(detFile)
404 ampFile = os.path.join(self._afwTestDataDir, "testCameraAmps.dat")
405 ampListDict = self.makeAmpLists(ampFile, isLsstLike=isLsstLike)
406 camConfig = CameraConfig()
407 camConfig.name = "testCamera%s"%('LSST' if isLsstLike else 'SC')
408 camConfig.detectorList = dict((i, detConfig)
409 for i, detConfig in enumerate(detectorConfigs))
410 camConfig.plateScale = self.plateScale
411 camConfig.focalPlaneParity = focalPlaneParity
412 pScaleRad = lsst.geom.arcsecToRad(self.plateScale)
413 radialDistortCoeffs = [0.0, 1.0/pScaleRad,
414 0.0, self.radialDistortion/pScaleRad]
415 tConfig = afwGeom.TransformConfig()
416 tConfig.transform.name = 'inverted'
417 radialClass = afwGeom.transformRegistry['radial']
418 tConfig.transform.active.transform.retarget(radialClass)
419 tConfig.transform.active.transform.coeffs = radialDistortCoeffs
420 tmc = TransformMapConfig()
421 tmc.nativeSys = FOCAL_PLANE.getSysName()
422 tmc.transforms = {FIELD_ANGLE.getSysName(): tConfig}
423 camConfig.transformDict = tmc
424 return camConfig, ampListDict
425
426
427@inTestCase
428def compare2DFunctions(self, func1, func2, minVal=-10, maxVal=None, nVal=5):
429 """Compare two Point2D(list(Point2D)) functions by evaluating them over a
430 range of values.
431
432 Notes
433 -----
434 Assumes the functions can be called with ``list[Point2D]`` and return
435 ``list[Point2D]``.
436 """
437 if maxVal is None:
438 maxVal = -minVal
439 dVal = (maxVal - minVal) / (nVal - 1)
440 points = []
441 for xInd in range(nVal):
442 x = minVal + (xInd * dVal)
443 for yInd in range(nVal):
444 y = minVal + (yInd * dVal)
445 fromPoint = lsst.geom.Point2D(x, y)
446 points.append(fromPoint)
447
448 vres1 = func1(points)
449 vres2 = func2(points)
450 for res1, res2 in zip(vres1, vres2):
451 self.assertPairsAlmostEqual(res1, res2)
452
453
454@inTestCase
455def assertTransformMapsEqual(self, map1, map2, **kwds):
456 """Compare two TransformMaps.
457 """
458 self.assertEqual(list(map1), list(map2)) # compares the sets of CameraSys
459 for sysFrom in map1:
460 for sysTo in map1:
461 with self.subTest(sysFrom=sysFrom, sysTo=sysTo):
462 transform1 = map1.getTransform(sysFrom, sysTo)
463 transform2 = map2.getTransform(sysFrom, sysTo)
464 self.compare2DFunctions(transform1.applyForward, transform2.applyForward, **kwds)
465 self.compare2DFunctions(transform1.applyInverse, transform2.applyInverse, **kwds)
466
467
468@inTestCase
469def assertAmplifiersEqual(self, amp1, amp2):
470 self.assertEqual(amp1.getName(), amp2.getName())
471 self.assertEqual(amp1.getBBox(), amp2.getBBox())
472 self.assertFloatsEqual(amp1.getGain(), amp2.getGain(), ignoreNaNs=True)
473 self.assertFloatsEqual(amp1.getReadNoise(), amp2.getReadNoise(), ignoreNaNs=True)
474 self.assertFloatsEqual(amp1.getSaturation(), amp2.getSaturation(), ignoreNaNs=True)
475 self.assertEqual(amp1.getReadoutCorner(), amp2.getReadoutCorner())
476 self.assertFloatsEqual(amp1.getSuspectLevel(), amp2.getSuspectLevel(), ignoreNaNs=True)
477 self.assertEqual(amp1.getLinearityCoeffs().shape, amp2.getLinearityCoeffs().shape)
478 self.assertFloatsEqual(amp1.getLinearityCoeffs(), amp2.getLinearityCoeffs(), ignoreNaNs=True)
479 self.assertEqual(amp1.getLinearityType(), amp2.getLinearityType())
480 self.assertFloatsEqual(amp1.getLinearityThreshold(), amp2.getLinearityThreshold(), ignoreNaNs=True)
481 self.assertFloatsEqual(amp1.getLinearityMaximum(), amp2.getLinearityMaximum(), ignoreNaNs=True)
482 self.assertEqual(amp1.getLinearityUnits(), amp2.getLinearityUnits())
483 self.assertEqual(amp1.getRawBBox(), amp2.getRawBBox())
484 self.assertEqual(amp1.getRawDataBBox(), amp2.getRawDataBBox())
485 self.assertEqual(amp1.getRawFlipX(), amp2.getRawFlipX())
486 self.assertEqual(amp1.getRawFlipY(), amp2.getRawFlipY())
487 self.assertEqual(amp1.getRawHorizontalOverscanBBox(), amp2.getRawHorizontalOverscanBBox())
488 self.assertEqual(amp1.getRawVerticalOverscanBBox(), amp2.getRawVerticalOverscanBBox())
489 self.assertEqual(amp1.getRawPrescanBBox(), amp2.getRawPrescanBBox())
490
491
492@inTestCase
493def assertDetectorsEqual(self, detector1, detector2, *, compareTransforms=True, **kwds):
494 """Compare two Detectors.
495 """
496 self.assertEqual(detector1.getName(), detector2.getName())
497 self.assertEqual(detector1.getId(), detector2.getId())
498 self.assertEqual(detector1.getSerial(), detector2.getSerial())
499 self.assertEqual(detector1.getPhysicalType(), detector2.getPhysicalType())
500 self.assertEqual(detector1.getBBox(), detector2.getBBox())
501 self.assertEqual(detector1.getPixelSize(), detector2.getPixelSize())
502 orientationIn = detector1.getOrientation()
503 orientationOut = detector2.getOrientation()
504 self.assertEqual(orientationIn.getFpPosition(), orientationOut.getFpPosition())
505 self.assertEqual(orientationIn.getReferencePoint(), orientationOut.getReferencePoint())
506 self.assertEqual(orientationIn.getYaw(), orientationOut.getYaw())
507 self.assertEqual(orientationIn.getPitch(), orientationOut.getPitch())
508 self.assertEqual(orientationIn.getRoll(), orientationOut.getRoll())
509 self.assertFloatsEqual(detector1.getCrosstalk(), detector2.getCrosstalk())
510 if compareTransforms:
511 self.assertTransformMapsEqual(detector1.getTransformMap(), detector2.getTransformMap(), **kwds)
512 self.assertEqual(len(detector1.getAmplifiers()), len(detector2.getAmplifiers()))
513 for amp1, amp2 in zip(detector1.getAmplifiers(), detector2.getAmplifiers()):
514 self.assertAmplifiersEqual(amp1, amp2)
515
516
517@inTestCase
518def assertDetectorCollectionsEqual(self, collection1, collection2, **kwds):
519 """Compare two DetectorCollections.
520 """
521 self.assertCountEqual(list(collection1.getNameIter()), list(collection2.getNameIter()))
522 for k in collection1.getNameIter():
523 self.assertDetectorsEqual(collection1[k], collection2[k], **kwds)
524
525
526@inTestCase
527def assertCamerasEqual(self, camera1, camera2, **kwds):
528 """Compare two Cameras.
529 """
530 self.assertDetectorCollectionsEqual(camera1, camera2, **kwds)
531 self.assertTransformMapsEqual(camera1.getTransformMap(), camera2.getTransformMap())
532 self.assertEqual(camera1.getName(), camera2.getName())
533 self.assertEqual(camera1.getPupilFactoryName(), camera2.getPupilFactoryName())
534 self.assertEqual(camera1.getFocalPlaneParity(), camera2.getFocalPlaneParity())
A mutable Amplifier subclass class that can be used to incrementally construct or modify Amplifiers.
Definition Amplifier.h:305
A helper class for creating and modifying cameras.
Definition Camera.h:210
Camera coordinate system; used as a key in in TransformMap.
Definition CameraSys.h:83
Describe a detector's orientation in the focal plane.
Definition Orientation.h:51
__init__(self, plateScale=20.0, radialDistortion=0.925, isLsstLike=False, focalPlaneParity=False)
Definition testUtils.py:192
makeTestRepositoryItems(self, isLsstLike=False, focalPlaneParity=False)
Definition testUtils.py:389
makeAmpLists(self, ampFile, isLsstLike=False)
Definition testUtils.py:258
__init__(self, name="detector 1", id=1, detType=DetectorType.SCIENCE, serial="xkcd722", bbox=None, numAmps=3, pixelSize=(0.02, 0.02), ampExtent=(5, 6), orientation=Orientation(), plateScale=20.0, radialDistortion=0.925, crosstalk=None, modFunc=None, physicalType="CCD", cameraBuilder=None)
Definition testUtils.py:102
An integer coordinate rectangle.
Definition Box.h:55
makeCameraFromAmpLists(cameraConfig, ampListDict, pupilFactoryClass=PupilFactory)
makePixelToTanPixel(bbox, orientation, focalPlaneToField, pixelSizeMm)
assertTransformMapsEqual(self, map1, map2, **kwds)
Definition testUtils.py:455
compare2DFunctions(self, func1, func2, minVal=-10, maxVal=None, nVal=5)
Definition testUtils.py:428
assertDetectorCollectionsEqual(self, collection1, collection2, **kwds)
Definition testUtils.py:518
assertCamerasEqual(self, camera1, camera2, **kwds)
Definition testUtils.py:527
assertDetectorsEqual(self, detector1, detector2, *, compareTransforms=True, **kwds)
Definition testUtils.py:493
assertAmplifiersEqual(self, amp1, amp2)
Definition testUtils.py:469
std::shared_ptr< TransformPoint2ToPoint2 > makeRadialTransform(std::vector< double > const &coeffs)
A purely radial polynomial distortion.
constexpr double arcsecToRad(double x) noexcept
Definition Angle.h:56