import numpy as np
from numpy.typing import NDArray
import astropy.units as u
from astropy.coordinates import SkyCoord
from astropy.time import Time
from astropy.units import Quantity
from sunpy.time import TimeRange
from stixpy.coordinates.frames import STIXImaging
from stixpy.product.product import L1Product
THERMAL_INDEX_MAP = np.array(["None", "Minor", "Small", "Moderate", "Major"])
FLUX_INDEX_MAP = np.array(["None", "Weak", "Significant"])
LOCATION_INDEX_MAP = np.array(["None", "Previous", "New"])
TM_PROCESSING_STATUS = {
0x01: "Imaging Started",
0x02: "Imaging Ended",
0x04: "Spectroscopy Started",
0x08: "Spectroscopy Ended",
0x10: "Publication Started",
0x20: "Publication Ended",
0x40: "Cancelled",
0x80: "New Entry",
}
__all__ = ["QuickLookProduct", "QLLightCurve", "QLBackground", "QLVariance", "QLFlareFlag", "QLLightCurve"]
[docs]
class QuickLookProduct(L1Product):
"""
Basic science data class
"""
@property
def time(self) -> Time:
return self.data["time"]
@property
def exposure_time(self) -> Quantity[u.s]:
return self.data["timedel"].to(u.s)
@property
def time_range(self) -> TimeRange:
"""
A `sunpy.time.TimeRange` for the data.
"""
return TimeRange(self.time[0] - self.exposure_time[0] / 2, self.time[-1] + self.exposure_time[-1] / 2)
[docs]
class QLLightCurve(QuickLookProduct):
"""
Quicklook Lightcurves nominal in 5 energy bins every 4s
"""
[docs]
@classmethod
def is_datasource_for(cls, *, meta, **kwargs):
"""Determines if meta data match"""
service_subservice_ssid = tuple(meta[name] for name in ["STYPE", "SSTYPE", "SSID"])
level = meta["level"]
if service_subservice_ssid == (21, 6, 30) and level == "L1":
return True
def __repr__(self):
return f"{self.__class__.__name__}\n {self.time_range}"
[docs]
class QLBackground(QuickLookProduct):
"""
Quicklook Background nominal in 5 energy bins every 8s
"""
[docs]
@classmethod
def is_datasource_for(cls, *, meta, **kwargs):
"""Determines if meta data match"""
service_subservice_ssid = tuple(meta[name] for name in ["STYPE", "SSTYPE", "SSID"])
level = meta["level"]
if service_subservice_ssid == (21, 6, 31) and level == "L1":
return True
def __repr__(self):
return f"{self.__class__.__name__}\n {self.time_range}"
[docs]
class QLVariance(QuickLookProduct):
"""
Quicklook variance nominally in 5 energy bins every 8s
"""
[docs]
@classmethod
def is_datasource_for(cls, *, meta, **kwargs):
"""Determines if meta data match"""
service_subservice_ssid = tuple(meta[name] for name in ["STYPE", "SSTYPE", "SSID"])
level = meta["level"]
if service_subservice_ssid == (21, 6, 33) and level == "L1":
return True
def __repr__(self):
return f"{self.__class__.__name__}\n {self.time_range}"
[docs]
class QLFlareFlag(QuickLookProduct):
"""
Quicklook Flare flag and location
"""
[docs]
@classmethod
def is_datasource_for(cls, *, meta, **kwargs):
"""Determines if meta data match"""
service_subservice_ssid = tuple(meta[name] for name in ["STYPE", "SSTYPE", "SSID"])
level = meta["level"]
if service_subservice_ssid == (21, 6, 34) and level == "L1":
return True
@property
def thermal_index(self):
r"""
Flare thermal index - amount of thermal emission
"""
return THERMAL_INDEX_MAP[self.data["thermal_index"]]
@property
def flare_location(self) -> SkyCoord:
r"""
Flare location as`
Notes
-----
From STIX-TN-0109-FHNW_I3R3 `YLOS = -Ysc = -Yint` and `ZLOS = Zsc = Xint`
"""
return SkyCoord(
self.data["loc_z"] * u.arcmin, -self.data["loc_y"] * u.arcmin, frame=STIXImaging(obstime=self.data["time"])
)
@property
def non_thermal_index(self) -> NDArray[str]:
r"""
Flare non-thermal index - significant non-thermal emission
"""
return THERMAL_INDEX_MAP[self.data["non_thermal_index"]]
@property
def location_flag(self) -> NDArray[str]:
r"""
Flare location flag
"""
return THERMAL_INDEX_MAP[self.data["location_status"]]
def __repr__(self):
return f"{self.__class__.__name__}\n {self.time_range}"
class QLTMStatusFlareList(QuickLookProduct):
"""
Quicklook variance nominally in 5 energy bins every 8s
"""
@classmethod
def is_datasource_for(cls, *, meta, **kwargs):
"""Determines if meta data match"""
service_subservice_ssid = tuple(meta[name] for name in ["STYPE", "SSTYPE", "SSID"])
level = meta["level"]
if service_subservice_ssid == (21, 6, 43) and level == "L1":
return True
@property
def processing_status(self):
r"""
Processing status
"""
return [TM_PROCESSING_STATUS[status] for status in self.data["processing_mask"]]
def __repr__(self):
return f"{self.__class__.__name__}\n {self.time_range}"