Source code for gdt.missions.maxi.gsc.response

# CONTAINS TECHNICAL DATA/COMPUTER SOFTWARE DELIVERED TO THE U.S. GOVERNMENT 
# WITH UNLIMITED RIGHTS
#
# Grant No.: 80NSSC21K0651
# Grantee Name: Universities Space Research Association
# Grantee Address: 425 3rd Street SW, Suite 950, Washington DC 20024
#
# Copyright 2024 by Universities Space Research Association (USRA). All rights 
# reserved.
#
# Developed by: Adam Goldstein
#               Universities Space Research Association
#               Science and Technology Institute
#               https://sti.usra.edu
#
# This work is a derivative of the Gamma-ray Data Tools (GDT), including the 
# Core and Fermi packages, originally developed by the following:
#
#     William Cleveland and Adam Goldstein
#     Universities Space Research Association
#     Science and Technology Institute
#     https://sti.usra.edu
#     
#     Daniel Kocevski
#     National Aeronautics and Space Administration (NASA)
#     Marshall Space Flight Center
#     Astrophysics Branch (ST-12)
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not 
# use this file except in compliance with the License. You may obtain a copy of 
# the License at
#
#    http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software 
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 
# License for the specific language governing permissions and limitations under 
# the License.

import os
import astropy.io.fits as fits
import numpy as np

from gdt.core.response import Rsp
from gdt.core.data_primitives import Bins, Ebounds, ResponseMatrix
from gdt.core.file import FitsFileContextManager
from .detectors import GscDetectors
from .headers import ArfHeaders, RmfHeaders
from ..time import *

__all__ = ['GscArf', 'GscRmf', 'GscRsp']

[docs]class GscArf(FitsFileContextManager): """A MAXI GSC Ancillary Response File (ARF) """ def __init__(self): self._headers = None self._arf_803 = None self._arf_854 = None self._detector = None @property def detector(self): """(str): The detector name""" return self._detector
[docs] def get_arf(self, hv): """Retrieve the ARF Args: hv (int): The HV bit corresponding to the ARF to retrieve. Valid values are 803 and 854. Returns: (:class:`~gdt.core.data_primitives.Bins`) """ if hv == 803: return self._arf_803 elif hv == 854: return self._arf_854 else: raise ValueError('hv must be either 803 or 854')
[docs] @classmethod def open(cls, file_path, **kwargs): """Read an ARF from disk. Args: file_path (str): The file path Returns: (:class:`GscArf`) """ obj = super().open(file_path, **kwargs) hdrs = [hdu.header for hdu in obj.hdulist] obj._headers = ArfHeaders.from_headers(hdrs) obj._arf_803 = Bins(obj.column(1, 'SPECRESP'), obj.column(1, 'ENERG_LO'), obj.column(1, 'ENERG_HI')) obj._arf_854 = Bins(obj.column(2, 'SPECRESP'), obj.column(2, 'ENERG_LO'), obj.column(2, 'ENERG_HI')) obj._detector = GscDetectors.from_full_name(hdrs[1]['INSTRUME']).name obj.close() return obj
[docs]class GscRmf(Rsp): """A MAXI GSC Response Matrix File (RMF) """
[docs] def apply_arf(self, arf_obj): """Apply an ARF to the RMF and return a complete detector response. Args: arf_obj (:class:`GscArf`): The ARF object Returns: (:class:`GscRsp`) """ if 'hv803' in self.filename: arf = arf_obj.get_arf(803) elif 'hv854' in self.filename: arf = arf_obj.get_arf(854) else: raise RuntimeError('Unkown RMF type') drm = ResponseMatrix(self.drm.matrix * arf.counts[np.newaxis, :], self.drm.photon_bins.low_edges(), self.drm.photon_bins.high_edges(), self.drm.ebounds.low_edges(), self.drm.ebounds.high_edges()) fname = '.'.join(self.filename.split('.')[:-1]) + '.rsp' obj = GscRsp.from_data(drm, filename=fname, headers=self.headers, detector=self.detector, start_time=self.tstart, stop_time=self.tstop) return obj
[docs] @classmethod def open(cls, file_path, **kwargs): """Read a RMF from disk. Args: file_path (str): The file path Returns: (:class:`GscRmf`) """ obj = super().open(file_path, **kwargs) hdrs = [hdu.header for hdu in obj.hdulist] headers = RmfHeaders.from_headers(hdrs) det = GscDetectors.from_full_name(hdrs[0]['INSTRUME']) matrix = obj._decompress_drm(obj.column(1, 'MATRIX'), hdrs[1]['NAXIS2'], hdrs[1]['DETCHANS'], obj.column(1, 'F_CHAN'), obj.column(1, 'N_CHAN')) drm = ResponseMatrix(matrix, obj.column(1, 'ENERG_LO'), obj.column(1, 'ENERG_HI'), obj.column(2, 'E_MIN'), obj.column(2, 'E_MAX')) obj.close() obj = cls.from_data(drm, filename=obj.filename, headers=headers, detector=det.name, start_time=0.0, stop_time=0.0) return obj
@staticmethod def _decompress_drm(matrix, num_photon_bins, num_channels, _fchan, _nchan): """Decompresses a DRM using the standard F_CHAN, N_CHAN, and N_GRP keywords. Args: drm_data (np.recarray): The DRM data Returns: (np.array) """ # The format of the compress matrix is a series of groups, for each # energy bin, of channels with non-zero values. # fchan stands for the first channel of each of these groups # and nchan for the number of channels in the group group. # Each row in the matrix is a 1D list consisting on the contatenated # values of all groups for a given energy bin # Note that in FITS the first index is 1 drm = np.zeros((num_photon_bins, num_channels)) for fchan, nchan, effective_area, drm_row \ in zip(_fchan, _nchan, matrix, drm): channel_offset = 0 start_idx = fchan - 1 end_idx = start_idx + nchan drm_row[start_idx:end_idx] = \ effective_area[channel_offset:channel_offset + nchan] channel_offset += nchan return drm
[docs]class GscRsp(Rsp): """Class for MAXI GSC response files """ pass