def test_amp_ids(self): """Test auto-detection of amp names""" hdr = dict( CCDSECA=self.header['CCDSECA'], CCDSECB=self.header['CCDSECB'], CCDSECC=self.header['CCDSECC'], CCDSECD=self.header['CCDSECD'], ) self.assertEqual(get_amp_ids(hdr), ['A', 'B', 'C', 'D']) hdr = dict( CCDSEC1=self.header['CCDSECA'], CCDSEC2=self.header['CCDSECB'], CCDSEC3=self.header['CCDSECC'], CCDSEC4=self.header['CCDSECD'], ) self.assertEqual(get_amp_ids(hdr), ['1', '2', '3', '4']) hdr = dict() with self.assertRaises(KeyError): get_amp_ids(hdr)
def ampregion(image): """ Get the pixel boundary regions for amps Args: image: desispec.image.Image object """ pixboundary = [] for kk in get_amp_ids(image.meta): # A-D or 1-4 #- get the amp region in pix ampboundary = parse_sec_keyword(image.meta["CCDSEC" + kk]) pixboundary.append(ampboundary) return pixboundary
def cal_rdnoise(hdul, camera, ccd_calibration_filename, use_overscan_row, overscan_per_row, use_active): if True: hdul_this = hdul[camera] header = hdul_this.header rawimage = hdul_this.data # works! preproc.preproc(hdul['B0'].data,hdul['B0'].header,h1) amp_ids = preproc.get_amp_ids(header) #- Output arrays ny = 0 nx = 0 for amp in amp_ids: yy, xx = parse_sec_keyword(header['CCDSEC%s' % amp]) ny = max(ny, yy.stop) nx = max(nx, xx.stop) image = np.zeros((ny, nx)) readnoise = np.zeros_like(image) nogain = False cfinder = None if ccd_calibration_filename is not False: cfinder = CalibFinder([header, primary_header], yaml_file=ccd_calibration_filename) for amp in amp_ids: # Grab the sections ov_col = parse_sec_keyword(header['BIASSEC' + amp]) ov_row = parse_sec_keyword(header['ORSEC' + amp]) if use_active: if amp == 'A' or amp == 'D': ov_col2 = np.s_[ov_col[1].start:ov_col[1].stop, 0:100] else: ov_col2 = np.s_[ov_col[1].start:ov_col[1].stop, 0:100] import pdb pdb.set_trace() if 'ORSEC' + amp in header.keys(): ov_row = parse_sec_keyword(header['ORSEC' + amp]) elif use_overscan_row: log.error( 'No ORSEC{} keyword; not using overscan_row'.format(amp)) use_overscan_row = False if nogain: gain = 1. else: #- Initial teststand data may be missing GAIN* keywords; don't crash if 'GAIN' + amp in header: gain = header['GAIN' + amp] #- gain = electrons / ADU else: if cfinder and cfinder.haskey('GAIN' + amp): gain = float(cfinder.value('GAIN' + amp)) log.info( 'Using GAIN{}={} from calibration data'.format( amp, gain)) else: gain = 1.0 log.warning( 'Missing keyword GAIN{} in header and nothing in calib data; using {}' .format(amp, gain)) #- Add saturation level if 'SATURLEV' + amp in header: saturlev = header['SATURLEV' + amp] # in electrons else: if cfinder and cfinder.haskey('SATURLEV' + amp): saturlev = float(cfinder.value('SATURLEV' + amp)) log.info( 'Using SATURLEV{}={} from calibration data'.format( amp, saturlev)) else: saturlev = 200000 log.warning( 'Missing keyword SATURLEV{} in header and nothing in calib data; using 200000' .format(amp, saturlev)) # Generate the overscan images raw_overscan_col = rawimage[ov_col].copy() # 2064*64 if use_overscan_row: raw_overscan_row = rawimage[ov_row].copy() # 32*2057 overscan_row = np.zeros_like(raw_overscan_row) # Remove overscan_col from overscan_row raw_overscan_squared = rawimage[ov_row[0], ov_col[1]].copy() # 32*64 for row in range(raw_overscan_row.shape[0]): o, r = _overscan(raw_overscan_squared[row]) overscan_row[row] = raw_overscan_row[row] - o # Now remove the overscan_col nrows = raw_overscan_col.shape[0] log.info("nrows in overscan=%d" % nrows) overscan_col = np.zeros(nrows) rdnoise = np.zeros(nrows) if (cfinder and cfinder.haskey('OVERSCAN' + amp) and cfinder.value("OVERSCAN" + amp).upper() == "PER_ROW") or overscan_per_row: log.info( "Subtracting overscan per row for amplifier %s of camera %s" % (amp, camera)) for j in range(nrows): if np.isnan(np.sum(overscan_col[j])): log.warning( "NaN values in row %d of overscan of amplifier %s of camera %s" % (j, amp, camera)) continue o, r = _overscan(raw_overscan_col[j]) #log.info("%d %f %f"%(j,o,r)) overscan_col[j] = o rdnoise[j] = r else: log.info( "Subtracting average overscan for amplifier %s of camera %s" % (amp, camera)) o, r = _overscan(raw_overscan_col) overscan_col += o rdnoise += r rdnoise *= gain median_rdnoise = np.median(rdnoise) median_overscan = np.median(overscan_col) return rdnoise, median_rdnoise
def bias_stats(camera='z3', use_overscan_row=True): """ Measure stats on bias-subtracted frames Args: camera: Returns: """ dpath = '/home/xavier/DESI/DESI_SCRATCH/minisv2/data/20200304/' bias_files = glob.glob(dpath + '000*/desi-*.fz') bias_files.sort() # Load bias images hdul = fits.open(bias_files[0]) hdu = hdul[camera.upper()] smnum = hdu.header['SPECID'] new_bias_file = os.path.join(dpath, 'bias_{}.fits'.format(camera)) new_bias = fits.open(new_bias_file)[0].data old_bias_file = os.path.join( os.getenv('DESI_SPECTRO_CALIB'), 'ccd', 'bias-sm{}-{}-20191021.fits'.format(smnum, camera[0])) old_bias = fits.open(old_bias_file)[0].data calib_file = os.path.join(os.getenv('DESI_SPECTRO_CALIB'), 'spec', 'sm{}'.format(smnum), 'sm{}-{}.yaml'.format(smnum, camera[0])) # Loop and process stat_tbl = None for ss, bias_file in enumerate(bias_files): hdul = fits.open(bias_file) # Process tdict = dict(file=[os.path.basename(bias_file)]) lbias = 'new' for lbias, bimg in zip(['new', 'old'], [new_bias, old_bias]): img = preproc.preproc(hdu.data, hdu.header, hdul[0].header, pixflat=False, mask=False, nocrosstalk=True, ccd_calibration_filename=calib_file, use_overscan_row=use_overscan_row, dark=False, flag_savgol=False, bias_img=bimg) # Stats amp_ids = preproc.get_amp_ids(hdu.header) for amp in amp_ids: kk = preproc.parse_sec_keyword(hdu.header['CCDSEC' + amp]) zero, rms = preproc._overscan(img.pix[kk]) tdict[lbias + amp + '_zero'] = [zero] tdict[lbias + amp + '_rms'] = [rms] # Add to Table if stat_tbl is None: stat_tbl = Table(tdict) else: stat_tbl.add_row(tdict) # Write if use_overscan_row: root = '_' else: root = '_norow_' outbase = 'bias_stats{}{}.fits'.format(root, camera) outfile = os.path.join(dpath, 'Stats', outbase) stat_tbl.write(outfile, overwrite=True) print("Wrote: {}".format(outfile))
def fiducialregion(frame, psf): """ Get the fiducial amplifier regions on the CCD pixel to fiber by wavelength space Args: frame: desispec.frame.Frame object psf: desispec.psf.PSF like object """ startspec = 0 #- will be None if don't have fibers on the right of the CCD. endspec = 499 #- will be None if don't have fibers on the right of the CCD startwave0 = 0 #- lower index for the starting fiber startwave1 = 0 #- lower index for the last fiber for the amp region endwave0 = frame.wave.shape[0] #- upper index for the starting fiber endwave1 = frame.wave.shape[ 0] #- upper index for the last fiber for that amp pixboundary = [] fidboundary = [] #- Adding the min, max boundary individually for the benefit of dumping to yaml. leftmax = 499 #- for amp 1 and 3 rightmin = 0 #- for amp 2 and 4 bottommax = frame.wave.shape[0] #- for amp 1 and 2 topmin = 0 #- for amp 3 and 4 #- Loop over each amp for kk in get_amp_ids(frame.meta): # A-D or 1-4 #- get the amp region in pix ampboundary = parse_sec_keyword(frame.meta["CCDSEC" + kk]) pixboundary.append(ampboundary) for ispec in range(frame.flux.shape[0]): if np.all(psf.x(ispec) > ampboundary[1].start): startspec = ispec #-cutting off wavelenth boundaries from startspec yy = psf.y(ispec, frame.wave) k = np.where(yy > ampboundary[0].start)[0] startwave0 = k[0] yy = psf.y(ispec, frame.wave) k = np.where(yy < ampboundary[0].stop)[0] endwave0 = k[-1] break else: startspec = None startwave0 = None endwave0 = None if startspec is not None: for ispec in range(frame.flux.shape[0])[::-1]: if np.all(psf.x(ispec) < ampboundary[1].stop): endspec = ispec #-cutting off wavelenth boundaries from startspec yy = psf.y(ispec, frame.wave) k = np.where(yy > ampboundary[0].start)[0] startwave1 = k[0] yy = psf.y(ispec, frame.wave) k = np.where(yy < ampboundary[0].stop)[0] endwave1 = k[-1] break else: endspec = None startwave1 = None endwave1 = None if startwave0 is not None and startwave1 is not None: startwave = max(startwave0, startwave1) else: startwave = None if endwave0 is not None and endwave1 is not None: endwave = min(endwave0, endwave1) else: endwave = None if endspec is not None: #endspec+=1 #- last entry exclusive in slice, so add 1 #endwave+=1 if endspec < leftmax: leftmax = endspec if startspec > rightmin: rightmin = startspec if endwave < bottommax: bottommax = endwave if startwave > topmin: topmin = startwave else: rightmin = 0 #- Only if no spec in right side of CCD. passing 0 to encertain valid data type. Nontype throws a type error in yaml.dump. #fiducialb=(slice(startspec,endspec,None),slice(startwave,endwave,None)) #- Note: y,x --> spec, wavelength #fidboundary.append(fiducialb) #- return pixboundary,fidboundary return leftmax, rightmin, bottommax, topmin