def parseCcds(policy, ccdParams, ccdToUse=None): """ Make DetectorConfigs for each CCD in the mosaic @param policy: Poicy object to parse @param ccdParams: CCD level parameters returned by makeCcdParams @param ccdToUse: Type of CCD to use to construct the config, use Policy value if None @return a dictionary containing a list of DetectorConfigs and a dictionary of AmpInfoTable objects keyed on CCD name """ specialChipMap = {} eParams = makeEparams(policy) ampInfoDict = {} ccdInfoList = [] rafts = policy.getArray('Raft') if len(rafts) > 1: raise ValueError("Expecting only one raft") for ccd in rafts[0].getArray('Ccd'): detConfig = DetectorConfig() schema = afwTable.AmpInfoTable.makeMinimalSchema() ampCatalog = afwTable.AmpInfoCatalog(schema) if ccdToUse is not None: ccdParam = ccdParams[ccdToUse] else: ccdParam = ccdParams[ccd.get('ptype')] detConfig.name = ccd.get('name') detConfig.serial = str(ccd.get('serial')) detConfig.id = int(ccd.get('name')[-2:]) offset = ccd.getArray('offset') if ccdParam['offsetUnit'] == 'pixels': offset[0] *= ccdParam['pixelSize'] offset[1] *= ccdParam['pixelSize'] detConfig.offset_x = offset[0] detConfig.offset_y = offset[1] if detConfig.name in specialChipMap: detConfig.detectorType = specialChipMap[detConfig.name] else: detConfig.detectorType = SCIENCE detConfig.pixelSize_x = ccdParam['pixelSize'] detConfig.pixelSize_y = ccdParam['pixelSize'] detConfig.refpos_x = (ccdParam['xsize'] - 1) / 2. detConfig.refpos_y = (ccdParam['ysize'] - 1) / 2. detConfig.bbox_x0 = 0 detConfig.bbox_y0 = 0 detConfig.bbox_x1 = ccdParam['xsize'] - 1 detConfig.bbox_y1 = ccdParam['ysize'] - 1 detConfig.rollDeg = 0. detConfig.pitchDeg = 0. detConfig.yawDeg = 90. * ccd.get('nQuarter') + ccd.getArray( 'orientation')[2] for amp in ccdParam['ampArr']: eparms = None for ep in eParams[ccd.get('name')]: if amp['id'] == ep['index'][0]: eparms = ep if eparms is None: raise ValueError("Could not find electronic params.") addAmp(ampCatalog, amp, eparms) ampInfoDict[ccd.get('name')] = ampCatalog ccdInfoList.append(detConfig) return {"ccdInfo": ccdInfoList, "ampInfo": ampInfoDict}
def buildDetector(self): """Take all the information and build a Detector object. The Detector object is necessary for doing things like assembly. Returns ------- detector : `lsst.afw.cameraGeom.Detector` The detector. """ if self.detector is not None: return self.detector schema = afwTable.AmpInfoTable.makeMinimalSchema() ampInfo = afwTable.AmpInfoCatalog(schema) for ampMetadata in self.ampMetadataList: record = ampInfo.addNew() self.defaultAmpMap.setAttributes(record, ampMetadata, self.doRaise) record.setHasRawInfo(True) detConfig = afwCameraGeom.DetectorConfig() self.defaultDetectorMap.setAttributes(detConfig, self.detectorMetadata, self.doRaise) self.detector = afwCameraGeom.makeDetector(detConfig, ampInfo, self.focalPlaneToField) return self.detector
def makeCcd(ccdId): schema = afwTable.AmpInfoTable.makeMinimalSchema() ampCatalog = afwTable.AmpInfoCatalog(schema) ccdName = ccdId + 1 for i in range(1): addAmp(ampCatalog, i, readout[ccdId - 1][i], gain_all[ccdId - 1][i]) return ampCatalog.writeFits("n%s_necam.fits" % ccdName)
def makeCcd(): schema = afwTable.AmpInfoTable.makeMinimalSchema() ampCatalog = afwTable.AmpInfoCatalog(schema) addAmp(ampCatalog) return ampCatalog.writeFits('g2_goto.fits')
def setUp(self): ampInfoShema = afwTable.AmpInfoTable.makeMinimalSchema() ampInfoCat = afwTable.AmpInfoCatalog(ampInfoShema) ampInfo = ampInfoCat.addNew() ampInfo.setRawBBox( afwGeom.Box2I(afwGeom.Point2I(-5, 7), afwGeom.Extent2I(53, 104))) ampInfo.setSuspectLevel(25000) self.ampInfo = ampInfo self.isrTask = ipIsr.IsrTask()
def makeDetector(self, bbox=None, numAmps=None, rowInds=None, colIndOffsets=None, detName="det_a", detSerial="123", linearityType="LookupTable"): """!Make a detector @param[in] bbox bounding box for image @param[n] numAmps x,y number of amplifiers (pair of int) @param[in] rowInds index of lookup table for each amplifier (array of shape numAmps) @param[in] colIndOffsets column index offset for each amplifier (array of shape numAmps) @param[in] detName detector name (a str) @param[in] detSerial detector serial numbe (a str) @param[in] linearityType name of linearity type (a str) @return a detector (an lsst.afw.cameraGeom.Detector) """ bbox = bbox if bbox is not None else self.bbox numAmps = numAmps if numAmps is not None else self.numAmps rowInds = rowInds if rowInds is not None else self.rowInds colIndOffsets = colIndOffsets if colIndOffsets is not None else self.colIndOffsets schema = afwTable.AmpInfoTable.makeMinimalSchema() ampInfoCat = afwTable.AmpInfoCatalog(schema) boxArr = BoxGrid(box=bbox, numColRow=numAmps) for i in range(numAmps[0]): for j in range(numAmps[1]): ampInfo = ampInfoCat.addNew() ampInfo.setName("amp %d_%d" % (i + 1, j + 1)) ampInfo.setBBox(boxArr[i, j]) ampInfo.setLinearityType(linearityType) # setLinearityCoeffs is picky about getting a mixed int/float list. ampInfo.setLinearityCoeffs( np.array([rowInds[i, j], colIndOffsets[i, j], 0, 0], dtype=float)) detId = 1 orientation = cameraGeom.Orientation() pixelSize = afwGeom.Extent2D(1, 1) transMap = {} return cameraGeom.Detector( detName, detId, cameraGeom.SCIENCE, detSerial, bbox, ampInfoCat, orientation, pixelSize, transMap, )
def createDetector(nAmpX, nAmpY, nPixX, nPixY, pre, hOscan, vOscan, ext, isPerAmp): '''!Fill ampInfo tables \param[in] nAmpX -- Number of amps in the x direction \param[in] nAmpY -- Number of amps in the y direction \param[in] nPixX -- Number of pixels in the amp in the x direction \param[in] nPixY -- Number of pixels in the amp in the y direction \param[in] pre -- Number of prescan rows \param[in] hOscan -- Number of horizontal overscan columns \param[in] vOscan -- Number of vertical overscan rows \param[in] ext -- Number of pixels in the extended register \param[in] isPerAmp -- Are the raw amp data in separate images? \return an lsst.afw.cameraGeom.Detector object ''' schema = afwTable.AmpInfoTable.makeMinimalSchema() ampCatalog = afwTable.AmpInfoCatalog(schema) flipy = True for iy in range(nAmpY): flipy = not flipy flipx = True for ix in range(nAmpX): flipx = not flipx record = ampCatalog.addNew() populateAmpBoxes(nPixX, nPixY, pre, hOscan, vOscan, ext, flipx, flipy, ix, iy, isPerAmp, record) record.setGain(ix + iy * nAmpX + 1.) detConfig = DetectorConfig() detConfig.name = 'TestDetector' detConfig.id = 0 detConfig.bbox_x0 = 0 detConfig.bbox_y0 = 0 detConfig.bbox_x1 = nAmpX * nPixX - 1 detConfig.bbox_y1 = nAmpY * nPixY - 1 detConfig.detectorType = 0 #Science type detConfig.serial = 'THX1138' detConfig.offset_x = 0. detConfig.offset_y = 0. detConfig.refpos_x = nAmpX * nPixX * 0.5 - 0.5 detConfig.refpos_y = nAmpY * nPixY * 0.5 - 0.5 detConfig.yawDeg = 0. detConfig.pitchDeg = 0. detConfig.rollDeg = 0. detConfig.pixelSize_x = 10. / 1000. #in mm detConfig.pixelSize_y = 10. / 1000. #in mm detConfig.transposeDetector = False detConfig.transformDict.nativeSys = PIXELS.getSysName() fpTransform = afwGeom.xyTransformRegistry['identity']() plateScale = 1. return makeDetector(detConfig, ampCatalog, fpTransform, plateScale)
def makeDetector(self, bbox=None, numAmps=None, sqCoeffs=None, linearityType="Squared"): """!Make a detector @param[in] bbox bounding box for image @param[n] numAmps x,y number of amplifiers (pair of int) @param[in] sqCoeffs square coefficient for each amplifier (2D array of float) @param[in] detName detector name (a str) @param[in] detID detector ID (an int) @param[in] detSerial detector serial numbe (a str) @param[in] linearityType name of linearity type (a str) @return a detector (an lsst.afw.cameraGeom.Detector) """ bbox = bbox if bbox is not None else self.bbox numAmps = numAmps if numAmps is not None else self.numAmps sqCoeffs = sqCoeffs if sqCoeffs is not None else self.sqCoeffs schema = afwTable.AmpInfoTable.makeMinimalSchema() ampInfoCat = afwTable.AmpInfoCatalog(schema) boxArr = BoxGrid(box=bbox, numColRow=numAmps) for i in range(numAmps[0]): for j in range(numAmps[1]): ampInfo = ampInfoCat.addNew() ampInfo.setName("amp %d_%d" % (i + 1, j + 1)) ampInfo.setBBox(boxArr[i, j]) ampInfo.setLinearityType(linearityType) ampInfo.setLinearityCoeffs([sqCoeffs[i, j]]) detName = "det_a" detId = 1 detSerial = "123" orientation = cameraGeom.Orientation() pixelSize = afwGeom.Extent2D(1, 1) transMap = {} return cameraGeom.Detector( detName, detId, cameraGeom.SCIENCE, detSerial, bbox, ampInfoCat, orientation, pixelSize, transMap, )
def makeCcd(ccdId): ''' Make a CCD out of a set of amps Remove the for loop if you only have one amp per CCD. note that the name used to be set to 'ccd0_superbit.fits' in ampCatalog.writeFits('ccd%s_superbit.fits' %ccdId) (ccdId=0) That causes problems since 'ccd0_superbit' never appears in header of calib/data files, so not useful as identifier Changed writeFits to 'superbitccd', which \does\ appear in header ''' schema = afwTable.AmpInfoTable.makeMinimalSchema() ampCatalog = afwTable.AmpInfoCatalog(schema) for i in range(1): addAmp(ampCatalog,i,readout[ccdId][i],gain_all[ccdId][i]) return ampCatalog.writeFits('superbitccd.fits')
def makeCcd(ccdName, ccdId, offsetPoint): """make the information necessary to build a set detector @param ccdName: string name of the ccd @param ccdId: Integer id of the ccd @param offsetPoint: Point2D position of the center of the ccd in mm @return a dict of a DetectorConfig and an AmpInfoCatalog """ obsSdssDir = lsst.utils.getPackageDir('obs_sdss') opDir = os.path.join(obsSdssDir, "etc") sc = SdssCameraState(opDir, "opConfig-50000.par", "opECalib-50000.par") eparams = sc.getEParams(ccdName) width = 1024 * 2 height = 1361 pixelSize = 24e-3 # pixel size in mm schema = afwTable.AmpInfoTable.makeMinimalSchema() ampCatalog = afwTable.AmpInfoCatalog(schema) for i in range(2): addAmp(ampCatalog, i, eparams[i][1]) detConfig = DetectorConfig() detConfig.name = ccdName detConfig.id = ccdId detConfig.bbox_x0 = 0 detConfig.bbox_y0 = 0 detConfig.bbox_x1 = width - 1 detConfig.bbox_y1 = height - 1 detConfig.serial = ccdName detConfig.detectorType = SCIENCE detConfig.offset_x = offsetPoint.getX() detConfig.offset_y = offsetPoint.getY() detConfig.refpos_x = (width - 1) / 2. detConfig.refpos_y = (height - 1) / 2. detConfig.yawDeg = 0. detConfig.pitchDeg = 0. detConfig.rollDeg = 0. detConfig.pixelSize_x = pixelSize detConfig.pixelSize_y = pixelSize detConfig.transposeDetector = False detConfig.transformDict.nativeSys = PIXELS.getSysName() return {'ccdConfig': detConfig, 'ampInfo': ampCatalog}
def makeAmpTables(segmentsFile): """ Read the segments file from a PhoSim release and produce the appropriate AmpInfo @param segmentsFile -- String indicating where the file is located """ returnDict = {} readoutMap = {'LL': afwTable.LL, 'LR': afwTable.LR, 'UR': afwTable.UR, 'UL': afwTable.UL} detectorName = [] # set to a value that is an invalid dict key, to catch bugs with open(segmentsFile) as fh: fh.readline() for l in fh: els = l.rstrip().split() detectorName = els[1] # skip focus and guiding for now: if detectorName[0] in ('F', 'G'): continue if detectorName not in returnDict: schema = afwTable.AmpInfoTable.makeMinimalSchema() returnDict[detectorName] = afwTable.AmpInfoCatalog(schema) record = returnDict[detectorName].addNew() name = els[2] gain = float(els[7]) saturation = int(els[8]) readnoise = float(els[9]) xoff = int(els[5]) yoff = int(els[6]) ndatax = int(els[3]) ndatay = int(els[4]) flipx = False flipy = False if detectorName.startswith("S") and name == "A": readCorner = readoutMap['UR'] elif detectorName.startswith("S") and name == "B": readCorner = readoutMap['UL'] elif detectorName.startswith("N") and name == "A": readCorner = readoutMap['LL'] elif detectorName.startswith("N") and name == "B": readCorner = readoutMap['LR'] else: raise RuntimeError("Did not recognize detector name or amp name") prescan = 6 hoverscan = 50 voverscan = 50 rawBBox = afwGeom.Box2I(afwGeom.Point2I(xoff, yoff), afwGeom.Extent2I(ndatax + prescan + hoverscan, ndatay + voverscan)) # Note: I'm not particularry happy with how the data origin is derived (it neglects [xy]off), # but I don't see a better way. if readCorner is afwTable.LL: originRawData = afwGeom.Point2I(xoff + prescan + hoverscan, yoff) originData = afwGeom.Point2I(0, 0) originHOverscan = afwGeom.Point2I(xoff + prescan, yoff) originVOverscan = afwGeom.Point2I(xoff + prescan + hoverscan, yoff + ndatay) elif readCorner is afwTable.LR: originRawData = afwGeom.Point2I(xoff, yoff) originData = afwGeom.Point2I(ndatax, 0) originHOverscan = afwGeom.Point2I(xoff + ndatax, yoff) originVOverscan = afwGeom.Point2I(xoff, yoff + ndatay) elif readCorner is afwTable.UL: originRawData = afwGeom.Point2I(xoff + prescan + hoverscan, yoff + voverscan) originData = afwGeom.Point2I(0, 0) originHOverscan = afwGeom.Point2I(xoff + prescan, yoff + voverscan) originVOverscan = afwGeom.Point2I(xoff + prescan + hoverscan, yoff) elif readCorner is afwTable.UR: originRawData = afwGeom.Point2I(xoff, yoff + voverscan) originData = afwGeom.Point2I(ndatax, 0) originHOverscan = afwGeom.Point2I(xoff + ndatax, yoff + voverscan) originVOverscan = afwGeom.Point2I(xoff, yoff) else: raise RuntimeError("Expected readout corner to be LL, LR, UL, or UR") rawDataBBox = afwGeom.Box2I(originRawData, afwGeom.Extent2I(ndatax, ndatay)) dataBBox = afwGeom.Box2I(originData, afwGeom.Extent2I(ndatax, ndatay)) rawHorizontalOverscanBBox = afwGeom.Box2I(originHOverscan, afwGeom.Extent2I(hoverscan, ndatay)) rawVerticalOverscanBBox = afwGeom.Box2I(originVOverscan, afwGeom.Extent2I(ndatax, voverscan)) print("\nDetector=%s; Amp=%s" % (detectorName, name)) print(rawHorizontalOverscanBBox) print(rawVerticalOverscanBBox) print(dataBBox) print(rawBBox) # Set the elements of the record for this amp record.setBBox(dataBBox) # This is the box for the amp in the assembled frame record.setName(name) record.setReadoutCorner(readCorner) record.setGain(gain) record.setSaturation(saturation) record.setSuspectLevel(float("nan")) record.setReadNoise(readnoise) ampIndex = dict(A=0, B=1)[name] record.setLinearityCoeffs([ampIndex, 0, 0, 0]) record.setLinearityType(LinearizeLookupTable.LinearityType) print("Linearity type=%r; coeffs=%s" % (record.getLinearityType(), record.getLinearityCoeffs())) record.setHasRawInfo(True) record.setRawFlipX(flipx) record.setRawFlipY(flipy) record.setRawBBox(rawBBox) # I believe that xy offset is not needed if the raw data are pre-assembled record.setRawXYOffset(afwGeom.Extent2I(0, 0)) """ if readCorner is afwTable.LL: record.setRawXYOffset(afwGeom.Extent2I(xoff + prescan + hoverscan, yoff)) elif readCorner is afwTable.LR: record.setRawXYOffset(afwGeom.Extent2I(xoff, yoff)) elif readCorner is afwTable.UL: record.setRawXYOffset(afwGeom.Extent2I(xoff + prescan + hoverscan, yoff + voverscan)) elif readCorner is afwTable.UR: record.setRawXYOffset(afwGeom.Extent2I(xoff, yoff + voverscan)) """ record.setRawDataBBox(rawDataBBox) record.setRawHorizontalOverscanBBox(rawHorizontalOverscanBBox) record.setRawVerticalOverscanBBox(rawVerticalOverscanBBox) # I think of prescan as being along the bottom of the raw data. I actually # don't know how you would do a prescan in the serial direction. record.setRawPrescanBBox(afwGeom.Box2I()) return returnDict
def setUp(self): self.schema = afwTable.AmpInfoTable.makeMinimalSchema() self.catalog = afwTable.AmpInfoCatalog(self.schema)
def makeCcd(utId): schema = afwTable.AmpInfoTable.makeMinimalSchema() ampCatalog = afwTable.AmpInfoCatalog(schema) for i in range(2): addAmp(ampCatalog, i, readout[utId][i], gain_all[utId][i]) return ampCatalog.writeFits('g%s_goto.fits' % utId[-1])
def __init__(self, name="detector 1", id=1, detType=SCIENCE, serial="xkcd722", bbox=None, # do not use mutable objects as defaults numAmps=3, pixelSize=(0.02, 0.02), ampExtent=(5, 6), orientation=Orientation(), plateScale=20.0, radialDistortion=0.925, modFunc=None, ): """!Construct a DetectorWrapper @param[in] name detector name @param[in] id detector ID (int) @param[in] detType detector type (an lsst.afw.cameraGeom.DetectorType) @param[in] serial serial "number" (a string) @param[in] bbox bounding box; defaults to (0, 0), (1024x1024) (an lsst.afw.geom.Box2I) @param[in] numAmps number of amplifiers (int) @param[in] pixelSize pixel size (mm) (an lsst.afw.geom.Point2D) @param[in] ampExtent dimensions of amplifier image bbox (an lsst.afw.geom.Extent2I) @param[in] orientation orientation of CCC in focal plane (lsst.afw.cameraGeom.Orientation) @param[in] plateScale plate scale in arcsec/mm; 20.0 is for LSST @param[in] radialDistortion radial distortion, in mm/rad^2 (the r^3 coefficient of the radial distortion polynomial that converts PUPIL in radians to FOCAL_PLANE in mm); 0.925 is the value Dave Monet measured for lsstSim data @param[in] modFunc a function that can modify attributes just before constructing the detector; modFunc receives one argument: a DetectorWrapper with all attributes except detector set. """ # note that (0., 0.) for the reference position is the center of the first pixel self.name = name self.id = int(id) self.type = detType self.serial = serial if bbox is None: bbox = afwGeom.Box2I(afwGeom.Point2I(0, 0), afwGeom.Extent2I(1024, 1048)) self.bbox = bbox self.pixelSize = afwGeom.Extent2D(*pixelSize) self.ampExtent = afwGeom.Extent2I(*ampExtent) self.plateScale = float(plateScale) self.radialDistortion = float(radialDistortion) schema = afwTable.AmpInfoTable.makeMinimalSchema() self.ampInfo = afwTable.AmpInfoCatalog(schema) for i in range(numAmps): record = self.ampInfo.addNew() ampName = "amp %d" % (i + 1,) record.setName(ampName) record.setBBox(afwGeom.Box2I(afwGeom.Point2I(-1, 1), self.ampExtent)) record.setGain(1.71234e3) record.setReadNoise(0.521237e2) record.setReadoutCorner(afwTable.LL) record.setHasRawInfo(False) self.orientation = orientation # compute TAN_PIXELS transform pScaleRad = afwGeom.arcsecToRad(self.plateScale) radialDistortCoeffs = [0.0, 1.0/pScaleRad, 0.0, self.radialDistortion/pScaleRad] focalPlaneToPupil = afwGeom.RadialXYTransform(radialDistortCoeffs) pixelToTanPixel = makePixelToTanPixel( bbox = self.bbox, orientation = self.orientation, focalPlaneToPupil = focalPlaneToPupil, pixelSizeMm = self.pixelSize, plateScale = self.plateScale, ) self.transMap = { FOCAL_PLANE: self.orientation.makePixelFpTransform(self.pixelSize), CameraSys(TAN_PIXELS, self.name): pixelToTanPixel, CameraSys(ACTUAL_PIXELS, self.name): afwGeom.RadialXYTransform([0, 0.95, 0.01]), } if modFunc: modFunc(self) self.detector = Detector( self.name, self.id, self.type, self.serial, self.bbox, self.ampInfo, self.orientation, self.pixelSize, self.transMap, )
def makeAmpTables(segmentsFile, gainFile): """ Read the segments file from a PhoSim release and produce the appropriate AmpInfo @param segmentsFile -- String indicating where the file is located """ gainDict = {} """ with open(gainFile) as fh: for l in fh: els = l.rstrip().split() gainDict[els[0]] = {'gain':float(els[1]), 'saturation':int(els[2])} """ returnDict = {} #TODO currently there is no linearity provided, but we should identify #how to get this information. linearityCoeffs = (0.,1.,0.,0.) linearityType = "Polynomial" readoutMap = {'LL':afwTable.LL, 'LR':afwTable.LR, 'UR':afwTable.UR, 'UL':afwTable.UL} ampCatalog = None detectorName = [] # set to a value that is an invalid dict key, to catch bugs correctY0 = False with open(segmentsFile) as fh: for l in fh: if l.startswith("#"): continue els = l.rstrip().split() if len(els) == 4: if ampCatalog is not None: returnDict[detectorName] = ampCatalog detectorName = expandDetectorName(els[0]) numy = int(els[2]) schema = afwTable.AmpInfoTable.makeMinimalSchema() ampCatalog = afwTable.AmpInfoCatalog(schema) if len(els[0].split('_')) == 3: #wavefront sensor correctY0 = True else: correctY0 = False continue record = ampCatalog.addNew() name = els[0].split("_")[-1] name = '%s,%s'%(name[1], name[2]) #Because of the camera coordinate system, we choose an #image coordinate system that requires a -90 rotation to get #the correct pixel positions from the #phosim segments file y0 = numy - 1 - int(els[2]) y1 = numy - 1 - int(els[1]) #Another quirk of the phosim file is that one of the wavefront sensor #chips has an offset of 2000 pix in y. It's always the 'C1' chip. if correctY0: if y0 > 0: y1 -= y0 y0 = 0 x0 = int(els[3]) x1 = int(els[4]) try: saturation = gainDict[els[0]]['saturation'] gain = gainDict[els[0]]['gain'] except KeyError: # Set default if no gain exists saturation = 65535 gain = float(els[7]) readnoise = float(els[11]) bbox = afwGeom.Box2I(afwGeom.Point2I(x0, y0), afwGeom.Point2I(x1, y1)) if int(els[5]) == -1: flipx = False else: flipx = True if int(els[6]) == 1: flipy = False else: flipy = True #Since the amps are stored in amp coordinates, the readout is the same #for all amps readCorner = readoutMap['LL'] ndatax = x1 - x0 + 1 ndatay = y1 - y0 + 1 #Because in versions v3.3.2 and earlier there was no overscan, we use the extended register as the overscan region prescan = 1 hoverscan = 0 extended = 4 voverscan = 0 rawBBox = afwGeom.Box2I(afwGeom.Point2I(0,0), afwGeom.Extent2I(extended+ndatax+hoverscan, prescan+ndatay+voverscan)) rawDataBBox = afwGeom.Box2I(afwGeom.Point2I(extended, prescan), afwGeom.Extent2I(ndatax, ndatay)) rawHorizontalOverscanBBox = afwGeom.Box2I(afwGeom.Point2I(0, prescan), afwGeom.Extent2I(extended, ndatay)) rawVerticalOverscanBBox = afwGeom.Box2I(afwGeom.Point2I(extended, prescan+ndatay), afwGeom.Extent2I(ndatax, voverscan)) rawPrescanBBox = afwGeom.Box2I(afwGeom.Point2I(extended, 0), afwGeom.Extent2I(ndatax, prescan)) extraRawX = extended + hoverscan extraRawY = prescan + voverscan rawx0 = x0 + extraRawX*(x0//ndatax) rawy0 = y0 + extraRawY*(y0//ndatay) #Set the elements of the record for this amp record.setBBox(bbox) record.setName(name) record.setReadoutCorner(readCorner) record.setGain(gain) record.setSaturation(saturation) record.setReadNoise(readnoise) record.setLinearityCoeffs(linearityCoeffs) record.setLinearityType(linearityType) record.setHasRawInfo(True) record.setRawFlipX(flipx) record.setRawFlipY(flipy) record.setRawBBox(rawBBox) record.setRawXYOffset(afwGeom.Extent2I(rawx0, rawy0)) record.setRawDataBBox(rawDataBBox) record.setRawHorizontalOverscanBBox(rawHorizontalOverscanBBox) record.setRawVerticalOverscanBBox(rawVerticalOverscanBBox) record.setRawPrescanBBox(rawPrescanBBox) returnDict[detectorName] = ampCatalog return returnDict
def __init__( self, name="detector 1", id=1, detType=SCIENCE, serial="xkcd722", bbox=None, # do not use mutable objects as defaults numAmps=3, pixelSize=(0.02, 0.02), ampExtent=(5, 6), orientation=Orientation(), plateScale=20.0, radialDistortion=0.925, crosstalk=None, modFunc=None, physicalType="CCD", ): # note that (0., 0.) for the reference position is the center of the # first pixel self.name = name self.id = int(id) self.type = detType self.serial = serial if bbox is None: bbox = lsst.geom.Box2I(lsst.geom.Point2I(0, 0), lsst.geom.Extent2I(1024, 1048)) self.bbox = bbox self.pixelSize = lsst.geom.Extent2D(*pixelSize) self.ampExtent = lsst.geom.Extent2I(*ampExtent) self.plateScale = float(plateScale) self.radialDistortion = float(radialDistortion) schema = afwTable.AmpInfoTable.makeMinimalSchema() self.ampInfo = afwTable.AmpInfoCatalog(schema) for i in range(numAmps): record = self.ampInfo.addNew() ampName = "amp %d" % (i + 1, ) record.setName(ampName) record.setBBox( lsst.geom.Box2I(lsst.geom.Point2I(-1, 1), self.ampExtent)) record.setGain(1.71234e3) record.setReadNoise(0.521237e2) record.setReadoutCorner(afwTable.LL) record.setHasRawInfo(False) self.orientation = orientation # compute TAN_PIXELS transform pScaleRad = lsst.geom.arcsecToRad(self.plateScale) radialDistortCoeffs = [ 0.0, 1.0 / pScaleRad, 0.0, self.radialDistortion / pScaleRad ] focalPlaneToField = afwGeom.makeRadialTransform(radialDistortCoeffs) pixelToTanPixel = makePixelToTanPixel( bbox=self.bbox, orientation=self.orientation, focalPlaneToField=focalPlaneToField, pixelSizeMm=self.pixelSize, ) self.transMap = { FOCAL_PLANE: self.orientation.makePixelFpTransform(self.pixelSize), CameraSys(TAN_PIXELS, self.name): pixelToTanPixel, CameraSys(ACTUAL_PIXELS, self.name): afwGeom.makeRadialTransform([0, 0.95, 0.01]), } if crosstalk is None: crosstalk = [[0.0 for _ in range(numAmps)] for _ in range(numAmps)] self.crosstalk = crosstalk self.physicalType = physicalType if modFunc: modFunc(self) self.detector = Detector( self.name, self.id, self.type, self.serial, self.bbox, self.ampInfo, self.orientation, self.pixelSize, self.transMap, np.array(self.crosstalk, dtype=np.float32), self.physicalType, )
def makeAmpCatalogs(self, ampFile, isLsstLike=False): """Construct a dict of AmpInfoCatalog, one per detector. Parameters ---------- ampFile : `str` Path to amplifier data file. isLsstLike : `bool` If True then there is one raw image per amplifier; if False then there is one raw image per detector. """ readoutMap = { 'LL': afwTable.ReadoutCorner.LL, 'LR': afwTable.ReadoutCorner.LR, 'UR': afwTable.ReadoutCorner.UR, 'UL': afwTable.ReadoutCorner.UL, } amps = [] with open(ampFile) as fh: names = fh.readline().rstrip().lstrip("#").split("|") for l in fh: els = l.rstrip().split("|") ampProps = dict([(name, el) for name, el in zip(names, els)]) amps.append(ampProps) ampTablesDict = {} schema = afwTable.AmpInfoTable.makeMinimalSchema() linThreshKey = schema.addField('linearityThreshold', type=np.float64) linMaxKey = schema.addField('linearityMaximum', type=np.float64) linUnitsKey = schema.addField('linearityUnits', type=str, size=9) self.ampInfoDict = {} for amp in amps: if amp['ccd_name'] in ampTablesDict: ampCatalog = ampTablesDict[amp['ccd_name']] self.ampInfoDict[amp['ccd_name']]['namps'] += 1 else: ampCatalog = afwTable.AmpInfoCatalog(schema) ampTablesDict[amp['ccd_name']] = ampCatalog self.ampInfoDict[amp['ccd_name']] = {'namps': 1, 'linInfo': {}} record = ampCatalog.addNew() bbox = lsst.geom.Box2I( lsst.geom.Point2I(int(amp['trimmed_xmin']), int(amp['trimmed_ymin'])), lsst.geom.Point2I(int(amp['trimmed_xmax']), int(amp['trimmed_ymax']))) rawBbox = lsst.geom.Box2I( lsst.geom.Point2I(int(amp['raw_xmin']), int(amp['raw_ymin'])), lsst.geom.Point2I(int(amp['raw_xmax']), int(amp['raw_ymax']))) rawDataBbox = lsst.geom.Box2I( lsst.geom.Point2I(int(amp['raw_data_xmin']), int(amp['raw_data_ymin'])), lsst.geom.Point2I(int(amp['raw_data_xmax']), int(amp['raw_data_ymax']))) rawHOverscanBbox = lsst.geom.Box2I( lsst.geom.Point2I(int(amp['hoscan_xmin']), int(amp['hoscan_ymin'])), lsst.geom.Point2I(int(amp['hoscan_xmax']), int(amp['hoscan_ymax']))) rawVOverscanBbox = lsst.geom.Box2I( lsst.geom.Point2I(int(amp['voscan_xmin']), int(amp['voscan_ymin'])), lsst.geom.Point2I(int(amp['voscan_xmax']), int(amp['voscan_ymax']))) rawPrescanBbox = lsst.geom.Box2I( lsst.geom.Point2I(int(amp['pscan_xmin']), int(amp['pscan_ymin'])), lsst.geom.Point2I(int(amp['pscan_xmax']), int(amp['pscan_ymax']))) xoffset = int(amp['x_offset']) yoffset = int(amp['y_offset']) flipx = bool(int(amp['flipx'])) flipy = bool(int(amp['flipy'])) readcorner = 'LL' if not isLsstLike: offext = lsst.geom.Extent2I(xoffset, yoffset) if flipx: xExt = rawBbox.getDimensions().getX() rawBbox.flipLR(xExt) rawDataBbox.flipLR(xExt) rawHOverscanBbox.flipLR(xExt) rawVOverscanBbox.flipLR(xExt) rawPrescanBbox.flipLR(xExt) if flipy: yExt = rawBbox.getDimensions().getY() rawBbox.flipTB(yExt) rawDataBbox.flipTB(yExt) rawHOverscanBbox.flipTB(yExt) rawVOverscanBbox.flipTB(yExt) rawPrescanBbox.flipTB(yExt) if not flipx and not flipy: readcorner = 'LL' elif flipx and not flipy: readcorner = 'LR' elif flipx and flipy: readcorner = 'UR' elif not flipx and flipy: readcorner = 'UL' else: raise RuntimeError("Couldn't find read corner") flipx = False flipy = False rawBbox.shift(offext) rawDataBbox.shift(offext) rawHOverscanBbox.shift(offext) rawVOverscanBbox.shift(offext) rawPrescanBbox.shift(offext) xoffset = 0 yoffset = 0 offset = lsst.geom.Extent2I(xoffset, yoffset) record.setBBox(bbox) record.setRawXYOffset(offset) record.setName(str(amp['name'])) record.setReadoutCorner(readoutMap[readcorner]) record.setGain(float(amp['gain'])) record.setReadNoise(float(amp['readnoise'])) record.setLinearityCoeffs([ float(amp['lin_coeffs']), ]) record.setLinearityType(str(amp['lin_type'])) record.setHasRawInfo(True) record.setRawFlipX(flipx) record.setRawFlipY(flipy) record.setRawBBox(rawBbox) record.setRawDataBBox(rawDataBbox) record.setRawHorizontalOverscanBBox(rawHOverscanBbox) record.setRawVerticalOverscanBBox(rawVOverscanBbox) record.setRawPrescanBBox(rawPrescanBbox) record.set(linThreshKey, float(amp['lin_thresh'])) record.set(linMaxKey, float(amp['lin_max'])) record.set(linUnitsKey, str(amp['lin_units'])) # The current schema assumes third order coefficients saveCoeffs = (float(amp['lin_coeffs']), ) saveCoeffs += (np.nan, np.nan, np.nan) self.ampInfoDict[amp['ccd_name']]['linInfo'][amp['name']] = \ {'lincoeffs': saveCoeffs, 'lintype': str(amp['lin_type']), 'linthresh': float(amp['lin_thresh']), 'linmax': float(amp['lin_max']), 'linunits': str(amp['lin_units'])} return ampTablesDict
import lsst.afw.table as afwTable import lsst.afw.geom as afwGeom import numpy as np # This is copying from afw/tests/testAmpInfoTable.py: schema = afwTable.AmpInfoTable.makeMinimalSchema() catalog = afwTable.AmpInfoCatalog(schema) record = catalog.addNew() name = 'Amp1' bbox = afwGeom.Box2I(afwGeom.Point2I(0, 0), afwGeom.Extent2I(8176, 6132)) gain = 0.7 saturation = 57571 readNoise = 12.5 readoutCorner = afwTable.LL #I think this means Lower Left. linearityCoeffs = (1.0, np.nan, np.nan, np.nan) linearityType = "None" rawBBox = afwGeom.Box2I(afwGeom.Point2I(0, 0), afwGeom.Extent2I(8176, 6132)) rawXYOffset = afwGeom.Extent2I(0, 0) rawDataBBox = afwGeom.Box2I(afwGeom.Point2I(0, 0), afwGeom.Extent2I(8176, 6132)) #rawHorizontalOverscanBBox = afwGeom.Box2I(afwGeom.Point2I(8176, 0), afwGeom.Extent2I(0, 8176)) #rawVerticalOverscanBBox = afwGeom.Box2I(afwGeom.Point2I(6132, 0), afwGeom.Extent2I(0, 6132)) rawPrescanBBox = afwGeom.Box2I(afwGeom.Point2I(0, 0), afwGeom.Extent2I(0, 0)) record.setHasRawInfo(True) #Sets the first Flag=True record.setRawFlipX(False) #Sets the second Flag=False record.setRawFlipY(False) #Sets the third Flag=False record.setBBox(bbox) record.setName(name) record.setGain(gain)
def parseCcds(policy, ccdParams, ccdToUse=None): """ parse a policy into a set of ampInfo and detectorConfig objects @param[in] policy Policy to parse @param[in] ccdParams dictionary of dictionaries describing the ccds in the camera @param[in] ccdToUse Override the type of ccd to use given in the policy @return a dictionary of lists with detectorConfigs with the 'ccdInfo' key and AmpInfoCatalogs with the 'ampInfo' key """ # The pafs I have now in the hsc dir include the focus sensors (but not the guiders) specialChipMap = { '108': cameraGeom.FOCUS, '110': cameraGeom.FOCUS, '111': cameraGeom.FOCUS, '107': cameraGeom.FOCUS, '105': cameraGeom.FOCUS, '104': cameraGeom.FOCUS, '109': cameraGeom.FOCUS, '106': cameraGeom.FOCUS } eParams = makeEparams(policy) lParams = makeLparams(policy) ampInfoDict = {} ccdInfoList = [] rafts = policy.getArray('Raft') if len(rafts) > 1: raise ValueError("Expecting only one raft") for ccd in rafts[0].getArray('Ccd'): detConfig = DetectorConfig() schema = afwTable.AmpInfoTable.makeMinimalSchema() ampCatalog = afwTable.AmpInfoCatalog(schema) if ccdToUse is not None: ccdParam = ccdParams[ccdToUse] else: ccdParam = ccdParams[ccd.get('ptype')] detConfig.name = ccd.get('name') #This should be the serial number on the device, but for now is an integer id detConfig.serial = str(ccd.get('serial')) detConfig.id = ccd.get('serial') offset = ccd.getArray('offset') if ccdParam['offsetUnit'] == 'pixels': offset[0] *= ccdParam['pixelSize'] offset[1] *= ccdParam['pixelSize'] detConfig.offset_x = offset[0] detConfig.offset_y = offset[1] if detConfig.serial in specialChipMap: detConfig.detectorType = specialChipMap[detConfig.serial] else: detConfig.detectorType = SCIENCE detConfig.pixelSize_x = ccdParam['pixelSize'] detConfig.pixelSize_y = ccdParam['pixelSize'] detConfig.refpos_x = (ccdParam['xsize'] - 1) / 2. detConfig.refpos_y = (ccdParam['ysize'] - 1) / 2. detConfig.bbox_x0 = 0 detConfig.bbox_y0 = 0 detConfig.bbox_x1 = ccdParam['xsize'] - 1 detConfig.bbox_y1 = ccdParam['ysize'] - 1 detConfig.rollDeg = 0. detConfig.pitchDeg = 0. detConfig.yawDeg = 90. * ccd.get('nQuarter') + ccd.getArray( 'orientation')[2] for amp in ccdParam['ampArr']: eparms = None for ep in eParams[ccd.get('name')]: if amp['id'] - 1 == ep['index'][0]: eparms = ep if eparms is None: raise ValueError("Could not find electronic params.") lparms = None # Only science ccds (serial 0 through 103) have linearity params defined in hsc_geom.paf if detConfig.detectorType is SCIENCE: if lParams.has_key(ccd.get('serial')): for ep in lParams[ccd.get('serial')]: if amp['id'] == ep['index']: lparms = ep if lparms is None: if lParams.has_key( -1 ): # defaults in suprimecam/Full_Suprimecam*_geom.paf for ep in lParams[-1]: if amp['id'] == ep['index']: lparms = ep if lparms is None: raise ValueError("Could not find linearity params.") if lparms is None: lparms = {} lparms['index'] = amp['id'] lparms['coefficient'] = 0.0 lparms['type'] = 'NONE' lparms['threshold'] = 0.0 lparms['maxCorrectable'] = 0.0 lparms['intensityUnits'] = 'UNKNOWN' addAmp(ampCatalog, amp, eparms, lparms) ampInfoDict[ccd.get('name')] = ampCatalog ccdInfoList.append(detConfig) return {"ccdInfo": ccdInfoList, "ampInfo": ampInfoDict}