def ampBiasLevel(filepath): file = fits.open(filepath) exp = geom.Exposure() exp.image = file[1].data exp.header = file[0].header exp = geom.Exposure(exp) ampIms, osIms, _ = exp.splitImage(doTrim=False) return [np.median(amp) for amp in ampIms]
def imStats(im, asBias=False, rowTrim=(5, 5), colTrim=(5, 5), osColTrim=(3, 1)): exp = geom.Exposure(im) expTime = exp.expTime ampIms, osIms, _ = exp.splitImage() stats = [] ampRows = slice(rowTrim[0], None if rowTrim[-1] in {0, None} else -rowTrim[1]) ampCols = slice(colTrim[0], None if colTrim[-1] in {0, None} else -colTrim[1]) osRows = ampRows osCols = slice(osColTrim[0], None if osColTrim[-1] in {0, None} else -osColTrim[1]) for a_i in range(8): stats1 = ampStats(ampIms[a_i][ampRows, ampCols], osIms[a_i][osRows, osCols], exp.header, exptime=exp.header['EXPTIME'], asBias=asBias) stats1['amp'] = a_i stats.append(stats1) return expTime, ampIms, osIms, stats
def plotOffsetScans(fnames): import os.path for f in fnames: exp = geom.Exposure(f) m = exp.header['offset.ch0.2n'] r = exp.header['offset.ch0.2p'] expid = int(os.path.basename(f)[4:10], base=10) title = "%s\nexp %d ref=%s master=%s" % (f, expid, m, r) fim = exp.image plotAmpRows(fim, cols=np.arange(10, fim.shape[1] // 8 - 10), title=title)
def tuneOffsets(ccd=None, feeControl=None): amps = list(range(8)) feeControl.zeroOffsets(amps) im, fname = ccdFuncs.fullExposure('bias', ccd=ccd, feeControl=feeControl, nrows=300) exp = geom.Exposure(im) ampIms, osIms, _ = exp.splitImage(doTrim=False) means = [] for a_i in range(8): reg = osIms[a_i][20:-20][2:-2] means.append(reg.mean()) m, r = calcOffsets(1000, np.array(means)) print("applying master: %s" % (m)) print("applying refs : %s" % (r)) feeControl.setOffsets(amps, m, leg='n', doSave=False) feeControl.setOffsets(amps, r, leg='p', doSave=True) feeControl.setMode('offset') im, fname = ccdFuncs.fullExposure('bias', ccd=ccd, feeControl=feeControl, nrows=200) exp = geom.Exposure(im) ampIms, osIms, _ = exp.splitImage(doTrim=False) means = [] for a_i in range(8): reg = osIms[a_i][20:-20][2:-2] means.append(reg.mean()) print("final means: %s" % ' '.join(["%0.1f" % m for m in means]))
def serialOverscanStats(image, readRows=(0, 4300)): """Calculate serial overscan levels and noise for all amplifiers. Parameters ---------- image : `numpy.ndarray` Raw CCD Image Returns ------- stats : `pd.DataFrame` DataFrame with stats for each amplifier. """ exp = geom.Exposure() exp.image = image ampIms, osIms, _ = exp.splitImage() stats = [perAmpSerialOverScan(osIm[slice(*readRows)]) for osIm in osIms] return pd.DataFrame(stats, columns=['level', 'noise'])
def flatStats(f1name, f2name): """ Return stats from two compatible flats. To calculate the gain and noise: Take two identical flats. Do stats in a standard central region, say 100x100. Find the median signal level in this region and subtract the median signal in the overscan. The average of these two is the SIGNAL, S Subtract them Find the quartiles in this same region. For gaussian noise, the sigma is .741*(3rdqt - 1stqt), but we have taken a difference, so Sig1 = (0.741/sqrt(2))*(3rdqt - 1stqt) is the `sigma' in the illuminated part. Also calculate the 3-sigma clipped standard deviation and divide it by sqrt(2). This is Trusig1. In the overscan region, do the same Sig2 = (0.741/sqrt(2))*(3rdqt - 1stqt), This is the read noise in ADU. calculate Trusig2 likewise. Then Sig = sqrt (Sig1^2 - Sig2^2) is the shot noise in the signal, and Trusig = sqrt(Trusig1^2 - Trusig2^2) is the `real' sigma. Compare. Then the inverse gain G in e-/ADU is G = S/(Sig^2). Calculate for both the sigma based on quartiles and the one based on moments. The noise in electrons is Sig2*G. """ exp1 = geom.Exposure(f1name) exp2 = geom.Exposure(f2name) f1AmpIms, f1OsIms, _ = exp1.splitImage(doTrim=True) f2AmpIms, f2OsIms, _ = exp2.splitImage(doTrim=True) if (exp1.expType != 'flat' or exp2.expType != 'flat' or exp1.expTime != exp2.expTime): raise RuntimeError( "require matching flats (%s(%s) vs %s(%s)" % (exp1.expType, exp1.expTime, exp2.expType, exp2.expTime)) stats = [] diffAmpIms = [] diffOsIms = [] for a_i in range(8): stats1, ampIm1, osIm1 = ampDiffStats(f1AmpIms[a_i], f2AmpIms[a_i], f1OsIms[a_i], f2OsIms[a_i], exptime=exp1.expTime) stats1['amp'] = a_i stats.append(stats1) diffAmpIms.append(ampIm1) diffOsIms.append(osIm1) return diffAmpIms, diffOsIms, stats
def CTEStats(flist, bias, amps=None, useCols=None): ''' The algorithm is for either one flat or, better, a series of nominally identical flats prepared thus: adjust by adding a constant so that the median or mean OVERSCAN levels are the same median the frames pixel-by pixel and subtract a medianed bias, prepared the same way, and also adjusted additively so that the median of the overscan area is the same as the frame--thus the median of the overscan extents in the corrected frame is zero. let Ib be the median of the last illuminated row (for parallel CTE) or column (for serial), (the last with level ~like the rest of the illuminated area. let Ic1 be the median of the next row or column, and Ic2 the median of the one after that, Ic3 the one after that, .... Let Ic be the sum of the first few (3?) of these Then if Ic1 << Ib, the CTE is 1 - (Ic/Ib)/Npix Where Npix is the number of transfers to get to the edge you are investigating--512 for the serial CTE and 4xxx for the parallel. If Ic is not much less than Ib, the CTE is awful and it does not matter what you do. The last illuminated row or column is, of course, a property of the device, so determine it from the high flats and use the number for the low ones if it is unclear where it is for the low ones. ''' flatExps = [] allNormedOsCols = [] allNormedOsRows = [] allNormedCols = [] allNormedRows = [] allBiasOsRows = [] allBiasOsCols = [] allBiasRawAmps = [] colCTEs = [] rowCTEs = [] expType = None expTime = None rowCteRowSlice = slice(100, None) colCteRowSlice = slice(150, -60) colSlice = slice(None, None) # preload the files for f_i, fname in enumerate(flist): exp = geom.Exposure(fname) if f_i == 0: expTime = exp.expTime if exp.expType != 'flat': raise RuntimeError("must use flats") if exp.expTime != expTime: raise RuntimeError("require matching flats (%s(%s) vs %s(%s)" % (exp.header['IMAGETYP'], exp.header['EXPTIME'], expType, expTime)) flatExps.append(exp) print("%s %0.1f: %s" % (exp.expType, exp.expTime, fname)) print() print("#amp HCTE VCTE ampCol overCol ampRow overRow") if amps is None: amps = list(range(exp.namps)) biasexp = geom.Exposure(bias) for a_i in amps: # Load the bias parts once. biasOsRows = biasexp.overscanRowImage(a_i)[:, colSlice] biasOsCols = biasexp.overscanColImage(a_i)[colCteRowSlice, :] biasImg = biasexp.ampImage(a_i) allBiasOsRows.append(biasOsRows) allBiasOsCols.append(biasOsCols) allBiasRawAmps.append(biasImg) biasRowMed = np.median(biasOsRows) biasColMed = np.median(biasOsCols) ampOsCols = [] ampOsRows = [] ampRows = [] ampCols = [] for exp in flatExps: osRows = exp.overscanRowImage(a_i)[:, colSlice] osCols = exp.overscanColImage(a_i)[colCteRowSlice, :] ampImg = exp.ampImage(a_i) osRowMed = np.median(osRows) osColMed = np.median(osCols) ampOsCols.append((osCols - osColMed) - (biasOsCols - biasColMed)) ampOsRows.append((osRows - osRowMed) - (biasOsRows - biasRowMed)) ampCols.append((ampImg[colCteRowSlice, colSlice] - osColMed) - (biasImg[colCteRowSlice, colSlice] - biasColMed)) ampRows.append((ampImg[rowCteRowSlice, colSlice] - osRowMed) - (biasImg[rowCteRowSlice, colSlice] - biasRowMed)) normedOsCols = np.median(np.dstack(ampOsCols), axis=2) normedOsRows = np.median(np.dstack(ampOsRows), axis=2) normedCols = np.median(np.dstack(ampCols), axis=2) normedRows = np.median(np.dstack(ampRows), axis=2) allNormedOsCols.append(normedOsCols) allNormedOsRows.append(normedOsRows) allNormedCols.append(normedCols) allNormedRows.append(normedRows) if False: colCTE = 1 - (normedOsCols[:, :3].sum() / normedCols[:, -1].sum()) / ampImg.shape[1] rowCTE = 1 - (normedOsRows[:3, :].sum() / normedRows[-1, :].sum()) / ampImg.shape[0] elif useCols == 'b1': osCol = normedOsCols[:, 0] ampCol = normedCols[:, -1] osRow = normedOsRows[2, :] ampRow = np.mean(normedOsRows[0:2, :], axis=0) else: osCol = normedOsCols[:, 0] ampCol = normedCols[:, -1] osRow = normedOsRows[0, :] ampRow = normedRows[-1, :] colCTE = 1 - (np.mean(osCol * ampCol) / np.mean(ampCol * ampCol)) / ampImg.shape[1] rowCTE = 1 - (np.mean(osRow * ampRow) / np.mean(ampRow * ampRow)) / ampImg.shape[0] colCTEs.append(colCTE) rowCTEs.append(rowCTE) print("%d: %0.7f %0.7f %7.2f %7.3f %7.2f %7.3f" % (a_i, colCTE, rowCTE, np.mean(ampCol), np.mean(osCol), np.mean(ampRow), np.mean(osRow))) return (colCTEs, rowCTEs, allNormedCols, allNormedOsCols, allNormedRows, allNormedOsRows, allBiasOsCols, allBiasOsRows)