Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1# 

2# LSST Data Management System 

3# Copyright 2008, 2009, 2010 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# 

22import os 

23 

24import lsst.utils 

25import lsst.afw.geom as afwGeom 

26import lsst.afw.cameraGeom.utils as cameraGeomUtils 

27from lsst.afw.cameraGeom import makeCameraFromAmpLists, CameraConfig, DetectorConfig, \ 

28 TransformMapConfig, DetectorType, PIXELS, FIELD_ANGLE, FOCAL_PLANE, NullLinearityType 

29import lsst.afw.table as afwTable 

30import lsst.geom as geom 

31from lsst.obs.sdss.convertOpECalib import SdssCameraState 

32 

33# 

34# Make an Amp 

35# 

36 

37 

38def addAmp(ampCatalog, i, eparams): 

39 """ Add an amplifier to an AmpInfoCatalog 

40 

41 @param ampCatalog: An instance of an AmpInfoCatalog object to fill with amp properties 

42 @param i which amplifier? (i == 0 ? left : right) 

43 @param eparams: Electronic parameters. This is a list of tuples with (i, params), 

44 where params is a dictionary of electronic parameters. 

45 """ 

46 # 

47 # Layout of active and overclock pixels in the as-readout data The layout is: 

48 # Amp0 || extended | overclock | data || data | overclock | extended || Amp1 

49 # for each row; all rows are identical in drift-scan data 

50 # 

51 height = 1361 # number of rows in a frame 

52 width = 1024 # number of data pixels read out through one amplifier 

53 nExtended = 8 # number of pixels in the extended register 

54 nOverclock = 32 # number of (horizontal) overclock pixels 

55 # 

56 # Construct the needed bounding boxes given that geometrical information. 

57 # 

58 # Note that all the offsets are relative to the origin of this amp, not to its eventual 

59 # position in the CCD 

60 # 

61 record = ampCatalog.addNew() 

62 xtot = width + nExtended + nOverclock 

63 allPixels = geom.BoxI(geom.PointI(0, 0), geom.ExtentI(xtot, height)) 

64 biasSec = geom.BoxI(geom.PointI(nExtended if i == 0 else width, 0), 

65 geom.ExtentI(nOverclock, height)) 

66 dataSec = geom.BoxI(geom.PointI(nExtended + nOverclock if i == 0 else 0, 0), 

67 geom.ExtentI(width, height)) 

68 emptyBox = geom.BoxI() 

69 bbox = geom.BoxI(geom.PointI(0, 0), geom.ExtentI(width, height)) 

70 bbox.shift(geom.Extent2I(width*i, 0)) 

71 

72 shiftp = geom.Extent2I(xtot*i, 0) 

73 allPixels.shift(shiftp) 

74 biasSec.shift(shiftp) 

75 dataSec.shift(shiftp) 

76 

77 record.setBBox(bbox) 

78 record.setRawXYOffset(geom.ExtentI(0, 0)) 

79 record.setName('left' if i == 0 else 'right') 

80 record.setReadoutCorner(afwTable.LL if i == 0 else afwTable.LR) 

81 record.setGain(eparams['gain']) 

82 record.setReadNoise(eparams['readNoise']) 

83 record.setSaturation(eparams['fullWell']) 

84 record.setSuspectLevel(float("nan")) 

85 record.setLinearityType(NullLinearityType) 

86 record.setLinearityCoeffs([1., ]) 

87 record.setHasRawInfo(True) 

88 record.setRawFlipX(False) 

89 record.setRawFlipY(False) 

90 record.setRawBBox(allPixels) 

91 record.setRawDataBBox(dataSec) 

92 record.setRawHorizontalOverscanBBox(biasSec) 

93 record.setRawVerticalOverscanBBox(emptyBox) 

94 record.setRawPrescanBBox(emptyBox) 

95 

96# 

97# Make a Ccd out of 2 Amps 

98# 

99 

100 

101def makeCcd(ccdName, ccdId, offsetPoint): 

102 """make the information necessary to build a set detector 

103 @param ccdName: string name of the ccd 

104 @param ccdId: Integer id of the ccd 

105 @param offsetPoint: Point2D position of the center of the ccd in mm 

106 @return a dict of a DetectorConfig and an AmpInfoCatalog 

107 """ 

108 obsSdssDir = lsst.utils.getPackageDir('obs_sdss') 

109 opDir = os.path.join(obsSdssDir, "etc") 

110 sc = SdssCameraState(opDir, "opConfig-50000.par", "opECalib-50000.par") 

111 eparams = sc.getEParams(ccdName) 

112 width = 1024*2 

113 height = 1361 

114 

115 pixelSize = 24e-3 # pixel size in mm 

116 schema = afwTable.AmpInfoTable.makeMinimalSchema() 

117 ampCatalog = afwTable.AmpInfoCatalog(schema) 

118 for i in range(2): 

119 addAmp(ampCatalog, i, eparams[i][1]) 

120 detConfig = DetectorConfig() 

121 detConfig.name = ccdName 

122 detConfig.id = ccdId 

123 detConfig.bbox_x0 = 0 

124 detConfig.bbox_y0 = 0 

125 detConfig.bbox_x1 = width - 1 

126 detConfig.bbox_y1 = height - 1 

127 detConfig.serial = ccdName 

128 detConfig.detectorType = DetectorType.SCIENCE 

129 detConfig.offset_x = offsetPoint.getX() 

130 detConfig.offset_y = offsetPoint.getY() 

131 detConfig.refpos_x = (width-1)/2. 

132 detConfig.refpos_y = (height-1)/2. 

133 detConfig.yawDeg = 0. 

134 detConfig.pitchDeg = 0. 

135 detConfig.rollDeg = 0. 

136 detConfig.pixelSize_x = pixelSize 

137 detConfig.pixelSize_y = pixelSize 

138 detConfig.transposeDetector = False 

139 detConfig.transformDict.nativeSys = PIXELS.getSysName() 

140 return {'ccdConfig': detConfig, 'ampInfo': ampCatalog} 

141 

142# 

143# Make a Camera out of 6 dewars and 5 chips per dewar 

144# 

145 

146 

147def makeCamera(name="SDSS", outputDir=None): 

148 """Make a camera 

149 @param name: name of the camera 

150 @param outputDir: If not None, write the objects used to make the camera to this location 

151 @return a camera object 

152 """ 

153 camConfig = CameraConfig() 

154 camConfig.name = name 

155 camConfig.detectorList = {} 

156 camConfig.plateScale = 16.5 # arcsec/mm 

157 pScaleRad = geom.arcsecToRad(camConfig.plateScale) 

158 radialDistortCoeffs = [0.0, 1.0/pScaleRad] 

159 tConfig = afwGeom.TransformConfig() 

160 tConfig.transform.name = 'inverted' 

161 radialClass = afwGeom.transformRegistry['radial'] 

162 tConfig.transform.active.transform.retarget(radialClass) 

163 tConfig.transform.active.transform.coeffs = radialDistortCoeffs 

164 tmc = TransformMapConfig() 

165 tmc.nativeSys = FOCAL_PLANE.getSysName() 

166 tmc.transforms = {FIELD_ANGLE.getSysName(): tConfig} 

167 camConfig.transformDict = tmc 

168 

169 ccdId = 0 

170 ampInfoCatDict = {} 

171 for i in range(6): 

172 dewarName = str(i+1) 

173 filters = "riuzg" 

174 for j, c in enumerate(reversed(filters)): 

175 ccdName = "%s%s" % (c, dewarName) 

176 offsetPoint = geom.Point2D(25.4*2.5*(2.5-i), 25.4*2.1*(2.0 - j)) 

177 ccdInfo = makeCcd(ccdName, ccdId, offsetPoint) 

178 ampInfoCatDict[ccdName] = ccdInfo['ampInfo'] 

179 camConfig.detectorList[ccdId] = ccdInfo['ccdConfig'] 

180 ccdId += 1 

181 if outputDir is not None: 

182 camConfig.save(os.path.join(outputDir, 'camera.py')) 

183 for k in ampInfoCatDict: 

184 ampInfoCatDict[k].writeFits(os.path.join(outputDir, "%s.fits"%(k))) 

185 return makeCameraFromAmpLists(camConfig, ampInfoCatDict) 

186 

187# 

188# Print a Ccd 

189# 

190 

191 

192def printCcd(title, ccd, trimmed=True, indent=""): 

193 """Print info about a ccd 

194 @param title: title for the ccd 

195 @param ccd: Detector object to interrogate 

196 @param trimmed: Find out information about a trimmed ccd? 

197 @param indent: Prefix to each output line 

198 """ 

199 print(indent, title, "CCD: ", ccd.getName()) 

200 if trimmed: 

201 allPixels = ccd.getBBox() 

202 else: 

203 allPixels = cameraGeomUtils.calcRawCcdBBox(ccd) 

204 print(indent, "Total size: %dx%d" % (allPixels.getWidth(), allPixels.getHeight())) 

205 for i, amp in enumerate(ccd): 

206 biasSec = amp.getRawHorizontalOverscanBBox() 

207 dataSec = amp.getRawDataBBox() 

208 

209 print(indent, " Amp: %s gain: %g" % (amp.getName(), 

210 amp.getGain())) 

211 

212 print(indent, " bias sec: %dx%d+%d+%d" % (biasSec.getWidth(), biasSec.getHeight(), 

213 biasSec.getMinX(), biasSec.getMinY())) 

214 

215 print(indent, " data sec: %dx%d+%d+%d" % (dataSec.getWidth(), dataSec.getHeight(), 

216 dataSec.getMinX(), dataSec.getMinY())) 

217 if i == 0: 

218 print() 

219 

220# 

221# Print a Camera 

222# 

223 

224 

225def printCamera(title, camera): 

226 """Print information about a camera 

227 @param title: title for camera output 

228 @param camera: Camera object to use to print the information 

229 """ 

230 print(title, "Camera:", camera.getName()) 

231 

232 for det in camera: 

233 print("%s %dx%d centre (mm): %s" % 

234 (det.getName(), 

235 det.getBBox().getWidth(), det.getBBox().getHeight(), 

236 det.getCenter(FOCAL_PLANE))) 

237 

238 

239def main(): 

240 camera = makeCamera("SDSS") 

241 

242 print() 

243 printCamera("", camera) 

244 

245 ccd = camera["r1"] 

246 

247 printCcd("Raw ", ccd, trimmed=False) 

248 

249 print() 

250 printCcd("Trimmed ", ccd, trimmed=True) 

251 

252 

253if __name__ == "__main__": 253 ↛ 254line 253 didn't jump to line 254, because the condition on line 253 was never true

254 main()