# 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 numpy as np
from astropy.coordinates import SkyCoord
import astropy.units as u
from gdt.core.detector import Detectors
__all__ = ['GscDetectors', 'GscFov']
[docs]
class GscFov(Detectors):
"""The MAXI GSC detector fields of view for the two GSC instruments:
GSC-H (Horizontal) and GSC-Z (Zenith). The FOVs each are
~160 degrees x ~3 degrees.
.. rubric:: Attributes Summary
.. autosummary::
azimuth
elevation
fov
full_name
number
zenith
.. rubric:: Methods Summary
.. autosummary::
from_full_name
from_num
from_str
pointing
skycoord
.. rubric:: Attributes Documentation
.. autoattribute:: azimuth
.. autoattribute:: elevation
.. autoattribute:: fov
.. autoattribute:: full_name
.. autoattribute:: number
.. autoattribute:: zenith
.. rubric:: Methods Documentation
.. automethod:: from_full_name
.. automethod:: from_num
.. automethod:: from_str
.. automethod:: pointing
.. automethod:: skycoord
"""
H = ('GSC-H', 0, 0.0 * u.deg, 90.0 * u.deg, [(280.0, 91.5), (80.0, 91.5),
(80.0, 88.5), (280.0, 88.5),
(280.0, 91.5)])
Z = ('GSC-Z', 1, 0.0 * u.deg, 180.0 * u.deg, [(268.5, 100.0), (91.5, 100.0),
(88.5, 100.0), (271.5, 100.0),
(268.5, 100.0)])
def __init__(self, full_name, number, azimuth, zenith, fov_box):
super().__init__(full_name, number, azimuth, zenith)
self._fov_box = fov_box
@property
def fov(self):
"""(list of tuples): The bounding box of the FOV"""
return self._fov_box
[docs]
def skycoord(self, frame):
"""Creates a polygon in the MAXI frame based on the bounding box of the
FOV. The last coordinate represents the center of the FOV, while the
remaining coordinates (0, N-1) represent the closed polygon of the FOV.
Args:
frame (MaxiFrame): The MAXI coordinate frame
Returns:
(astropy.coordinate.SkyCoord)
"""
fov = self.fov
# number of points along each edge of the box
num_points = [101, 3, 101, 3]
segs = []
for i in range(4):
arc_fracs = np.linspace(0.0, 1.0, num_points[i])
coord1 = SkyCoord(fov[i][0], 90.0-fov[i][1], unit='deg')
coord2 = SkyCoord(fov[i+1][0], 90.0-fov[i+1][1], unit='deg')
segs.append( point_on_arc(coord1, coord2, arc_fracs) )
az = np.concatenate([seg.ra for seg in segs])
el = np.concatenate([seg.dec for seg in segs])
# include the center of the FOV as the last coordinate
az = np.append(az, self.azimuth)
el = np.append(el, self.elevation)
return SkyCoord(az, el, frame=frame, unit='deg')
# mark: FIXME The pointings transformed from the quaternions in the teldef files
# do not appear to correctly rotate into J2000. found no documentation so far
# on how they should be used.
[docs]
class GscDetectors(Detectors):
"""The MAXI GSC detectors.
.. rubric:: Attributes Summary
.. autosummary::
azimuth
elevation
full_name
number
zenith
.. rubric:: Methods Summary
.. autosummary::
from_full_name
from_num
from_str
h_detectors
is_h_detector
is_z_detector
pointing
skycoord
z_detectors
.. rubric:: Attributes Documentation
.. autoattribute:: azimuth
.. autoattribute:: elevation
.. autoattribute:: full_name
.. autoattribute:: number
.. autoattribute:: zenith
.. rubric:: Methods Documentation
.. automethod:: from_full_name
.. automethod:: from_num
.. automethod:: from_str
.. automethod:: h_detectors
.. automethod:: is_h_detector
.. automethod:: is_z_detector
.. automethod:: pointing
.. automethod:: skycoord
.. automethod:: z_detectors
"""
HA0 = ('GSC_0', 0, None, None)
HA1 = ('GSC_1', 1, None, None)
HA2 = ('GSC_2', 2, None, None)
HB0 = ('GSC_6', 6, None, None)
HB1 = ('GSC_7', 7, None, None)
HB2 = ('GSC_8', 8, None, None)
ZA0 = ('GSC_3', 3, None, None)
ZA1 = ('GSC_4', 4, None, None)
ZA2 = ('GSC_5', 5, None, None)
ZB0 = ('GSC_9', 9, None, None)
ZB1 = ('GSC_A', 10, None, None)
ZB2 = ('GSC_B', 11, None, None)
def __init__(self, full_name, number, azimuth, zenith):
super().__init__(full_name, number, azimuth, zenith)
[docs]
@classmethod
def h_detectors(cls):
"""Get all detectors that are in the horizontal direction.
Returns:
(list of :class:`GscDetectors`)
"""
return [x for x in cls if x.is_h_detector()]
[docs]
@classmethod
def z_detectors(cls):
"""Get all detectors that are in the zenith direction.
Returns:
(list of :class:`GscDetectors`)
"""
return [x for x in cls if x.is_z_detector()]
[docs]
def is_h_detector(self):
"""Check if detector is a horizontal detector.
Returns:
(bool)
"""
return self.name[0] == 'H'
[docs]
def is_z_detector(self):
"""Check if detector is a zenith detector.
Returns:
(bool)
"""
return self.name[0] == 'Z'
def point_on_arc(coord1, coord2, frac):
"""Given two bounding coordinates of the segment of a great circle, return
a point that is ``frac`` fraction of the way along the path between
``coord1`` and ``coord2``.
Args:
coord1 (astropy.coordinates.SkyCoord): The coordinate at the start of
the bounding path
coord2 (astropy.coordinates.SkyCoord): The coordinate at the end of
the bounding path
frac (float or np.array): The fractional distance(s) along the path
Returns:
(astropy.coordinates.SkyCoord)
"""
lon1, lat1 = coord1.ra.to('rad').value, coord1.dec.to('rad').value
lon2, lat2 = coord2.ra.to('rad').value, coord2.dec.to('rad').value
delta = coord1.separation(coord2).to('rad').value
a = np.sin( (1.0 - frac) * delta) / np.sin(delta)
b = np.sin(frac * delta) / np.sin(delta)
x = a * np.cos(lat1) * np.cos(lon1) + b * np.cos(lat2) * np.cos(lon2)
y = a * np.cos(lat1) * np.sin(lon1) + b * np.cos(lat2) * np.sin(lon2)
z = a * np.sin(lat1) + b * np.sin(lat2)
lat3 = np.arctan2(z, (x**2 + y**2)**0.5)
lon3 = np.arctan2(y, x)
return SkyCoord(lon3, lat3, unit='rad')