Source code for ecpi.simu.lib.context
'''
Created on 28 sept. 2020
'''
import logging
import numpy as np
from ecpi.simu.lib.instru_x import SimuECLAIRsMaskProjection
from ecpi.common.instru.model_effect import ECLAIRsDetectorEffectDefault
from ecpi.common.mission.attitude import AttitudeECLAIRs
from ecpi.common.sky.extented_body import EarthInFov
logger = logging.getLogger(__name__)
[docs]class Observable:
'''Designe pattern observer
'''
def __init__(self):
'''Init Observable
'''
self.observers = []
[docs] def add_observer(self, observer):
'''
:param observer:
'''
print('add ',observer.name)
if observer not in self.observers:
self.observers.append(observer)
else:
logger.debug('Failed to add: {}'.format(observer))
def _del_all_observer(self):
'''_del_all_observer
'''
self.observers = []
[docs] def del_observer(self, observer):
'''
:param observer:
'''
try:
self.observers.remove(observer)
except ValueError:
logger.error('Failed to remove: {}'.format(observer))
[docs] def notify_observer(self):
'''notify_observer
'''
# [o.update(self) for o in self.observers]
[o.update() for o in self.observers]
[docs]class ContextSimulator(Observable):
"""Defines a general simulation context for ECLAIRs observations
"""
def __init__(self):
"""**Constructor**
"""
super().__init__()
# high level context simulator and tools for simulator
self._sim_geom = SimuECLAIRsMaskProjection()
self._mdl_effect = ECLAIRsDetectorEffectDefault()
self._ecl_att = AttitudeECLAIRs()
self._earth_fov = EarthInFov()
# Parameters of simulation
self._idx_chan = 0
"""energy limits in keV"""
self._e_min = 0
self._e_max = 0
"""satellite unit quaternion"""
self._quat_att = None
"""position [X, Y, Z] of SVOM in km in J2000"""
self._pos = [0, 0, 0]
self._id_pos = -1
"""velocity [VX, VY, VZ] of SVOM in km/s in J2000"""
self._vel = [0, 0, 0]
"""observation start time in s"""
self._t_start = 0
"""time duration in s"""
self._duration = 0
# Getter
@property
def duration(self):
'''duration
'''
return self._duration
@property
def t_start(self):
'''t_start
'''
return self._t_start
@property
def eclairs_attitude(self):
'''eclairs_attitude
'''
return self._ecl_att
@property
def idx_chan(self):
'''idx_chan
'''
return self._idx_chan
@property
def pos_sat(self):
'''pos_sat
'''
return self._pos
@property
def energy_range(self):
'''energy_range
'''
return self._e_min, self._e_max
@property
def earth_pos_fov_unit(self):
'''earth_pos_fov_unit
'''
return self._earth_fov.get_fov_xyz()
@property
def earth_limb(self):
'''earth_limb
'''
return self._earth_fov.limb_angle()
#TODO: peut etre passer en _earth_fov en publique, à voir ...
[docs] def open_fov_map(self):
return self._earth_fov.open_fov_map()
[docs] def earth_frac(self):
m = self._earth_fov.open_fov_map()
return self._earth_fov.compute_earth_frac(m)
[docs] def is_in_fov(self):
return self._earth_fov.is_in_fov()
# Setter
def _set_energy_band(self, e_min, e_max):
"""Set the energy lower and upper limit for sources models classes
.. warning:: must be renamed as _set_energy_band
:param e_min: energy lower limit in keV
:type e_min: float
:param e_max: energy upper limit in keV
:type e_max: float
"""
self._e_min = e_min
self._e_max = e_max
[docs] def set_duration(self, duration):
'''
:param duration: simulation time in s
:type duration: float
'''
self._duration = duration
[docs] def set_t_start(self, t_start):
'''
:param t_start: start time of the simulated observation in s from mjdref
:type t_start: float
'''
self._t_start = t_start
[docs] def set_sim_geom(self, sim_pts):
'''
:param sim_pts:
'''
self._sim_geom = sim_pts
[docs] def set_mdl_effect(self, mdl_effect):
'''
:param mdl_effect:
'''
self._mdl_effect = mdl_effect
[docs] def set_quaternion_svom(self, quater):
"""
..note :: proposal:
add switch to allow the user to choose between
specifying a quaternion or an attitude. Or split functions.
"""
self._ecl_att.set_attitude_svom_quater(quater)
self._earth_fov.set_quater_svom(quater)
[docs] def set_idx_chan(self, idx_chan):
"""Set energy channel index
:param idx_chan: energy channel index
:type idx_chan: int
"""
self._idx_chan = idx_chan
self._e_min = self._mdl_effect.chan_boundary[idx_chan]
self._e_max = self._mdl_effect.chan_boundary[idx_chan + 1]
[docs] def set_pos_sat(self, pos):
'''
:param pos: satellite position in km in GCRS frame
:type pos: array 3 float
'''
print("set position")
if not isinstance(pos, np.ndarray):
self._pos = np.array(pos)
else:
self._pos = pos
self._earth_fov.set_pos_sat_time(self._pos)
self._id_pos += 1
self.notify_observer()
[docs]class GlobalContextSimulator(ContextSimulator):
"""Create global simulation context.
"""
instance = None
def __new__(cls, *args, **kargs):
"""
used singleton design pattern, ie only one instance of class ManageProcessusEcpi
"""
if cls.instance is None:
return object.__new__(cls, *args, **kargs)
else:
return cls.instance
def __init__(self):
# REMENBER : self is the return of __new__
if GlobalContextSimulator.instance is None:
super().__init__()
GlobalContextSimulator.instance = self
[docs] def reset(self):
self._del_all_observer()
# JMC some tests reduce chanel or modify ECLAIRsDetectorEffect
self._mdl_effect = ECLAIRsDetectorEffectDefault()