lsst.obs.base  14.0-18-g0eec5f5+11
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 
37  def __init__(self, cameraYamlFile):
38  """Construct a Camera
39  """
40  with open(cameraYamlFile) as fd:
41  cameraParams = yaml.load(fd, Loader=yaml.Loader)
42 
43  plateScale = afwGeom.Angle(cameraParams["plateScale"], afwGeom.arcseconds)
44  # radial coefficients of the form [0, no units, 1/rad but usually 0, 1/rad^2, ...]
45  # Radial distortion is modeled as a radial polynomial that converts from focal plane radius (in mm)
46  # to field angle (in radians). The coefficients are divided by the plate scale (in mm/radians)
47  # meaning that C1 is always 1.
48  radialCoeffs = np.array(cameraParams["radialCoeffs"])/plateScale.asRadians()
49  fieldAngleToFocalPlane = afwGeom.makeRadialTransform(radialCoeffs)
50  focalPlaneToFieldAngle = fieldAngleToFocalPlane.getInverse()
51  cameraTransformMap = cameraGeom.TransformMap(cameraGeom.FOCAL_PLANE,
52  {cameraGeom.FIELD_ANGLE: focalPlaneToFieldAngle})
53  detectorList = self._makeDetectorList(cameraParams["CCDs"], focalPlaneToFieldAngle)
54  cameraGeom.Camera.__init__(self, cameraParams["name"], detectorList, cameraTransformMap)
55 
56  def _makeDetectorList(self, ccdParams, focalPlaneToFieldAngle):
57  """!Make a list of detectors
58  @param[in] ccdParams Dict of YAML descriptions of CCDs
59  @param[in] focalPlaneToFieldAngle lsst.afw.geom.TransformPoint2ToPoint2
60  from FOCAL_PLANE to FIELD_ANGLE coordinates
61  @return a list of detectors (lsst.afw.cameraGeom.Detector)
62  """
63  detectorList = []
64  detectorConfigList = self._makeDetectorConfigList(ccdParams)
65  for ccd, detectorConfig in zip(ccdParams.values(), detectorConfigList):
66  ampInfoCatalog = self._makeAmpInfoCatalog(ccd)
67  detector = makeDetector(detectorConfig, ampInfoCatalog, focalPlaneToFieldAngle)
68  detectorList.append(detector)
69  return detectorList
70 
71  def _makeDetectorConfigList(self, ccdParams):
72  """!Make a list of detector configs
73 
74  @return a list of detector configs (lsst.afw.cameraGeom.DetectorConfig)
75  """
76  detectorConfigs = []
77  for name, ccd in ccdParams.items():
78  detectorConfig = cameraGeom.DetectorConfig()
79  detectorConfigs.append(detectorConfig)
80 
81  detectorConfig.name = name
82  detectorConfig.id = ccd['id']
83  detectorConfig.serial = ccd['serial']
84  detectorConfig.detectorType = ccd['detectorType']
85  # This is the orientation we need to put the serial direction along the x-axis
86  detectorConfig.bbox_x0, detectorConfig.bbox_y0 = ccd['bbox'][0]
87  detectorConfig.bbox_x1, detectorConfig.bbox_y1 = ccd['bbox'][1]
88  detectorConfig.pixelSize_x, detectorConfig.pixelSize_y = ccd['pixelSize']
89  detectorConfig.transformDict.nativeSys = ccd['transformDict']['nativeSys']
90  transforms = ccd['transformDict']['transforms']
91  detectorConfig.transformDict.transforms = None if transforms == 'None' else transforms
92  detectorConfig.refpos_x, detectorConfig.refpos_y = ccd['refpos']
93  detectorConfig.offset_x, detectorConfig.offset_y = ccd['offset']
94  detectorConfig.transposeDetector = ccd['transposeDetector']
95  detectorConfig.pitchDeg = ccd['pitch']
96  detectorConfig.yawDeg = ccd['yaw']
97  detectorConfig.rollDeg = ccd['roll']
98  if 'crosstalk' in ccd:
99  detectorConfig.crosstalk = ccd['crosstalk']
100 
101  return detectorConfigs
102 
103  @staticmethod
104  def _makeBBoxFromList(ylist):
105  """Given a list [(x0, y0), (xsize, ysize)], probably from a yaml file, return a BoxI
106  """
107  (x0, y0), (xsize, ysize) = ylist
108  return afwGeom.BoxI(afwGeom.PointI(x0, y0), afwGeom.ExtentI(xsize, ysize))
109 
110  def _makeAmpInfoCatalog(self, ccd):
111  """Construct an amplifier info catalog
112  """
113  # Much of this will need to be filled in when we know it.
114  assert len(ccd['amplifiers']) > 0
115  amp = list(ccd['amplifiers'].values())[0]
116 
117  rawBBox = self._makeBBoxFromList(amp['rawBBox']) # total in file
118  xRawExtent, yRawExtent = rawBBox.getDimensions()
119 
120  from lsst.afw.table import LL, LR, UL, UR
121  readCorners = dict(LL=LL, LR=LR, UL=UL, UR=UR)
122 
123  schema = AmpInfoTable.makeMinimalSchema()
124 
125  linThreshKey = schema.addField('linearityThreshold', type=float)
126  linMaxKey = schema.addField('linearityMaximum', type=float)
127  linUnitsKey = schema.addField('linearityUnits', type=str, size=9)
128  hduKey = schema.addField('hdu', type=np.int32)
129  # end placeholder
130  self.ampInfoDict = {}
131  ampCatalog = AmpInfoCatalog(schema)
132  for name, amp in sorted(ccd['amplifiers'].items(), key=lambda x: x[1]['hdu']):
133  record = ampCatalog.addNew()
134  record.setName(name)
135  record.set(hduKey, amp['hdu'])
136 
137  ix, iy = amp['ixy']
138  perAmpData = amp['perAmpData']
139  if perAmpData:
140  x0, y0 = 0, 0 # origin of data within each amp image
141  else:
142  x0, y0 = ix*xRawExtent, iy*yRawExtent
143 
144  rawDataBBox = self._makeBBoxFromList(amp['rawDataBBox']) # Photosensitive area
145  xDataExtent, yDataExtent = rawDataBBox.getDimensions()
146  record.setBBox(afwGeom.BoxI(
147  afwGeom.PointI(ix*xDataExtent, iy*yDataExtent), rawDataBBox.getDimensions()))
148 
149  rawBBox = self._makeBBoxFromList(amp['rawBBox'])
150  rawBBox.shift(afwGeom.ExtentI(x0, y0))
151  record.setRawBBox(rawBBox)
152 
153  rawDataBBox = self._makeBBoxFromList(amp['rawDataBBox'])
154  rawDataBBox.shift(afwGeom.ExtentI(x0, y0))
155  record.setRawDataBBox(rawDataBBox)
156 
157  rawSerialOverscanBBox = self._makeBBoxFromList(amp['rawSerialOverscanBBox'])
158  rawSerialOverscanBBox.shift(afwGeom.ExtentI(x0, y0))
159  record.setRawHorizontalOverscanBBox(rawSerialOverscanBBox)
160 
161  rawParallelOverscanBBox = self._makeBBoxFromList(amp['rawParallelOverscanBBox'])
162  rawParallelOverscanBBox.shift(afwGeom.ExtentI(x0, y0))
163  record.setRawVerticalOverscanBBox(rawParallelOverscanBBox)
164 
165  rawSerialPrescanBBox = self._makeBBoxFromList(amp['rawSerialPrescanBBox'])
166  rawSerialPrescanBBox.shift(afwGeom.ExtentI(x0, y0))
167  record.setRawPrescanBBox(rawSerialPrescanBBox)
168 
169  if perAmpData:
170  record.setRawXYOffset(afwGeom.Extent2I(ix*xRawExtent, iy*yRawExtent))
171  else:
172  record.setRawXYOffset(afwGeom.Extent2I(0, 0))
173 
174  record.setReadoutCorner(readCorners[amp['readCorner']])
175  record.setGain(amp['gain'])
176  record.setReadNoise(amp['readNoise'])
177  record.setSaturation(amp['saturation'])
178  record.setHasRawInfo(True)
179  # flip data when assembling if needs be (e.g. data from the serial at the top of a CCD)
180  flipX, flipY = amp.get("flipXY")
181 
182  record.setRawFlipX(flipX)
183  record.setRawFlipY(flipY)
184  # linearity placeholder stuff
185  record.setLinearityCoeffs([float(val) for val in amp['linearityCoeffs']])
186  record.setLinearityType(amp['linearityType'])
187  record.set(linThreshKey, float(amp['linearityThreshold']))
188  record.set(linMaxKey, float(amp['linearityMax']))
189  record.set(linUnitsKey, "DN")
190  return ampCatalog
def _makeDetectorList(self, ccdParams, focalPlaneToFieldAngle)
Make a list of detectors.
Definition: yamlCamera.py:56
def __init__(self, cameraYamlFile)
Definition: yamlCamera.py:37
def _makeDetectorConfigList(self, ccdParams)
Make a list of detector configs.
Definition: yamlCamera.py:71