lsst.obs.base  15.0-12-g3681e7a+12
yamlCamera.py
Go to the documentation of this file.
1 #
2 # LSST Data Management System
3 # Copyright 2017 LSST Corporation.
4 #
5 # This product includes software developed by the
6 # LSST Project (http://www.lsst.org/).
7 #
8 # This program is free software: you can redistribute it and/or modify
9 # it under the terms of the GNU General Public License as published by
10 # the Free Software Foundation, either version 3 of the License, or
11 # (at your option) any later version.
12 #
13 # This program is distributed in the hope that it will be useful,
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 # GNU General Public License for more details.
17 #
18 # You should have received a copy of the LSST License Statement and
19 # the GNU General Public License along with this program. If not,
20 # see <http://www.lsstcorp.org/LegalNotices/>.
21 #
22 from __future__ import division, print_function
23 from builtins import zip
24 import yaml
25 
26 import numpy as np
27 import lsst.afw.cameraGeom as cameraGeom
28 import lsst.afw.geom as afwGeom
29 from lsst.afw.table import AmpInfoCatalog, AmpInfoTable
30 from lsst.afw.cameraGeom.cameraFactory import makeDetector
31 
32 
33 class YamlCamera(cameraGeom.Camera):
34  """The Commissioning Camera (comCam)
35 
36  Parameters
37  ----------
38  cameraYamlFile : `str`
39  Camera description YAML file.
40  """
41 
42  def __init__(self, cameraYamlFile):
43  with open(cameraYamlFile) as fd:
44  cameraParams = yaml.load(fd, Loader=yaml.Loader)
45 
46  plateScale = afwGeom.Angle(cameraParams["plateScale"], afwGeom.arcseconds)
47  # radial coefficients of the form [0, no units, 1/rad but usually 0, 1/rad^2, ...]
48  # Radial distortion is modeled as a radial polynomial that converts from focal plane radius (in mm)
49  # to field angle (in radians). The coefficients are divided by the plate scale (in mm/radians)
50  # meaning that C1 is always 1.
51  radialCoeffs = np.array(cameraParams["radialCoeffs"])/plateScale.asRadians()
52  fieldAngleToFocalPlane = afwGeom.makeRadialTransform(radialCoeffs)
53  focalPlaneToFieldAngle = fieldAngleToFocalPlane.getInverse()
54  cameraTransformMap = cameraGeom.TransformMap(cameraGeom.FOCAL_PLANE,
55  {cameraGeom.FIELD_ANGLE: focalPlaneToFieldAngle})
56  detectorList = self._makeDetectorList(cameraParams["CCDs"], focalPlaneToFieldAngle)
57  cameraGeom.Camera.__init__(self, cameraParams["name"], detectorList, cameraTransformMap)
58 
59  def _makeDetectorList(self, ccdParams, focalPlaneToFieldAngle):
60  """Make a list of detectors
61 
62  Parameters
63  ----------
64  ccdParams : `dict`
65  Dict of YAML descriptions of CCDs.
66  focalPlaneToFieldAngle : `lsst.afw.geom.TransformPoint2ToPoint2`
67  Angle from FOCAL_PLANE to FIELD_ANGLE coordinates.
68 
69  Returns
70  -------
71  `list` of `lsst.afw.cameraGeom.Detector`
72  list of detectors in this camera.
73  """
74  detectorList = []
75  detectorConfigList = self._makeDetectorConfigList(ccdParams)
76  for ccd, detectorConfig in zip(ccdParams.values(), detectorConfigList):
77  ampInfoCatalog = self._makeAmpInfoCatalog(ccd)
78  detector = makeDetector(detectorConfig, ampInfoCatalog, focalPlaneToFieldAngle)
79  detectorList.append(detector)
80  return detectorList
81 
82  def _makeDetectorConfigList(self, ccdParams):
83  """Make a list of detector configs
84 
85  Returns
86  -------
87  `list` of `lsst.afw.cameraGeom.DetectorConfig`
88  A list of detector configs.
89  """
90  detectorConfigs = []
91  for name, ccd in ccdParams.items():
92  detectorConfig = cameraGeom.DetectorConfig()
93  detectorConfigs.append(detectorConfig)
94 
95  detectorConfig.name = name
96  detectorConfig.id = ccd['id']
97  detectorConfig.serial = ccd['serial']
98  detectorConfig.detectorType = ccd['detectorType']
99  # This is the orientation we need to put the serial direction along the x-axis
100  detectorConfig.bbox_x0, detectorConfig.bbox_y0 = ccd['bbox'][0]
101  detectorConfig.bbox_x1, detectorConfig.bbox_y1 = ccd['bbox'][1]
102  detectorConfig.pixelSize_x, detectorConfig.pixelSize_y = ccd['pixelSize']
103  detectorConfig.transformDict.nativeSys = ccd['transformDict']['nativeSys']
104  transforms = ccd['transformDict']['transforms']
105  detectorConfig.transformDict.transforms = None if transforms == 'None' else transforms
106  detectorConfig.refpos_x, detectorConfig.refpos_y = ccd['refpos']
107  detectorConfig.offset_x, detectorConfig.offset_y = ccd['offset']
108  detectorConfig.transposeDetector = ccd['transposeDetector']
109  detectorConfig.pitchDeg = ccd['pitch']
110  detectorConfig.yawDeg = ccd['yaw']
111  detectorConfig.rollDeg = ccd['roll']
112  if 'crosstalk' in ccd:
113  detectorConfig.crosstalk = ccd['crosstalk']
114 
115  return detectorConfigs
116 
117  @staticmethod
118  def _makeBBoxFromList(ylist):
119  """Given a list [(x0, y0), (xsize, ysize)], probably from a yaml file,
120  return a BoxI
121  """
122  (x0, y0), (xsize, ysize) = ylist
123  return afwGeom.BoxI(afwGeom.PointI(x0, y0), afwGeom.ExtentI(xsize, ysize))
124 
125  def _makeAmpInfoCatalog(self, ccd):
126  """Construct an amplifier info catalog
127  """
128  # Much of this will need to be filled in when we know it.
129  assert len(ccd['amplifiers']) > 0
130  amp = list(ccd['amplifiers'].values())[0]
131 
132  rawBBox = self._makeBBoxFromList(amp['rawBBox']) # total in file
133  xRawExtent, yRawExtent = rawBBox.getDimensions()
134 
135  from lsst.afw.table import LL, LR, UL, UR
136  readCorners = dict(LL=LL, LR=LR, UL=UL, UR=UR)
137 
138  schema = AmpInfoTable.makeMinimalSchema()
139 
140  linThreshKey = schema.addField('linearityThreshold', type=float)
141  linMaxKey = schema.addField('linearityMaximum', type=float)
142  linUnitsKey = schema.addField('linearityUnits', type=str, size=9)
143  hduKey = schema.addField('hdu', type=np.int32)
144  # end placeholder
145  self.ampInfoDict = {}
146  ampCatalog = AmpInfoCatalog(schema)
147  for name, amp in sorted(ccd['amplifiers'].items(), key=lambda x: x[1]['hdu']):
148  record = ampCatalog.addNew()
149  record.setName(name)
150  record.set(hduKey, amp['hdu'])
151 
152  ix, iy = amp['ixy']
153  perAmpData = amp['perAmpData']
154  if perAmpData:
155  x0, y0 = 0, 0 # origin of data within each amp image
156  else:
157  x0, y0 = ix*xRawExtent, iy*yRawExtent
158 
159  rawDataBBox = self._makeBBoxFromList(amp['rawDataBBox']) # Photosensitive area
160  xDataExtent, yDataExtent = rawDataBBox.getDimensions()
161  record.setBBox(afwGeom.BoxI(
162  afwGeom.PointI(ix*xDataExtent, iy*yDataExtent), rawDataBBox.getDimensions()))
163 
164  rawBBox = self._makeBBoxFromList(amp['rawBBox'])
165  rawBBox.shift(afwGeom.ExtentI(x0, y0))
166  record.setRawBBox(rawBBox)
167 
168  rawDataBBox = self._makeBBoxFromList(amp['rawDataBBox'])
169  rawDataBBox.shift(afwGeom.ExtentI(x0, y0))
170  record.setRawDataBBox(rawDataBBox)
171 
172  rawSerialOverscanBBox = self._makeBBoxFromList(amp['rawSerialOverscanBBox'])
173  rawSerialOverscanBBox.shift(afwGeom.ExtentI(x0, y0))
174  record.setRawHorizontalOverscanBBox(rawSerialOverscanBBox)
175 
176  rawParallelOverscanBBox = self._makeBBoxFromList(amp['rawParallelOverscanBBox'])
177  rawParallelOverscanBBox.shift(afwGeom.ExtentI(x0, y0))
178  record.setRawVerticalOverscanBBox(rawParallelOverscanBBox)
179 
180  rawSerialPrescanBBox = self._makeBBoxFromList(amp['rawSerialPrescanBBox'])
181  rawSerialPrescanBBox.shift(afwGeom.ExtentI(x0, y0))
182  record.setRawPrescanBBox(rawSerialPrescanBBox)
183 
184  if perAmpData:
185  record.setRawXYOffset(afwGeom.Extent2I(ix*xRawExtent, iy*yRawExtent))
186  else:
187  record.setRawXYOffset(afwGeom.Extent2I(0, 0))
188 
189  record.setReadoutCorner(readCorners[amp['readCorner']])
190  record.setGain(amp['gain'])
191  record.setReadNoise(amp['readNoise'])
192  record.setSaturation(amp['saturation'])
193  record.setHasRawInfo(True)
194  # flip data when assembling if needs be (e.g. data from the serial at the top of a CCD)
195  flipX, flipY = amp.get("flipXY")
196 
197  record.setRawFlipX(flipX)
198  record.setRawFlipY(flipY)
199  # linearity placeholder stuff
200  record.setLinearityCoeffs([float(val) for val in amp['linearityCoeffs']])
201  record.setLinearityType(amp['linearityType'])
202  record.set(linThreshKey, float(amp['linearityThreshold']))
203  record.set(linMaxKey, float(amp['linearityMax']))
204  record.set(linUnitsKey, "DN")
205  return ampCatalog
def _makeDetectorList(self, ccdParams, focalPlaneToFieldAngle)
Definition: yamlCamera.py:59
def __init__(self, cameraYamlFile)
Definition: yamlCamera.py:42
def _makeDetectorConfigList(self, ccdParams)
Definition: yamlCamera.py:82