Source code for sofia_redux.instruments.flitecam.mkspecimg

# Licensed under a 3-clause BSD style license - see LICENSE.rst

from astropy import log
import numpy as np

from sofia_redux.instruments.forcast.hdmerge import hdmerge
from sofia_redux.spectroscopy.readflat import readflat
from sofia_redux.toolkit.image.adjust import rotate90
from sofia_redux.toolkit.utilities import date2seconds

__all__ = ['mkspecimg']


[docs] def mkspecimg(infiles, pair_subtract=True, flatfile=None, filenum=None): """ Rotate and pair-subtract spectral images. The process is: - Divide by the flat if provided. - If subtraction is desired, sort infiles by date-obs, then subtract all pairs in order. - Rotate the image 90 degrees counter-clockwise to align the spectral direction along the x-axis. - Propagate variance accordingly. Parameters ---------- infiles : `list` of fits.HDUList Input data. Should have FLUX, ERROR, and BADMASK extensions. pair_subtract : bool, optional If True, data will be subtracted in pairs, in order by time observed. If an odd number of files is specified, the last one will be dropped from the reduction. flatfile : str, optional Path to FITS file containing flat data to divide into the image. Should be in Spextool format, readable by `sofia_redux.spectroscopy.readflat.readflat`. filenum : list of int or str, optional List of file numbers corresponding to the input. Will be updated to match the order and pairs of file numbers corresponding to the output, and returned as a secondary output. Returns ------- outfiles : list of fits.HDUList Pair-subtracted spectral images. filenum : list of int or str, optional If an input filenum list is specified, the output is tuple(outfiles, filenum), where filenum is a list of file numbers matching the output order, if no pair subtraction is done. If pair subtraction was done, filenum is a list of lists of file numbers, where each element is the pair of input file numbers. """ # read flat data flat = None if flatfile is not None: flatdata = readflat(flatfile) if flatdata is None: raise ValueError(f'Could not read flat file {flatfile}') flat = flatdata['image'] log.info(f'Dividing by flat data in {flatfile}') # get times for sorting dateobs = [] for hdul in infiles: dt = hdul[0].header.get('DATE-OBS', default='3000-01-01T00:00:00') dateobs.append(date2seconds(str(dt))) idx = np.argsort(dateobs) if pair_subtract and len(infiles) == 1: log.warning('Only one file; turning off pair-subtraction.') pair_subtract = False if pair_subtract: iter = range(0, len(infiles), 2) else: iter = range(len(infiles)) outfiles = [] outfilenum = [] for i in iter: hdul = infiles[idx[i]] header = hdul[0].header flux = hdul['FLUX'].data var = hdul['ERROR'].data ** 2 mask = hdul['BADMASK'].data a_nod = str(header.get('NODBEAM', 'A')).strip().upper() # mask saturated pixels then delete mask flux[mask != 0] = np.nan del hdul['BADMASK'] # pair subtract if desired if pair_subtract: # drop orphaned files if i + 1 >= len(idx): log.warning(f"Mismatched pairs, dropping " f"{header.get('FILENAME')}") continue # get the B data b_hdul = infiles[idx[i + 1]] b_header = b_hdul[0].header b_flux = b_hdul['FLUX'].data b_var = b_hdul['ERROR'].data ** 2 b_mask = b_hdul['BADMASK'].data b_flux[b_mask != 0] = np.nan # subtract, propagate errors, merge headers flux -= b_flux var += b_var header = hdmerge([header, b_header]) # swap sign if "A" nod is really a B, to make dithers # line up correctly if a_nod != 'A': flux *= -1 # track file numbers if filenum is not None: outfilenum.append([filenum[idx[i]], filenum[idx[i + 1]]]) elif filenum is not None: outfilenum.append(filenum[idx[i]]) # rotate data to align spectral axis with x flux = rotate90(flux, 1) var = rotate90(var, 1) # divide flux by flat # don't propagate flat errors - they're systematic if flat is not None: flux = flux / flat var = var / flat ** 2 hdul['FLUX'].data = flux hdul['ERROR'].data = np.sqrt(var) hdul[0].header = header outfiles.append(hdul) if filenum is None: return outfiles else: return outfiles, outfilenum