import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
from matplotlib.patches import RegularPolygon
from pyspi.utils.livedets import get_live_dets
NUM_REAL_DETS = 19
NUM_PSEUDO_DOUBLE_DETS = 42
NUM_PSEUDO_TRIPLE_DETS = 42
NUM_TOTAL_DETS = NUM_REAL_DETS + NUM_PSEUDO_DOUBLE_DETS + \
NUM_PSEUDO_TRIPLE_DETS
# the origins of the detectors
# the underscore keeps these variable from being exposed
# to the user
_detector_origins = ((0, 0), (6, 0), (3, 5.196),
(-3, 5.196), (-6, 0), (-3, -5.196),
(3, -5.196), (9, -5.196), (12, 0),
(9, 5.196), (6, 10.392), (0, 10.392),
(-6, 10.392), (-9, 5.196), (-12, 0),
(-9, -5.196), (-6, -10.392), (0, -10.392),
(6, -10.392))
def _calc_double_origin(det1, det2):
x = (_detector_origins[det1][0] + _detector_origins[det2][0]) * 0.5
y = (_detector_origins[det1][1] + _detector_origins[det2][1]) * 0.5
return x, y
def _construct_double_events_table():
"""
Helper function to generate double event detector list
:return:
"""
# the list of double pairs
# for details see:
# https://heasarc.gsfc.nasa.gov/docs/integral/spi/pages/detectors.html
double_event_pairs = (
(0, 1), (0, 2), (0, 3), (0, 4), (0, 5), (0, 6), (1, 2), (1, 6),
(1, 7), (1, 8), (1, 9), (2, 3), (2, 9), (2, 10), (2, 11), (3, 4),
(3, 11), (3, 12), (3, 13), (4, 5), (4, 13), (4, 14), (4, 15), (5, 6),
(5, 15), (5, 16), (5, 17), (6, 7), (6, 17), (6, 18), (7, 8), (7, 18),
(8, 9), (9, 10), (10, 11), (11, 12), (12, 13), (13, 14),
(14, 15), (15, 16), (16, 17), (17, 18))
# calculate the origins
origins = np.array([_calc_double_origin(*pair)
for pair in double_event_pairs])
# build a dictionary for out put
out = dict(detector_number=range(19, 19 + 42),
x=origins[:, 0],
y=origins[:, 1],
detector1=np.array(double_event_pairs)[:, 0],
detector2=np.array(double_event_pairs)[:, 1]
)
# return the as a pandas data frame
return pd.DataFrame(out)
# build a list that can be exposed to the user
spi_pseudo_double_detectors = _construct_double_events_table()
[docs]class SPIDetector(object):
[docs] def __init__(self, detector_number, origin, is_pseudo_detector=False):
"""
A SPI detector is defined by its number, origin and type
:param detector_number: the detector number
:param origin: the detector origin
:param is_pseudo_detector: if this is a real detector or not
"""
self._detector_number = detector_number
self._origin = origin
self._is_pseudo_detector = is_pseudo_detector
self._bad = False
@property
def bad(self):
return self._bad
[docs] def set_bad(self, flag):
"""
Set the flag if this is a bad detector
:param flag: Bad detector?
:return:
"""
self._bad = flag
@property
def origin(self):
return self._origin
@property
def is_pseudo_detector(self):
return self._is_pseudo_detector
@property
def detector_number(self):
return self._detector_number
[docs]class DoubleEventDetector(SPIDetector):
[docs] def __init__(self, detector_number, origin, detector1, detector2):
"""
:param detector_number:
:param origin:
:param detector1:
:param detector2:
"""
super(DoubleEventDetector, self).__init__(detector_number, origin, is_pseudo_detector=True)
self._detector1 = detector1
self._detector2 = detector2
# Fill out this class
[docs]class TripleEventDetector(SPIDetector):
pass
[docs]class DetectorContents(object):
[docs] def __init__(self, detector_array):
assert len(detector_array) == NUM_TOTAL_DETS
self._contents = np.array(detector_array)
self._real_contents = np.array(detector_array[:NUM_REAL_DETS])
[docs] @classmethod
def from_spi_data(cls, spi_data):
pass
[docs] @classmethod
def from_total_effective_area(cls, spi_response, azimuth, zenith):
effective_area = spi_response.effective_area_per_detector(azimuth, zenith).sum(axix=0)
return cls(effective_area)
[docs]class SPI(object):
[docs] def __init__(self, bad_detectors=[], time=None):
self._bad_detectors = bad_detectors
if time:
good_detectors = get_live_dets(time)
self._bad_detectors = [i
for i in range(19)
if i not in good_detectors]
self._construct_detectors()
def _construct_detectors(self):
"""
:return:
"""
# the real detector origins
self._detector_origins = _detector_origins
# go through an build the list of detectors for SPI
self._detectors = []
n = 0 # keeps track of the detector number
for origin in self._detector_origins:
# first we build the real detectors
self._detectors.append(SPIDetector(detector_number=n,
origin=origin,
is_pseudo_detector=False))
n += 1
# now we build the double event detectors
for detector in spi_pseudo_double_detectors.iterrows():
detector = detector[1]
origin = (detector['x'], detector['y'])
self._detectors.append(DoubleEventDetector(detector_number=n,
origin=origin,
detector1=detector['detector1'],
detector2=detector['detector2']))
n += 1
# TODO: Add the triple event detectors
for det in self._detectors:
if det.detector_number in self._bad_detectors:
det.set_bad(True)
[docs] def plot_spi_working_dets(self,
with_pseudo_detectors=True,
show_detector_number=True):
"""
Plot the SPI Detectors and mark the detectors that are not working red
:param with_pseudo_detectors: Plot pseudo detectors?
:param show_detector_number: Show the det numbers in the plot?
:return:
"""
fig, ax = plt.subplots(subplot_kw={'aspect': 'equal'}, figsize=(15,15))
d=1
radius = d*2* 1.732
# first get the colors based of the contents
#colors, pseudo_colors = self._get_colors_from_contents(cmap,
# pseudo_cmap)
# color iterators
n = 0
pseudo_n = 0
# now we loop over all the detectors and if they have contents
# we will plot them
for detector in self._detectors:
# first the real detectors
if not detector.is_pseudo_detector:
if detector.bad:
color = "red"
else:
color = "green"
# create a ploygon and color it based of the contents
p = RegularPolygon(xy=tuple(d*i for i in detector.origin),
numVertices=6,
radius=radius,
facecolor=color,
ec='k',
lw=3)
ax.add_patch(p)
# show the detector number
if show_detector_number:
ax.text(d*detector.origin[0],
d*detector.origin[1],
detector.detector_number,
ha="center",
va="center",
color='k',
size=30)
ax.set_xlim(d*-16, d*16)
ax.set_ylim(d*-16, d*16)
ax.set_yticks([])
ax.set_xticks([])
return fig
def _loadtxt2d(self, intext):
try:
return np.loadtxt(intext, ndmin=2)
except:
return np.loadtxt(intext)