lsst.obs.base  13.0-47-gbf427ee+3
 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 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  radialCoeffs = np.array(cameraParams["radialCoeffs"])/plateScale.asRadians()
43  focalPlaneToPupil = afwGeom.makeRadialTransform(radialCoeffs)
44  pupilToFocalPlane = focalPlaneToPupil.getInverse()
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  if 'crosstalk' in ccd:
93  detectorConfig.crosstalk = ccd['crosstalk']
94 
95  return detectorConfigs
96 
97  @staticmethod
98  def _makeBBoxFromList(ylist):
99  """Given a list [(x0, y0), (xsize, ysize)], probably from a yaml file, return a BoxI
100  """
101  (x0, y0), (xsize, ysize) = ylist
102  return afwGeom.BoxI(afwGeom.PointI(x0, y0), afwGeom.ExtentI(xsize, ysize))
103 
104  def _makeAmpInfoCatalog(self, ccd):
105  """Construct an amplifier info catalog
106  """
107  # Much of this will need to be filled in when we know it.
108  assert len(ccd['amplifiers']) > 0
109  amp = ccd['amplifiers'].values()[0]
110 
111  rawBBox = self._makeBBoxFromList(amp['rawBBox']) # total in file
112  xRawExtent, yRawExtent = rawBBox.getDimensions()
113 
114  from lsst.afw.table import LL, LR, UL, UR
115  readCorners = dict(LL = LL, LR = LR, UL = UL, UR = UR)
116 
117  schema = AmpInfoTable.makeMinimalSchema()
118 
119  linThreshKey = schema.addField('linearityThreshold', type=float)
120  linMaxKey = schema.addField('linearityMaximum', type=float)
121  linUnitsKey = schema.addField('linearityUnits', type=str, size=9)
122  hduKey = schema.addField('hdu', type=np.int32)
123  # end placeholder
124  self.ampInfoDict = {}
125  ampCatalog = AmpInfoCatalog(schema)
126  for name, amp in sorted(ccd['amplifiers'].items(), key=lambda x: x[1]['hdu']):
127  record = ampCatalog.addNew()
128  record.setName(name)
129  record.set(hduKey, amp['hdu'])
130 
131  ix, iy = amp['ixy']
132  perAmpData = amp['perAmpData']
133  if perAmpData:
134  x0, y0 = 0, 0 # origin of data within each amp image
135  else:
136  x0, y0 = ix*xRawExtent, iy*yRawExtent
137 
138  rawDataBBox = self._makeBBoxFromList(amp['rawDataBBox']) # Photosensitive area
139  xDataExtent, yDataExtent = rawDataBBox.getDimensions()
140  record.setBBox(afwGeom.BoxI(
141  afwGeom.PointI(ix*xDataExtent, iy*yDataExtent), rawDataBBox.getDimensions()))
142 
143  rawBBox = self._makeBBoxFromList(amp['rawBBox'])
144  rawBBox.shift(afwGeom.ExtentI(x0, y0))
145  record.setRawBBox(rawBBox)
146 
147  rawDataBBox = self._makeBBoxFromList(amp['rawDataBBox'])
148  rawDataBBox.shift(afwGeom.ExtentI(x0, y0))
149  record.setRawDataBBox(rawDataBBox)
150 
151  rawSerialOverscanBBox = self._makeBBoxFromList(amp['rawSerialOverscanBBox'])
152  rawSerialOverscanBBox.shift(afwGeom.ExtentI(x0, y0))
153  record.setRawHorizontalOverscanBBox(rawSerialOverscanBBox)
154 
155  rawParallelOverscanBBox = self._makeBBoxFromList(amp['rawParallelOverscanBBox'])
156  rawParallelOverscanBBox.shift(afwGeom.ExtentI(x0, y0))
157  record.setRawVerticalOverscanBBox(rawParallelOverscanBBox)
158 
159  rawSerialPrescanBBox = self._makeBBoxFromList(amp['rawSerialPrescanBBox'])
160  rawSerialPrescanBBox.shift(afwGeom.ExtentI(x0, y0))
161  record.setRawPrescanBBox(rawSerialPrescanBBox)
162 
163  if perAmpData:
164  record.setRawXYOffset(afwGeom.Extent2I(ix*xRawExtent, iy*yRawExtent))
165  else:
166  record.setRawXYOffset(afwGeom.Extent2I(0, 0))
167 
168  record.setReadoutCorner(readCorners[amp['readCorner']])
169  record.setGain(amp['gain'])
170  record.setReadNoise(amp['readNoise'])
171  record.setSaturation(amp['saturation'])
172  record.setHasRawInfo(True)
173  # flip data when assembling if needs be (e.g. data from the serial at the top of a CCD)
174  flipX, flipY = amp.get("flipXY")
175 
176  record.setRawFlipX(flipX)
177  record.setRawFlipY(flipY)
178  # linearity placeholder stuff
179  record.setLinearityCoeffs([float(val) for val in amp['linearityCoeffs']])
180  record.setLinearityType(amp['linearityType'])
181  record.set(linThreshKey, float(amp['linearityThreshold']))
182  record.set(linMaxKey, float(amp['linearityMax']))
183  record.set(linUnitsKey, "DN")
184  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