Source code for sofia_redux.instruments.fifi_ls.get_resolution

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

import os

from astropy import log
from astropy.io import fits
import pandas

from sofia_redux.instruments import fifi_ls
from sofia_redux.toolkit.utilities import goodfile, hdinsert

__all__ = ['clear_resolution_cache', 'get_resolution_from_cache',
           'store_resolution_in_cache', 'get_resolution']

__resolution_cache = {}


[docs] def clear_resolution_cache(): """ Clear all data from the resolution cache. """ global __resolution_cache __resolution_cache = {}
[docs] def get_resolution_from_cache(resfile): """ Retrieves table from the resolution cache. Checks to see if the file still exists, can be read, and has not been altered since last time. If the file has changed, it will be deleted from the cache so that it can be re-read. Parameters ---------- resfile : str File path to the resolution file Returns ------- resolution : pandas.DataFrame Resolution table """ global __resolution_cache if resfile not in __resolution_cache: return if not goodfile(resfile): try: del __resolution_cache[resfile] except KeyError: # pragma: no cover pass return modtime = str(os.path.getmtime(resfile)) if modtime not in __resolution_cache.get(resfile, {}): return log.debug("Retrieving data from resolution file (%s) in cache" % resfile) return __resolution_cache.get(resfile, {}).get(modtime)
[docs] def store_resolution_in_cache(resfile, resolution): """ Store resolution data in the resolution cache. Parameters ---------- resfile : str File path to the resolution file resolution : pandas.DataFrame Resolution table """ global __resolution_cache log.debug("Storing data from resolution (%s) in cache" % resfile) __resolution_cache[resfile] = {} modtime = str(os.path.getmtime(resfile)) __resolution_cache[resfile][modtime] = resolution
[docs] def get_resolution(header, wmean=None, spatial=False): """ Retrieve expected spectral or spatial resolution. Requires spectral_resolution.txt file in fifi_ls/data/resolution. This file must have 4 columns: channel (b1, b2, or r), central wavelength (um), spectral resolution (um), and spatial resolution (FWHM, in arcsec). The header will be updated with the 'RESFILE' keyword specifying the name of the resolution file used. The procedure is: 1. Read resolution data from spectral_resolution.txt configuration file. 2. Match resolution to input data, using keywords CHANNEL, G_ORD_B, and G_WAVE_R or G_WAVE_B. 3. Return spectral or spatial resolution. Parameters ---------- header : fits.Header wmean : float, optional If set, use as the central wavelength for the data. If not set, will use keyword G_WAVE_B or G_WAVE_R as appropriate for the data. spatial : bool, optional If True, will return spatial resolution instead of spectral resolution Returns ------- float Expected resolution in um for spectral resolution and arcsec for spatial """ if not isinstance(header, fits.Header): msg = 'Invalid header' log.error(msg) raise ValueError(msg) resfile = os.path.join(os.path.dirname(fifi_ls.__file__), 'data', 'resolution', 'spectral_resolution.txt') if not goodfile(resfile, verbose=True): msg = "Cannot read resolution file: %s" % resfile log.error(msg) raise ValueError(msg) required_keywords = ['CHANNEL', 'G_ORD_B', 'G_WAVE_R', 'G_WAVE_B'] for key in required_keywords: if key not in header: msg = "Header missing %s keyword" % key log.error(msg) raise ValueError(msg) channel = header['CHANNEL'] b_order = str(header['G_ORD_B']).strip() if channel == 'BLUE': if b_order not in ['1', '2']: msg = "Invalid blue grating order (%s)" % b_order log.error(msg) raise ValueError(msg) ch = 'b%s' % b_order elif channel == 'RED': ch = 'r' else: ch = 'unknown' if ch == 'unknown': log.warning('Channel is unknown; setting resolution to default values') hdinsert(header, 'RESFILE', os.path.basename(resfile)) return 5.0 if spatial else 1000.0 # Get central wavelength if wmean is None: wmean = header['G_WAVE_R'] if ch == 'r' else header['G_WAVE_B'] try: wmean = float(wmean) except (TypeError, ValueError): msg = "Invalid wavelength mean: %s" % repr(wmean) log.error(msg) raise ValueError(msg) hdinsert(header, 'RESFILE', os.path.basename(resfile)) # Find appropriate resolution df = get_resolution_from_cache(resfile) if df is None: log.debug("Loading resolution file: %s" % resfile) names = ['chan', 'wavelength', 'res', 'mfwhm'] df = pandas.read_csv(resfile, comment='#', names=names, sep=r'\s+') store_resolution_in_cache(resfile, df) col = 'mfwhm' if spatial else 'res' resolution = df.loc[ (df[df['chan'] == ch].wavelength - wmean).abs().idxmin()][col] return float(resolution)