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