Coverage for python/lsst/obs/test/testCamera.py: 14%

90 statements  

« prev     ^ index     » next       coverage.py v7.1.0, created at 2023-02-05 18:08 -0800

1# This file is part of obs_test. 

2# 

3# Developed for the LSST Data Management System. 

4# This product includes software developed by the LSST Project 

5# (http://www.lsst.org). 

6# See the COPYRIGHT file at the top-level directory of this distribution 

7# for details of code ownership. 

8# 

9# This program is free software: you can redistribute it and/or modify 

10# it under the terms of the GNU General Public License as published by 

11# the Free Software Foundation, either version 3 of the License, or 

12# (at your option) any later version. 

13# 

14# This program is distributed in the hope that it will be useful, 

15# but WITHOUT ANY WARRANTY; without even the implied warranty of 

16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 

17# GNU General Public License for more details. 

18# 

19# You should have received a copy of the GNU General Public License 

20# along with this program. If not, see <http://www.gnu.org/licenses/>. 

21# 

22__all__ = ["TestCamera"] 

23 

24import numpy as np 

25 

26import lsst.afw.cameraGeom as cameraGeom 

27import lsst.geom as geom 

28import lsst.afw.geom as afwGeom 

29 

30 

31class TestCamera(cameraGeom.Camera): 

32 """A simple test Camera. 

33 

34 Notes 

35 ----- 

36 The camera has one ccd with name "0". 

37 That CCD has four amplifiers with names "00", "01", "10", and "11". 

38 

39 The camera is modeled after a small portion of the LSST sim 

40 Summer 2012 camera: a single detector with four amplifiers, 

41 consisting of raft 2,2 sensor 0,0, half of channels 0,0 0,1 1,0 and 1,1 

42 (the half closest to the Y centerline). 

43 

44 Note that the Summer 2012 camera has one very weird feature: 

45 the bias region (rawHOverscanBbox) is actually a prescan 

46 (it appears before the data pixels). 

47 

48 A raw image has the sky in the same orientation on all amplifier 

49 subregions, so no amplifier subregions need their pixels to be flipped. 

50 

51 Standard keys are: 

52 

53 * ``amp``: amplifier number: one of 00, 01, 10, 11 

54 * ``ccd``: ccd number: always 0 

55 * ``visit``: exposure number; test data includes one exposure 

56 with visit=1 

57 """ 

58 def __new__(cls): 

59 plateScale = geom.Angle(20, geom.arcseconds) # plate scale, in angle on sky/mm 

60 # Radial distortion is modeled as a radial polynomial that converts from focal plane (in mm) 

61 # to field angle (in radians). Thus the coefficients are: 

62 # C0: always 0, for continuity at the center of the focal plane; units are rad 

63 # C1: 1/plateScale; units are rad/mm 

64 # C2: usually 0; units are rad/mm^2 

65 # C3: radial distortion; units are rad/mm^3 

66 radialCoeff = np.array([0.0, 1.0, 0.0, 0.925]) / plateScale.asRadians() 

67 fieldAngleToFocalPlane = afwGeom.makeRadialTransform(radialCoeff) 

68 focalPlaneToFieldAngle = fieldAngleToFocalPlane.inverted() 

69 

70 camera = cameraGeom.Camera.Builder("test") 

71 cls._makeDetectors(camera, focalPlaneToFieldAngle) 

72 camera.setTransformFromFocalPlaneTo(cameraGeom.FIELD_ANGLE, focalPlaneToFieldAngle) 

73 return camera.finish() 

74 

75 def __init__(self): 

76 pass 

77 

78 @classmethod 

79 def _makeDetectors(cls, camera, focalPlaneToFieldAngle): 

80 """Make a list of detectors 

81 

82 Parameters 

83 ---------- 

84 camera : `lsst.afw.cameraGeom.camera.Builder` 

85 Camera to append detectors to. 

86 focalPlaneToFieldAngle : `lsst.afw.geom.TransformPoint2ToPoint2` 

87 Transform from ``FOCAL_PLANE`` to ``FIELD_ANGLE`` coordinates 

88 in the forward direction. 

89 """ 

90 detectorConfigList = cls._makeDetectorConfigList() 

91 for detectorConfig in detectorConfigList: 

92 amplifiers = cls._makeAmplifierCatalog() 

93 detBuilder = cameraGeom.addDetectorBuilderFromConfig( 

94 camera, 

95 detectorConfig, 

96 amplifiers, 

97 focalPlaneToFieldAngle, 

98 ) 

99 if detBuilder is None: 

100 raise RuntimeError("Could not add detector!") 

101 

102 @classmethod 

103 def _makeDetectorConfigList(cls): 

104 """Make a list of detector configs 

105 

106 Returns 

107 ------- 

108 detectorConfigList : `list` of `lsst.afw.cameraGeom.DetectorConfig` 

109 List of detector configs. 

110 """ 

111 # this camera has one detector that corresponds to a subregion of lsstSim detector R:2,2 S:0,0 

112 # with lower left corner 0, 1000 and dimensions 1018 x 2000 

113 # i.e. half of each of the following channels: 0,0, 0,1, 1,0 and 1,1 

114 detector0Config = cameraGeom.DetectorConfig() 

115 detector0Config.name = '0' 

116 detector0Config.id = 0 

117 detector0Config.serial = '0000011' 

118 detector0Config.detectorType = 0 

119 detector0Config.bbox_x0 = 0 

120 detector0Config.bbox_x1 = 1017 

121 detector0Config.bbox_y0 = 0 

122 detector0Config.bbox_y1 = 1999 

123 detector0Config.pixelSize_x = 0.01 

124 detector0Config.pixelSize_y = 0.01 

125 detector0Config.transformDict.nativeSys = 'Pixels' 

126 detector0Config.transformDict.transforms = None 

127 detector0Config.refpos_x = 2035.5 

128 detector0Config.refpos_y = 999.5 

129 detector0Config.offset_x = -42.26073 

130 detector0Config.offset_y = -42.21914 

131 detector0Config.transposeDetector = False 

132 detector0Config.pitchDeg = 0.0 

133 detector0Config.yawDeg = 90.0 

134 detector0Config.rollDeg = 0.0 

135 return [detector0Config] 

136 

137 @classmethod 

138 def _makeAmplifierCatalog(cls): 

139 """Construct an amplifier info catalog 

140 

141 Returns 

142 ------- 

143 ampCatalog : `List` of `lsst.afw.cameraGeom.Amplifier.Builder() 

144 Amplifier information catalog. 

145 

146 Notes 

147 ----- 

148 The LSSTSim S12 amplifiers are unusual in that they start with 4 pixels 

149 of usable bias region (which is used to set rawHOverscanBbox, despite the name), 

150 followed by the data. There is no other underscan or overscan. 

151 """ 

152 xDataExtent = 509 # trimmed 

153 yDataExtent = 1000 

154 xBiasExtent = 4 

155 xRawExtent = xDataExtent + xBiasExtent 

156 yRawExtent = yDataExtent 

157 readNoise = 3.975 # amplifier read noise, in e- 

158 saturationLevel = 65535 

159 linearityType = cameraGeom.NullLinearityType 

160 linearityCoeffs = [0, 0, 0, 0] 

161 

162 ampCatalog = [] 

163 for ampX in (0, 1): 

164 for ampY in (0, 1): 

165 # amplifier gain (e-/ADU) and read noiuse (ADU/pixel) from lsstSim raw data 

166 # note that obs_test amp <ampX><ampY> = lsstSim amp C<ampY>,<ampX> (axes are swapped) 

167 gain = { 

168 (0, 0): 1.7741, # C0,0 

169 (0, 1): 1.65881, # C1,0 

170 (1, 0): 1.74151, # C0,1 

171 (1, 1): 1.67073, # C1,1 

172 }[(ampX, ampY)] 

173 readNoise = { 

174 (0, 0): 3.97531706217237, # C0,0 

175 (0, 1): 4.08263755342685, # C1,0 

176 (1, 0): 4.02753931932633, # C0,1 

177 (1, 1): 4.1890610691135, # C1,1 

178 }[(ampX, ampY)] 

179 amplifier = cameraGeom.Amplifier.Builder() 

180 amplifier.setName("%d%d" % (ampX, ampY)) 

181 amplifier.setBBox(geom.Box2I( 

182 geom.Point2I(ampX * xDataExtent, ampY * yDataExtent), 

183 geom.Extent2I(xDataExtent, yDataExtent), 

184 )) 

185 

186 x0Raw = ampX * xRawExtent 

187 y0Raw = ampY * yRawExtent 

188 

189 # bias region (which is prescan, in this case) is before the data 

190 readCorner = cameraGeom.ReadoutCorner.LL 

191 x0Bias = x0Raw 

192 x0Data = x0Bias + xBiasExtent 

193 

194 amplifier.setRawBBox(geom.Box2I( 

195 geom.Point2I(x0Raw, y0Raw), 

196 geom.Extent2I(xRawExtent, yRawExtent), 

197 )) 

198 amplifier.setRawDataBBox(geom.Box2I( 

199 geom.Point2I(x0Data, y0Raw), 

200 geom.Extent2I(xDataExtent, yDataExtent), 

201 )) 

202 amplifier.setRawHorizontalOverscanBBox(geom.Box2I( 

203 geom.Point2I(x0Bias, y0Raw), 

204 geom.Extent2I(xBiasExtent, yRawExtent), 

205 )) 

206 amplifier.setRawXYOffset(geom.Extent2I(x0Raw, y0Raw)) 

207 amplifier.setReadoutCorner(readCorner) 

208 amplifier.setGain(gain) 

209 amplifier.setReadNoise(readNoise) 

210 amplifier.setSaturation(saturationLevel) 

211 amplifier.setSuspectLevel(float("nan")) 

212 amplifier.setLinearityCoeffs([float(val) for val in linearityCoeffs]) 

213 amplifier.setLinearityType(linearityType) 

214 # amplifier.setHasRawInfo(True) 

215 amplifier.setRawFlipX(False) 

216 amplifier.setRawFlipY(False) 

217 amplifier.setRawVerticalOverscanBBox(geom.Box2I()) # no vertical overscan 

218 amplifier.setRawPrescanBBox(geom.Box2I()) # no horizontal prescan 

219 ampCatalog.append(amplifier) 

220 return ampCatalog