예제 #1
0
def makeCameraFromPath(cameraConfig, ampInfoPath, shortNameFunc,
                       pupilFactoryClass=PupilFactory):
    """Make a Camera instance from a directory of ampInfo files

    The directory must contain one ampInfo fits file for each detector in cameraConfig.detectorList.
    The name of each ampInfo file must be shortNameFunc(fullDetectorName) + ".fits".

    Parameters
    ----------
    cameraConfig : `CameraConfig`
        Config describing camera and its detectors.
    ampInfoPath : `str`
        Path to ampInfo data files.
    shortNameFunc : callable
        A function that converts a long detector name to a short one.
    pupilFactoryClass : `type`, optional
        Class to attach to camera; default is `lsst.afw.cameraGeom.PupilFactory`.

    Returns
    -------
    camera : `lsst.afw.cameraGeom.Camera`
        New Camera instance.
    """
    ampInfoCatDict = dict()
    for detectorConfig in cameraConfig.detectorList.values():
        shortName = shortNameFunc(detectorConfig.name)
        ampCatPath = os.path.join(ampInfoPath, shortName + ".fits")
        ampInfoCatalog = AmpInfoCatalog.readFits(ampCatPath)
        ampInfoCatDict[detectorConfig.name] = ampInfoCatalog

    return makeCameraFromCatalogs(cameraConfig, ampInfoCatDict, pupilFactoryClass)
예제 #2
0
def makeCasuWfcam():
    path = os.path.join(getPackageDir("obs_ukirt"), "casuWfcam", "camera")

    for detNum, (gain, sat) in enumerate(zip(GAIN, SATURATION), 1):
        det = AmpInfoCatalog(AmpInfoTable.makeMinimalSchema())
        amp = det.addNew()
        amp.setName(str(detNum))
        amp.setBBox(Box2I())
        amp.setGain(gain)
        amp.setReadNoise(READNOISE)
        amp.setSaturation(sat)
#        amp.setSuspectLevel(float("nan"))
        amp.setReadoutCorner(LL)
        amp.setLinearityType("none")
        amp.setHasRawInfo(False)

        det.writeFits(os.path.join(path, "%d.fits" % (detNum,)))
예제 #3
0
    def _makeAmpInfoCatalog(self):
        """Construct a trivial amplifier information catalog.

        The CBP code makes no use of this catalog, so it is as simple
        as possible: one amplifiers that covers the whole CCD,
        with no overscan, and semi-plausible values for everything else.

        Returns
        -------
        ampInfo : `lsst.afw.cameraGeom.AmpInfoCatalog`
            Amplifier information catalog.
        """
        ampExtent = lsst.geom.Extent2I(self.detectorWidthPix,
                                       self.detectorHeightPix)
        saturationLevel = 65535
        linearityType = cameraGeom.NullLinearityType
        linearityCoeffs = [0, 0, 0, 0]

        schema = AmpInfoTable.makeMinimalSchema()

        self.ampInfoDict = {}
        ampCatalog = AmpInfoCatalog(schema)
        gain = 1.8
        readNoise = 3.9
        record = ampCatalog.addNew()
        record.setName("0")
        ampBBox = lsst.geom.Box2I(lsst.geom.Point2I(0, 0), ampExtent)
        record.setBBox(ampBBox)
        readCorner = LL
        record.setRawBBox(ampBBox)
        record.setRawDataBBox(ampBBox)
        record.setRawHorizontalOverscanBBox(lsst.geom.Box2I())
        record.setRawXYOffset(lsst.geom.Extent2I(0, 0))
        record.setReadoutCorner(readCorner)
        record.setGain(gain)
        record.setReadNoise(readNoise)
        record.setSaturation(saturationLevel)
        record.setSuspectLevel(float("nan"))
        record.setLinearityCoeffs([float(val) for val in linearityCoeffs])
        record.setLinearityType(linearityType)
        record.setHasRawInfo(True)
        record.setRawFlipX(False)
        record.setRawFlipY(False)
        record.setRawVerticalOverscanBBox(lsst.geom.Box2I())
        record.setRawPrescanBBox(lsst.geom.Box2I())
        return ampCatalog
예제 #4
0
def makeCasuWfcam():
    path = os.path.join(getPackageDir("obs_ukirt"), "casuWfcam", "camera")

    for detNum, (gain, sat) in enumerate(zip(GAIN, SATURATION), 1):
        det = AmpInfoCatalog(AmpInfoTable.makeMinimalSchema())
        amp = det.addNew()
        amp.setName(str(detNum))
        amp.setBBox(Box2I())
        amp.setGain(gain)
        amp.setReadNoise(READNOISE)
        amp.setSaturation(sat)
        #        amp.setSuspectLevel(float("nan"))
        amp.setReadoutCorner(LL)
        amp.setLinearityType("none")
        amp.setHasRawInfo(False)

        det.writeFits(os.path.join(path, "%d.fits" % (detNum, )))
예제 #5
0
파일: filecam.py 프로젝트: rbliu/obs_file
    def _makeAmpInfoCatalog(self):
        """Construct an amplifier info catalog
        """
        # Fake amp of zero size.  Not needed unless ISR is run
        xDataExtent = 0
        yDataExtent = 0

        saturation = 65535

        schema = AmpInfoTable.makeMinimalSchema()

        ampCatalog = AmpInfoCatalog(schema)
        record = ampCatalog.addNew()
        ampX, ampY = (0, 0)
        record.setName("%d%d" % (ampX, ampY))

        if bool(ampY):
            record.setBBox(
                afwGeom.Box2I(
                    afwGeom.Point2I(ampX * xDataExtent, ampY * yDataExtent),
                    afwGeom.Extent2I(xDataExtent, yDataExtent),
                ))
        else:
            record.setBBox(
                afwGeom.Box2I(
                    afwGeom.Point2I(ampX * xDataExtent, ampY * yDataExtent),
                    afwGeom.Extent2I(xDataExtent, yDataExtent),
                ))

        readCorner = LL  # in raw frames; always LL because raws are in amp coords
        record.setReadoutCorner(readCorner)
        record.setGain(1.)
        record.setReadNoise(0.)
        record.setSaturation(saturation)
        record.setHasRawInfo(False)
        return ampCatalog
예제 #6
0
def makeCameraFromPath(cameraConfig, ampInfoPath, shortNameFunc):
    """!Make a Camera instance from a directory of ampInfo files

    The directory must contain one ampInfo fits file for each detector in cameraConfig.detectorList.
    The name of each ampInfo file must be shortNameFunc(fullDetectorName) + ".fits".

    @param[in] cameraConfig  an instance of CameraConfig
    @param[in] ampInfoPath  path to ampInfo data files
    @param[in] shortNameFunc  a function that converts a long detector name to a short one
    @return camera (an lsst.afw.cameraGeom.Camera)
    """
    ampInfoCatDict = dict()
    for detectorConfig in cameraConfig.detectorList.itervalues():
        shortName = shortNameFunc(detectorConfig.name)
        ampCatPath = os.path.join(ampInfoPath, shortName + ".fits")
        ampInfoCatalog = AmpInfoCatalog.readFits(ampCatPath)
        ampInfoCatDict[detectorConfig.name] = ampInfoCatalog

    return makeCameraFromCatalogs(cameraConfig, ampInfoCatDict)
예제 #7
0
def makeCameraFromPath(cameraConfig, ampInfoPath, shortNameFunc):
    """!Make a Camera instance from a directory of ampInfo files

    The directory must contain one ampInfo fits file for each detector in cameraConfig.detectorList.
    The name of each ampInfo file must be shortNameFunc(fullDetectorName) + ".fits".

    @param[in] cameraConfig  an instance of CameraConfig
    @param[in] ampInfoPath  path to ampInfo data files
    @param[in] shortNameFunc  a function that converts a long detector name to a short one
    @return camera (an lsst.afw.cameraGeom.Camera)
    """
    ampInfoCatDict = dict()
    for detectorConfig in cameraConfig.detectorList.itervalues():
        shortName = shortNameFunc(detectorConfig.name)
        ampCatPath = os.path.join(ampInfoPath, shortName + ".fits")
        ampInfoCatalog = AmpInfoCatalog.readFits(ampCatPath)
        ampInfoCatDict[detectorConfig.name] = ampInfoCatalog

    return makeCameraFromCatalogs(cameraConfig, ampInfoCatDict)
예제 #8
0
    def _makeAmpInfoCatalog(self):
        """Construct an amplifier info catalog
        """

        extended = 1024  # extended register
        x_overscan = 40  # number of overscan pixel in x
        saturation = 65535
        # Linearity correction is still under discussion, so this is a placeholder.
        linearityType = "PROPORTIONAL"
        linearityThreshold = 0
        linearityMax = saturation
        linearityCoeffs = [linearityThreshold, linearityMax]
        schema = AmpInfoTable.makeMinimalSchema()
        linThreshKey = schema.addField('linearityThreshold', type=float)
        linMaxKey = schema.addField('linearityMaximum', type=float)
        linUnitsKey = schema.addField('linearityUnits', type=str, size=9)
        # end placeholder
        self.ampInfoDict = {}
        ampCatalog = AmpInfoCatalog(schema)

        for ampY in range(1, 3):
            for ampX in range(1, 3):
                record = ampCatalog.addNew()
                record.setName("%d%d" % (ampX, ampY))
                print('Amp Name : %s, %s' % (ampX, ampY))

                if ((ampX == 1) & (ampY == 1)):
                    record.setBBox(
                        afwGeom.Box2I(
                            afwGeom.Point2I(0, 0),
                            afwGeom.Extent2I(extended, extended),
                        ))
                    record.setRawHorizontalOverscanBBox(
                        afwGeom.Box2I(
                            afwGeom.Point2I(1044, 0),
                            afwGeom.Extent2I(x_overscan, extended),
                        ))
                    record.setRawXYOffset(\
                        afwGeom.Extent2I(1084, 1024))
                    # bias region
                    record.setRawBBox(
                        afwGeom.Box2I(
                            afwGeom.Point2I(10, 0),
                            afwGeom.Extent2I(extended, extended),
                        ))
                    record.setRawDataBBox(
                        afwGeom.Box2I(
                            afwGeom.Point2I(10, 0),
                            afwGeom.Extent2I(extended, extended),
                        ))

                if ((ampX == 1) & (ampY == 2)):
                    record.setBBox(
                        afwGeom.Box2I(
                            afwGeom.Point2I(1024, 0),
                            afwGeom.Extent2I(extended, extended),
                        ))
                    record.setRawHorizontalOverscanBBox(
                        afwGeom.Box2I(
                            afwGeom.Point2I(1084, 0),
                            afwGeom.Extent2I(x_overscan, extended),
                        ))
                    record.setRawXYOffset(\
                        afwGeom.Extent2I(1084, 1024))
                    # bias region
                    record.setRawBBox(
                        afwGeom.Box2I(
                            afwGeom.Point2I(1134, 0),
                            afwGeom.Extent2I(extended, extended),
                        ))
                    record.setRawDataBBox(
                        afwGeom.Box2I(
                            afwGeom.Point2I(1134, 0),
                            afwGeom.Extent2I(extended, extended),
                        ))

                if ((ampX == 2) & (ampY == 1)):
                    record.setBBox(
                        afwGeom.Box2I(
                            afwGeom.Point2I(0, 1024),
                            afwGeom.Extent2I(extended, extended),
                        ))
                    record.setRawHorizontalOverscanBBox(
                        afwGeom.Box2I(
                            afwGeom.Point2I(1044, 1024),
                            afwGeom.Extent2I(x_overscan, extended),
                        ))
                    record.setRawXYOffset(\
                        afwGeom.Extent2I(1084, 1024))
                    # bias region
                    record.setRawBBox(
                        afwGeom.Box2I(
                            afwGeom.Point2I(10, 1024),
                            afwGeom.Extent2I(extended, extended),
                        ))
                    record.setRawDataBBox(
                        afwGeom.Box2I(
                            afwGeom.Point2I(10, 1024),
                            afwGeom.Extent2I(extended, extended),
                        ))

                if ((ampX == 2) & (ampY == 2)):
                    record.setBBox(
                        afwGeom.Box2I(
                            afwGeom.Point2I(1024, 1024),
                            afwGeom.Extent2I(extended, extended),
                        ))
                    record.setRawHorizontalOverscanBBox(
                        afwGeom.Box2I(
                            afwGeom.Point2I(1084, 1024),
                            afwGeom.Extent2I(x_overscan, extended),
                        ))
                    record.setRawXYOffset(\
                        afwGeom.Extent2I(1084, 1024))
                    # bias region
                    record.setRawBBox(
                        afwGeom.Box2I(
                            afwGeom.Point2I(1134, 1024),
                            afwGeom.Extent2I(extended, extended),
                        ))
                    record.setRawDataBBox(
                        afwGeom.Box2I(
                            afwGeom.Point2I(1134, 1024),
                            afwGeom.Extent2I(extended, extended),
                        ))

                readCorner = LL  # in raw frames; always LL because raws are in amp coords

                record.setReadoutCorner(readCorner)
                record.setGain(self.gain[(ampX, ampY)])
                record.setReadNoise(self.readNoise[(ampX, ampY)])
                record.setSaturation(saturation)
                record.setHasRawInfo(True)
                record.setRawPrescanBBox(afwGeom.Box2I())
                # linearity placeholder stuff
                record.setLinearityCoeffs(
                    [float(val) for val in linearityCoeffs])
                record.setLinearityType(linearityType)
                record.set(linThreshKey, float(linearityThreshold))
                record.set(linMaxKey, float(linearityMax))
                record.set(linUnitsKey, "DN")
        return ampCatalog
예제 #9
0
    def _makeAmpInfoCatalog(self, ccd):
        """Construct an amplifier info catalog
        """
        # Much of this will need to be filled in when we know it.
        assert len(ccd['amplifiers']) > 0
        amp = list(ccd['amplifiers'].values())[0]

        rawBBox = self._makeBBoxFromList(amp['rawBBox'])  # total in file
        xRawExtent, yRawExtent = rawBBox.getDimensions()

        from lsst.afw.table import LL, LR, UL, UR
        readCorners = dict(LL=LL, LR=LR, UL=UL, UR=UR)

        schema = AmpInfoTable.makeMinimalSchema()

        linThreshKey = schema.addField('linearityThreshold', type=float)
        linMaxKey = schema.addField('linearityMaximum', type=float)
        linUnitsKey = schema.addField('linearityUnits', type=str, size=9)
        hduKey = schema.addField('hdu', type=np.int32)
        # end placeholder
        self.ampInfoDict = {}
        ampCatalog = AmpInfoCatalog(schema)
        for name, amp in sorted(ccd['amplifiers'].items(),
                                key=lambda x: x[1]['hdu']):
            record = ampCatalog.addNew()
            record.setName(name)
            record.set(hduKey, amp['hdu'])

            ix, iy = amp['ixy']
            perAmpData = amp['perAmpData']
            if perAmpData:
                x0, y0 = 0, 0  # origin of data within each amp image
            else:
                x0, y0 = ix * xRawExtent, iy * yRawExtent

            rawDataBBox = self._makeBBoxFromList(
                amp['rawDataBBox'])  # Photosensitive area
            xDataExtent, yDataExtent = rawDataBBox.getDimensions()
            record.setBBox(
                afwGeom.BoxI(
                    afwGeom.PointI(ix * xDataExtent, iy * yDataExtent),
                    rawDataBBox.getDimensions()))

            rawBBox = self._makeBBoxFromList(amp['rawBBox'])
            rawBBox.shift(afwGeom.ExtentI(x0, y0))
            record.setRawBBox(rawBBox)

            rawDataBBox = self._makeBBoxFromList(amp['rawDataBBox'])
            rawDataBBox.shift(afwGeom.ExtentI(x0, y0))
            record.setRawDataBBox(rawDataBBox)

            rawSerialOverscanBBox = self._makeBBoxFromList(
                amp['rawSerialOverscanBBox'])
            rawSerialOverscanBBox.shift(afwGeom.ExtentI(x0, y0))
            record.setRawHorizontalOverscanBBox(rawSerialOverscanBBox)

            rawParallelOverscanBBox = self._makeBBoxFromList(
                amp['rawParallelOverscanBBox'])
            rawParallelOverscanBBox.shift(afwGeom.ExtentI(x0, y0))
            record.setRawVerticalOverscanBBox(rawParallelOverscanBBox)

            rawSerialPrescanBBox = self._makeBBoxFromList(
                amp['rawSerialPrescanBBox'])
            rawSerialPrescanBBox.shift(afwGeom.ExtentI(x0, y0))
            record.setRawPrescanBBox(rawSerialPrescanBBox)

            if perAmpData:
                record.setRawXYOffset(
                    afwGeom.Extent2I(ix * xRawExtent, iy * yRawExtent))
            else:
                record.setRawXYOffset(afwGeom.Extent2I(0, 0))

            record.setReadoutCorner(readCorners[amp['readCorner']])
            record.setGain(amp['gain'])
            record.setReadNoise(amp['readNoise'])
            record.setSaturation(amp['saturation'])
            record.setHasRawInfo(True)
            # flip data when assembling if needs be (e.g. data from the serial at the top of a CCD)
            flipX, flipY = amp.get("flipXY")

            record.setRawFlipX(flipX)
            record.setRawFlipY(flipY)
            # linearity placeholder stuff
            record.setLinearityCoeffs(
                [float(val) for val in amp['linearityCoeffs']])
            record.setLinearityType(amp['linearityType'])
            record.set(linThreshKey, float(amp['linearityThreshold']))
            record.set(linMaxKey, float(amp['linearityMax']))
            record.set(linUnitsKey, "DN")
        return ampCatalog
예제 #10
0
    def _makeAmpInfoCatalog(self):
        """Construct an amplifier info catalog
        """
        # Much of this will need to be filled in when we know it.
        xDataExtent = 512  # trimmed
        yDataExtent = 2002

        extended = 10  # extended register
        h_overscan = 22  # number of overscan in x
        v_overscan = 46  # number of overscan in y

        xRawExtent = extended + h_overscan + xDataExtent
        yRawExtent = v_overscan + yDataExtent  # no prescan in vertical

        saturation = 65535
        # Linearity correction is still under discussion, so this is a placeholder.
        linearityType = "PROPORTIONAL"
        linearityThreshold = 0
        linearityMax = saturation
        linearityCoeffs = [linearityThreshold, linearityMax]

        schema = AmpInfoTable.makeMinimalSchema()

        linThreshKey = schema.addField('linearityThreshold', type=float)
        linMaxKey = schema.addField('linearityMaximum', type=float)
        linUnitsKey = schema.addField('linearityUnits', type=str, size=9)
        # end placeholder
        self.ampInfoDict = {}
        ampCatalog = AmpInfoCatalog(schema)
        for ampY in (0, 1):
            for ampX in range(8):
                record = ampCatalog.addNew()
                record.setName("%d%d" % (ampX, ampY))

                if bool(ampY):
                    record.setBBox(
                        afwGeom.Box2I(
                            afwGeom.Point2I(ampX * xDataExtent,
                                            ampY * yDataExtent),
                            afwGeom.Extent2I(xDataExtent, yDataExtent),
                        ))
                else:
                    record.setBBox(
                        afwGeom.Box2I(
                            afwGeom.Point2I((7 - ampX) * xDataExtent,
                                            ampY * yDataExtent),
                            afwGeom.Extent2I(xDataExtent, yDataExtent),
                        ))

                readCorner = LL  # in raw frames; always LL because raws are in amp coords
                # bias region
                x0Bias = extended + xDataExtent
                y0Data = 0
                x0Data = extended

                record.setRawBBox(
                    afwGeom.Box2I(
                        afwGeom.Point2I(0, 0),
                        afwGeom.Extent2I(xRawExtent, yRawExtent),
                    ))
                record.setRawDataBBox(
                    afwGeom.Box2I(
                        afwGeom.Point2I(x0Data, y0Data),
                        afwGeom.Extent2I(xDataExtent, yDataExtent),
                    ))
                record.setRawHorizontalOverscanBBox(
                    afwGeom.Box2I(
                        afwGeom.Point2I(x0Bias, y0Data),
                        afwGeom.Extent2I(h_overscan, yDataExtent),
                    ))
                record.setRawVerticalOverscanBBox(
                    afwGeom.Box2I(
                        afwGeom.Point2I(x0Data, y0Data + yDataExtent),
                        afwGeom.Extent2I(xDataExtent, v_overscan),
                    ))
                record.setRawXYOffset(
                    afwGeom.Extent2I(ampX * xRawExtent, ampY * yRawExtent))
                record.setReadoutCorner(readCorner)
                record.setGain(self.gain[(ampX, ampY)])
                record.setReadNoise(self.readNoise[(ampX, ampY)])
                record.setSaturation(saturation)
                record.setHasRawInfo(True)
                record.setRawFlipX(bool(ampY))
                # flip data when assembling if in top of chip
                record.setRawFlipY(bool(ampY))
                record.setRawPrescanBBox(afwGeom.Box2I())
                # linearity placeholder stuff
                record.setLinearityCoeffs(
                    [float(val) for val in linearityCoeffs])
                record.setLinearityType(linearityType)
                record.set(linThreshKey, float(linearityThreshold))
                record.set(linMaxKey, float(linearityMax))
                record.set(linUnitsKey, "DN")
        return ampCatalog
예제 #11
0
    def _makeAmpInfoCatalog(self):
        """Construct an amplifier info catalog

        The LSSTSim S12 amplifiers are unusual in that they start with 4 pixels
        of usable bias region (which is used to set rawHOverscanBbox, despite the name),
        followed by the data. There is no other underscan or overscan.
        """
        xDataExtent = 509  # trimmed
        yDataExtent = 1000
        xBiasExtent = 4
        xRawExtent = xDataExtent + xBiasExtent
        yRawExtent = yDataExtent
        readNoise = 3.975  # amplifier read noise, in e-
        saturationLevel = 65535
        linearityType = NullLinearityType
        linearityCoeffs = [0, 0, 0, 0]

        schema = AmpInfoTable.makeMinimalSchema()

        self.ampInfoDict = {}
        ampCatalog = AmpInfoCatalog(schema)
        for ampX in (0, 1):
            for ampY in (0, 1):
                # amplifier gain (e-/ADU) and read noiuse (ADU/pixel) from lsstSim raw data
                # note that obs_test amp <ampX><ampY> = lsstSim amp C<ampY>,<ampX> (axes are swapped)
                gain = {
                    (0, 0): 1.7741,  # C0,0
                    (0, 1): 1.65881,  # C1,0
                    (1, 0): 1.74151,  # C0,1
                    (1, 1): 1.67073,  # C1,1
                }[(ampX, ampY)]
                readNoise = {
                    (0, 0): 3.97531706217237,  # C0,0
                    (0, 1): 4.08263755342685,  # C1,0
                    (1, 0): 4.02753931932633,  # C0,1
                    (1, 1): 4.1890610691135,  # C1,1
                }[(ampX, ampY)]
                record = ampCatalog.addNew()
                record.setName("%d%d" % (ampX, ampY))
                record.setBBox(
                    afwGeom.Box2I(
                        afwGeom.Point2I(ampX * xDataExtent,
                                        ampY * yDataExtent),
                        afwGeom.Extent2I(xDataExtent, yDataExtent),
                    ))

                x0Raw = ampX * xRawExtent
                y0Raw = ampY * yRawExtent

                # bias region (which is prescan, in this case) is before the data
                readCorner = LL
                x0Bias = x0Raw
                x0Data = x0Bias + xBiasExtent

                record.setRawBBox(
                    afwGeom.Box2I(
                        afwGeom.Point2I(x0Raw, y0Raw),
                        afwGeom.Extent2I(xRawExtent, yRawExtent),
                    ))
                record.setRawDataBBox(
                    afwGeom.Box2I(
                        afwGeom.Point2I(x0Data, y0Raw),
                        afwGeom.Extent2I(xDataExtent, yDataExtent),
                    ))
                record.setRawHorizontalOverscanBBox(
                    afwGeom.Box2I(
                        afwGeom.Point2I(x0Bias, y0Raw),
                        afwGeom.Extent2I(xBiasExtent, yRawExtent),
                    ))
                record.setRawXYOffset(afwGeom.Extent2I(x0Raw, y0Raw))
                record.setReadoutCorner(readCorner)
                record.setGain(gain)
                record.setReadNoise(readNoise)
                record.setSaturation(saturationLevel)
                record.setSuspectLevel(float("nan"))
                record.setLinearityCoeffs(
                    [float(val) for val in linearityCoeffs])
                record.setLinearityType(linearityType)
                record.setHasRawInfo(True)
                record.setRawFlipX(False)
                record.setRawFlipY(False)
                record.setRawVerticalOverscanBBox(
                    afwGeom.Box2I())  # no vertical overscan
                record.setRawPrescanBBox(
                    afwGeom.Box2I())  # no horizontal prescan
        return ampCatalog
예제 #12
0
    def _makeAmpInfoCatalog(self):
        """Construct an amplifier info catalog

        The LSSTSim S12 amplifiers are unusual in that they start with 4 pixels
        of usable bias region (which is used to set rawHOverscanBbox, despite the name),
        followed by the data. There is no other underscan or overscan.
        """
        xDataExtent = 509 # trimmed
        yDataExtent = 1000
        xBiasExtent = 4
        xRawExtent = xDataExtent + xBiasExtent
        yRawExtent = yDataExtent
        readNoise = 3.975 # amplifier read noise, in e-
        linearityType = "PROPORTIONAL"
        linearityThreshold = 0
        linearityMax = 65535
        linearityCoeffs = [linearityThreshold, linearityMax]

        schema = AmpInfoTable.makeMinimalSchema()

        linThreshKey = schema.addField('linearityThreshold', type=float)
        linMaxKey = schema.addField('linearityMaximum', type=float)
        linUnitsKey = schema.addField('linearityUnits', type=str, size=9)
        self.ampInfoDict = {}
        ampCatalog = AmpInfoCatalog(schema)
        for ampX in (0, 1):
            for ampY in (0, 1):
                # amplifier gain (e-/ADU) and read noiuse (ADU/pixel) from lsstSim raw data
                # note that obs_test amp <ampX><ampY> = lsstSim amp C<ampY>,<ampX> (axes are swapped)
                gain = {
                    (0, 0): 1.7741,     # C0,0
                    (0, 1): 1.65881,    # C1,0
                    (1, 0): 1.74151,    # C0,1
                    (1, 1): 1.67073,    # C1,1
                }[(ampX, ampY)]
                readNoise = {
                    (0, 0): 3.97531706217237,   # C0,0
                    (0, 1): 4.08263755342685,   # C1,0
                    (1, 0): 4.02753931932633,   # C0,1
                    (1, 1): 4.1890610691135,    # C1,1
                }[(ampX, ampY)]
                record = ampCatalog.addNew()
                record.setName("%d%d" % (ampX, ampY))
                record.setBBox(afwGeom.Box2I(
                    afwGeom.Point2I(ampX * xDataExtent, ampY * yDataExtent),
                    afwGeom.Extent2I(xDataExtent, yDataExtent),
                ))

                x0Raw = ampX * xRawExtent
                y0Raw = ampY * yRawExtent

                # bias region (which is prescan, in this case) is before the data
                readCorner = LL
                x0Bias = x0Raw
                x0Data = x0Bias + xBiasExtent

                record.setRawBBox(afwGeom.Box2I(
                    afwGeom.Point2I(x0Raw, y0Raw),
                    afwGeom.Extent2I(xRawExtent, yRawExtent),
                ))
                record.setRawDataBBox(afwGeom.Box2I(
                    afwGeom.Point2I(x0Data, y0Raw),
                    afwGeom.Extent2I(xDataExtent, yDataExtent),
                ))
                record.setRawHorizontalOverscanBBox(afwGeom.Box2I(
                    afwGeom.Point2I(x0Bias, y0Raw),
                    afwGeom.Extent2I(xBiasExtent, yRawExtent),
                ))
                record.setRawXYOffset(afwGeom.Extent2I(x0Raw, y0Raw))
                record.setReadoutCorner(readCorner)
                record.setGain(gain)
                record.setReadNoise(readNoise)
                record.setSaturation(linearityMax)
                record.setLinearityCoeffs([float(val) for val in linearityCoeffs])
                record.setLinearityType(linearityType)
                record.setHasRawInfo(True)
                record.setRawFlipX(False)
                record.setRawFlipY(False)
                record.setRawVerticalOverscanBBox(afwGeom.Box2I()) # no vertical overscan
                record.setRawPrescanBBox(afwGeom.Box2I()) # no horizontal prescan
                record.set(linThreshKey, float(linearityThreshold))
                record.set(linMaxKey, float(linearityMax))
                record.set(linUnitsKey, "DN")
        return ampCatalog