LSSTApplications  18.1.0
LSSTDataManagementBasePackage
testCamera.py
Go to the documentation of this file.
1 # This file is part of obs_test.
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 #
22 __all__ = ["TestCamera"]
23 
24 import numpy as np
25 
26 import lsst.afw.cameraGeom as cameraGeom
27 import lsst.afw.geom as afwGeom
28 from lsst.afw.table import AmpInfoCatalog, AmpInfoTable, LL
29 from lsst.afw.cameraGeom import NullLinearityType
30 from lsst.afw.cameraGeom.cameraFactory import makeDetector
31 
32 
33 class TestCamera(cameraGeom.Camera):
34  """A simple test Camera.
35 
36  Notes
37  -----
38  The camera has one ccd with name "0".
39  That CCD has four amplifiers with names "00", "01", "10", and "11".
40 
41  The camera is modeled after a small portion of the LSST sim
42  Summer 2012 camera: a single detector with four amplifiers,
43  consisting of raft 2,2 sensor 0,0, half of channels 0,0 0,1 1,0 and 1,1
44  (the half closest to the Y centerline).
45 
46  Note that the Summer 2012 camera has one very weird feature:
47  the bias region (rawHOverscanBbox) is actually a prescan
48  (it appears before the data pixels).
49 
50  A raw image has the sky in the same orientation on all amplifier
51  subregions, so no amplifier subregions need their pixels to be flipped.
52 
53  Standard keys are:
54 
55  * ``amp``: amplifier number: one of 00, 01, 10, 11
56  * ``ccd``: ccd number: always 0
57  * ``visit``: exposure number; test data includes one exposure
58  with visit=1
59  """
60  def __init__(self):
61  plateScale = afwGeom.Angle(20, afwGeom.arcseconds) # plate scale, in angle on sky/mm
62  # Radial distortion is modeled as a radial polynomial that converts from focal plane (in mm)
63  # to field angle (in radians). Thus the coefficients are:
64  # C0: always 0, for continuity at the center of the focal plane; units are rad
65  # C1: 1/plateScale; units are rad/mm
66  # C2: usually 0; units are rad/mm^2
67  # C3: radial distortion; units are rad/mm^3
68  radialCoeff = np.array([0.0, 1.0, 0.0, 0.925]) / plateScale.asRadians()
69  fieldAngleToFocalPlane = afwGeom.makeRadialTransform(radialCoeff)
70  focalPlaneToFieldAngle = fieldAngleToFocalPlane.inverted()
71  cameraTransformMap = cameraGeom.TransformMap(cameraGeom.FOCAL_PLANE,
72  {cameraGeom.FIELD_ANGLE: focalPlaneToFieldAngle})
73  detectorList = self._makeDetectorList(focalPlaneToFieldAngle)
74  cameraGeom.Camera.__init__(self, "test", detectorList, cameraTransformMap)
75 
76  def _makeDetectorList(self, focalPlaneToFieldAngle):
77  """Make a list of detectors
78 
79  Parameters
80  ----------
81  focalPlaneToFieldAngle : `lsst.afw.geom.TransformPoint2ToPoint2`
82  Transform from ``FOCAL_PLANE`` to ``FIELD_ANGLE`` coordinates
83  in the forward direction.
84 
85  Returns
86  -------
87  detectorList : `list` of `lsst.afw.cameraGeom.Detector`
88  List of detectors.
89  """
90  detectorList = []
91  detectorConfigList = self._makeDetectorConfigList()
92  for detectorConfig in detectorConfigList:
93  ampInfoCatalog = self._makeAmpInfoCatalog()
94  detector = makeDetector(detectorConfig, ampInfoCatalog, focalPlaneToFieldAngle)
95  detectorList.append(detector)
96  return detectorList
97 
98  def _makeDetectorConfigList(self):
99  """Make a list of detector configs
100 
101  Returns
102  -------
103  detectorConfigList : `list` of `lsst.afw.cameraGeom.DetectorConfig`
104  List of detector configs.
105  """
106  # this camera has one detector that corresponds to a subregion of lsstSim detector R:2,2 S:0,0
107  # with lower left corner 0, 1000 and dimensions 1018 x 2000
108  # i.e. half of each of the following channels: 0,0, 0,1, 1,0 and 1,1
109  detector0Config = cameraGeom.DetectorConfig()
110  detector0Config.name = '0'
111  detector0Config.id = 0
112  detector0Config.serial = '0000011'
113  detector0Config.detectorType = 0
114  detector0Config.bbox_x0 = 0
115  detector0Config.bbox_x1 = 1017
116  detector0Config.bbox_y0 = 0
117  detector0Config.bbox_y1 = 1999
118  detector0Config.pixelSize_x = 0.01
119  detector0Config.pixelSize_y = 0.01
120  detector0Config.transformDict.nativeSys = 'Pixels'
121  detector0Config.transformDict.transforms = None
122  detector0Config.refpos_x = 2035.5
123  detector0Config.refpos_y = 999.5
124  detector0Config.offset_x = -42.26073
125  detector0Config.offset_y = -42.21914
126  detector0Config.transposeDetector = False
127  detector0Config.pitchDeg = 0.0
128  detector0Config.yawDeg = 90.0
129  detector0Config.rollDeg = 0.0
130  return [detector0Config]
131 
132  def _makeAmpInfoCatalog(self):
133  """Construct an amplifier info catalog
134 
135  Returns
136  -------
137  ampInfoCatalog : `lsst.afw.table.AmpInfoCatalog`
138  Amplifier information catalog.
139 
140  Notes
141  -----
142  The LSSTSim S12 amplifiers are unusual in that they start with 4 pixels
143  of usable bias region (which is used to set rawHOverscanBbox, despite the name),
144  followed by the data. There is no other underscan or overscan.
145  """
146  xDataExtent = 509 # trimmed
147  yDataExtent = 1000
148  xBiasExtent = 4
149  xRawExtent = xDataExtent + xBiasExtent
150  yRawExtent = yDataExtent
151  readNoise = 3.975 # amplifier read noise, in e-
152  saturationLevel = 65535
153  linearityType = NullLinearityType
154  linearityCoeffs = [0, 0, 0, 0]
155 
156  schema = AmpInfoTable.makeMinimalSchema()
157 
158  self.ampInfoDict = {}
159  ampInfoCatalog = AmpInfoCatalog(schema)
160  for ampX in (0, 1):
161  for ampY in (0, 1):
162  # amplifier gain (e-/ADU) and read noiuse (ADU/pixel) from lsstSim raw data
163  # note that obs_test amp <ampX><ampY> = lsstSim amp C<ampY>,<ampX> (axes are swapped)
164  gain = {
165  (0, 0): 1.7741, # C0,0
166  (0, 1): 1.65881, # C1,0
167  (1, 0): 1.74151, # C0,1
168  (1, 1): 1.67073, # C1,1
169  }[(ampX, ampY)]
170  readNoise = {
171  (0, 0): 3.97531706217237, # C0,0
172  (0, 1): 4.08263755342685, # C1,0
173  (1, 0): 4.02753931932633, # C0,1
174  (1, 1): 4.1890610691135, # C1,1
175  }[(ampX, ampY)]
176  record = ampInfoCatalog.addNew()
177  record.setName("%d%d" % (ampX, ampY))
178  record.setBBox(afwGeom.Box2I(
179  afwGeom.Point2I(ampX * xDataExtent, ampY * yDataExtent),
180  afwGeom.Extent2I(xDataExtent, yDataExtent),
181  ))
182 
183  x0Raw = ampX * xRawExtent
184  y0Raw = ampY * yRawExtent
185 
186  # bias region (which is prescan, in this case) is before the data
187  readCorner = LL
188  x0Bias = x0Raw
189  x0Data = x0Bias + xBiasExtent
190 
191  record.setRawBBox(afwGeom.Box2I(
192  afwGeom.Point2I(x0Raw, y0Raw),
193  afwGeom.Extent2I(xRawExtent, yRawExtent),
194  ))
195  record.setRawDataBBox(afwGeom.Box2I(
196  afwGeom.Point2I(x0Data, y0Raw),
197  afwGeom.Extent2I(xDataExtent, yDataExtent),
198  ))
199  record.setRawHorizontalOverscanBBox(afwGeom.Box2I(
200  afwGeom.Point2I(x0Bias, y0Raw),
201  afwGeom.Extent2I(xBiasExtent, yRawExtent),
202  ))
203  record.setRawXYOffset(afwGeom.Extent2I(x0Raw, y0Raw))
204  record.setReadoutCorner(readCorner)
205  record.setGain(gain)
206  record.setReadNoise(readNoise)
207  record.setSaturation(saturationLevel)
208  record.setSuspectLevel(float("nan"))
209  record.setLinearityCoeffs([float(val) for val in linearityCoeffs])
210  record.setLinearityType(linearityType)
211  record.setHasRawInfo(True)
212  record.setRawFlipX(False)
213  record.setRawFlipY(False)
214  record.setRawVerticalOverscanBBox(afwGeom.Box2I()) # no vertical overscan
215  record.setRawPrescanBBox(afwGeom.Box2I()) # no horizontal prescan
216  return ampInfoCatalog
A class representing an angle.
Definition: Angle.h:127
std::shared_ptr< TransformPoint2ToPoint2 > makeRadialTransform(std::vector< double > const &forwardCoeffs, std::vector< double > const &inverseCoeffs)
A purely radial polynomial distortion.
CatalogT< AmpInfoRecord > AmpInfoCatalog
Definition: fwd.h:97
def _makeDetectorList(self, focalPlaneToFieldAngle)
Definition: testCamera.py:76
def makeDetector(detectorConfig, ampInfoCatalog, focalPlaneToField)
An integer coordinate rectangle.
Definition: Box.h:54