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