Skip to content

Commit

Permalink
Added some camera geometry functionality for camera team
Browse files Browse the repository at this point in the history
In particular, mappings between amp/ccd/focal plane coordinates
using an LsstCameraTransforms object, but also getAmpImage and
channelToAmp free functions
  • Loading branch information
RobertLuptonTheGood committed Oct 4, 2019
1 parent b201a4c commit 2c510c8
Show file tree
Hide file tree
Showing 3 changed files with 72 additions and 24 deletions.
2 changes: 1 addition & 1 deletion policy/cameraHeader.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ CCD_E2V : &CCD_E2V

CCD_ITL : &CCD_ITL
detectorType : 0
refpos : [2036.5, 2000.5]
refpos : [2035.5, 1999.5]
offset : [.nan, .nan]
# This is the orientation we need to put the serial direction along the x-axis
bbox : [[0, 0], [4071, 3999]]
Expand Down
35 changes: 13 additions & 22 deletions python/lsst/obs/lsst/cameraTransforms.py
Original file line number Diff line number Diff line change
Expand Up @@ -366,24 +366,18 @@ def ampPixelToCcdPixel(x, y, detector, channel):
"""

amp = channelToAmp(detector, channel)
rawBBox, rawDataBBox = amp.getRawBBox(), amp.getRawDataBBox()
bbox = amp.getRawDataBBox()

# Allow for flips (due e.g. to physical location of the amplifiers)
w, h = rawBBox.getDimensions()
x, y = geom.PointI(x, y) # definitely ints
w, h = bbox.getDimensions()
if amp.getRawFlipX():
rawBBox.flipLR(w)
rawDataBBox.flipLR(w)

x = rawBBox.getWidth() - x - 1
x = w - x - 1

if amp.getRawFlipY():
rawBBox.flipTB(h)
rawDataBBox.flipTB(h)

y = rawBBox.getHeight() - y - 1

dxy = rawBBox.getBegin() - rawDataBBox.getBegin() # correction for overscan etc.
y = h - y - 1

return amp.getBBox().getBegin() + dxy + geom.ExtentI(x, y)
return amp.getBBox().getBegin() + geom.ExtentI(x, y)


def ccdPixelToAmpPixel(xy, detector):
Expand Down Expand Up @@ -411,31 +405,28 @@ def ccdPixelToAmpPixel(xy, detector):
RuntimeError
If the requested pixel doesn't lie on the detector
"""
xy = geom.PointI(xy) # use pixel coordinates

found = False
for amp in detector:
if geom.BoxD(amp.getBBox()).contains(xy):
if amp.getBBox().contains(xy):
x, y = xy - amp.getBBox().getBegin() # coordinates within data segment
found = True
xy = geom.PointI(xy) # pixel coordinates as ints
break

if not found:
raise RuntimeError("Point (%g, %g) does not lie on detector %s" % (xy[0], xy[1], detector.getName()))

x, y = xy - amp.getBBox().getBegin() # offset from origin of amp's data segment

# Allow for flips (due e.g. to physical location of the amplifiers)
w, h = amp.getRawDataBBox().getDimensions()
w, h = amp.getBBox().getDimensions()

if amp.getRawFlipX():
x = w - x - 1

if amp.getRawFlipY():
y = h - y - 1

dxy = amp.getRawBBox().getBegin() - amp.getRawDataBBox().getBegin() # correction for overscan etc.
xy = geom.ExtentI(x, y) - dxy

return amp, xy
return amp, geom.PointI(x, y)


def focalMmToCcdPixel(camera, focalPlaneXY):
Expand Down
59 changes: 58 additions & 1 deletion tests/test_lsstCam.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,9 @@
import unittest

import lsst.utils.tests
from lsst.geom import arcseconds, Extent2I
from lsst.geom import arcseconds, Extent2I, PointD, PointI
import lsst.afw.image
from lsst.obs.lsst.cameraTransforms import LsstCameraTransforms

from lsst.obs.lsst.testHelper import ObsLsstButlerTests, ObsLsstObsBaseOverrides

Expand Down Expand Up @@ -162,6 +163,62 @@ def testDetectorName(self):
with self.assertRaises(RuntimeError):
self.mapper._extractDetectorName({'visit': 1})

def testCameraTransforms(self):
"""Test the geometry routines requested by the camera team
These are found in cameraTransforms.py"""

camera = self.butler.get('camera', immediate=True)

raft = 'R22'
sensor = 'S11'
ccdid = f"{raft}_{sensor}"

lct = LsstCameraTransforms(camera)

# check that we can map ccd pixels to amp pixels
for cxy, apTrue in [
((0, 0), (1, 508, 0)), # noqa: E241
((509, 0), (2, 508, 0)),
]:
ap = lct.ccdPixelToAmpPixel(*cxy, ccdid)
self.assertEqual(ap, apTrue)

# check inverse mapping
for ap, cpTrue in [
((508, 0, 1), (0, 0)),
((0, 0, 9), (4071, 3999)),
]:
cp = lct.ampPixelToCcdPixel(*ap, ccdid)
self.assertEqual(cp, PointI(*cpTrue))

# check round-tripping
ampX, ampY, channel = 2, 0, 1
cx, cy = lct.ampPixelToCcdPixel(ampX, ampY, channel, ccdid)
finalChannel, finalAmpX, finalAmpY = lct.ccdPixelToAmpPixel(cx, cy, ccdid)
self.assertEqual((finalAmpX, finalAmpY, finalChannel), (ampX, ampY, channel))

# Check that four amp pixels near the camera's centre are
# indeed close in focal plane coords
for ap, fpTrue in [
((508, 1999, 5), ( 0.005, -0.005)), # noqa: E201,E241
((0, 1999, 4), (-0.005, -0.005)), # noqa: E201,E241
((0, 1999, 13), (-0.005, 0.005)), # noqa: E201,E241
((508, 1999, 12), ( 0.005, 0.005)), # noqa: E201,E241
]:
fp = lct.ampPixelToFocalMm(*ap, ccdid)
self.assertAlmostEqual((fp - PointD(*fpTrue)).computeNorm(), 0.0)

# and for ccd coordinates:
for cp, fpTrue in [
((2035, 1999), (-0.005, -0.005)), # noqa: E201,E241
((2036, 1999), ( 0.005, -0.005)), # noqa: E201,E241
((2035, 2000), (-0.005, 0.005)), # noqa: E201,E241
((2036, 2000), ( 0.005, 0.005)), # noqa: E201,E241
]:
fp = lct.ccdPixelToFocalMm(*cp, ccdid)
self.assertAlmostEqual((fp - PointD(*fpTrue)).computeNorm(), 0.0)


class MemoryTester(lsst.utils.tests.MemoryTestCase):
pass
Expand Down

0 comments on commit 2c510c8

Please sign in to comment.