Beispiel #1
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,)))
Beispiel #2
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
Beispiel #3
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, )))
Beispiel #4
0
    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
    def setUp(self):
        """Constructs a CCD with two amplifiers and prepares for ISR"""
        np.random.seed(12345)
        baseValue = 100.0
        gain = 1.0
        readNoise = 123456789.0
        saturation = 987654321.0
        height = 234
        imageSize = Extent2I(123, height)
        overscanSize = Extent2I(16, height)
        self.sigma = 1.234

        # Set up the various regions
        overscan1 = Box2I(Point2I(0, 0), overscanSize)
        image1 = Box2I(Point2I(overscanSize[0], 0), imageSize)
        image2 = Box2I(Point2I(overscanSize[0] + imageSize[0], 0), imageSize)
        overscan2 = Box2I(Point2I(overscanSize[0] + 2*imageSize[0], 0), overscanSize)

        leftBox = Box2I(overscan1.getMin(), Extent2I(overscan1.getWidth() + image1.getWidth(), height))
        rightBox = Box2I(image2.getMin(), Extent2I(image2.getWidth() + overscan2.getWidth(), height))

        target1 = Box2I(Point2I(0, 0), imageSize)
        target2 = Box2I(Point2I(image1.getWidth(), 0), imageSize)

        # Set the pixels
        exposure = ExposureF(Box2I(Point2I(0, 0), Extent2I(imageSize[0]*2 + overscanSize[0]*2, height)))
        yy = np.arange(0, height, 1, dtype=np.float32)
        leftImage = ExposureF(exposure, leftBox)
        leftImage.image.array[:] = baseValue + yy[:, np.newaxis]
        rightImage = ExposureF(exposure, rightBox)
        rightImage.image.array[:] = baseValue - yy[:, np.newaxis]

        leftOverscan = ExposureF(exposure, overscan1)
        leftOverscan.image.array += np.random.normal(0.0, self.sigma, leftOverscan.image.array.shape)
        rightOverscan = ExposureF(exposure, overscan2)
        rightOverscan.image.array += np.random.normal(0.0, self.sigma, leftOverscan.image.array.shape)
        exposure.mask.array[:] = 0.0
        exposure.variance.array[:] = np.nan

        # Construct the detectors
        amps = AmpInfoCatalog(AmpInfoTable.makeMinimalSchema())
        makeAmplifier(amps, "left", target1, image1, overscan1, gain, readNoise, saturation)
        makeAmplifier(amps, "right", target2, image2, overscan2, gain, readNoise, saturation)
        ccdBox = Box2I(Point2I(0, 0), Extent2I(image1.getWidth() + image2.getWidth(), height))
        ccd = Detector("detector", 1, SCIENCE, "det1", ccdBox, amps, Orientation(), Extent2D(1.0, 1.0), {})
        exposure.setDetector(ccd)
        header = PropertyList()
        header.add("EXPTIME", 0.0)
        exposure.getInfo().setVisitInfo(VisitInfo(header))

        self.exposure = exposure
        self.config = IsrTask.ConfigClass()

        # Disable everything we don't care about
        self.config.doBias = False
        self.config.doDark = False
        self.config.doFlat = False
        self.config.doFringe = False
        self.config.doDefect = False
        self.config.doAddDistortionModel = False
        self.config.doWrite = False
        self.config.expectWcs = False
        self.config.doLinearize = False
        self.config.doCrosstalk = False
        self.config.doBrighterFatter = False
        self.config.doAttachTransmissionCurve = False

        # Set the things that match our test setup
        self.config.overscanFitType = "CHEB"
        self.config.overscanOrder = 1
        self.config.doEmpiricalReadNoise = True

        self.task = IsrTask(config=self.config)
Beispiel #6
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
Beispiel #7
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
Beispiel #8
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
    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
    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