Source code for ctapipe.io.monitoringsource
"""
Handles reading of monitoring files
"""
from abc import abstractmethod
import astropy.table
import astropy.time
from ..containers import ArrayEventContainer
from ..core import TelescopeComponent
from .monitoringtypes import MonitoringType
__all__ = ["MonitoringSource"]
[docs]
class MonitoringSource(TelescopeComponent):
"""
Parent class for ``MonitoringSource``.
``MonitoringSource`` read input files and fill `~ctapipe.containers.ArrayEventContainer`
instances with corresponding monitoring data based on the event trigger time.
A new ``MonitoringSource`` should be created for each type of monitoring file read
into ctapipe, e.g. HDF5 files are read by the `~ctapipe.io.HDF5MonitoringSource`.
``MonitoringSource`` provides a common high-level interface for accessing monitoring
information from different data sources. Creating an ``MonitoringSource`` for a new
file format or other monitoring source ensures that data can be accessed in a common way,
regardless of the file format or data origin.
``MonitoringSource`` itself is an abstract class, but will create an
appropriate subclass. An ``MonitoringSource`` can also be created through the
configuration system, by passing ``config`` or ``parent`` as appropriate.
E.g. if using ``MonitoringSource`` inside of a ``Tool``, you would do:
>>> self.monitoring_source = MonitoringSource(parent=self) # doctest: +SKIP
"""
plugin_entry_point = "ctapipe_monitoring"
def __init__(self, subarray=None, config=None, parent=None, **kwargs):
super().__init__(subarray=subarray, config=config, parent=parent, **kwargs)
self.metadata = {"is_simulation": False}
@property
@abstractmethod
def monitoring_types(self) -> tuple[MonitoringType]:
"""
The monitoring types provided by this monitoring source
Returns
-------
tuple[ctapipe.io.MonitoringType]
"""
[docs]
def has_any_monitoring_types(self, monitoring_types) -> bool:
"""
Check if any of `monitoring_types` is in self.monitoring_types
Parameters
----------
monitoring_types: Iterable
Iterable of monitoring types
"""
return any(mt in self.monitoring_types for mt in monitoring_types)
[docs]
@abstractmethod
def get_table(
self,
monitoring_type: MonitoringType,
tel_id: int = None,
**kwargs,
) -> astropy.table.Table:
"""
Get the raw monitoring table for a given monitoring type.
Parameters
----------
monitoring_type : MonitoringType
The type of monitoring data to retrieve.
tel_id : int, optional
Telescope ID for telescope-level monitoring (camera, telescope pointing).
None for array-level monitoring (weather, FRAM, LiDAR).
**kwargs
Implementation-specific parameters (e.g., subtype for PIXEL_STATISTICS).
Returns
-------
astropy.table.Table
The monitoring table.
Raises
------
KeyError
If monitoring_type is not available.
TypeError
If tel_id scope doesn't match monitoring_type requirements.
"""
[docs]
@abstractmethod
def get_values(
self,
monitoring_type: MonitoringType,
time: astropy.time.Time,
tel_id: int = None,
**kwargs,
):
"""
Get monitoring values for specific timestamp(s).
Performs interpolation or nearest-neighbor lookup as appropriate.
Parameters
----------
monitoring_type : MonitoringType
The type of monitoring data to retrieve.
time : astropy.time.Time
Target timestamp(s). Can be scalar or array.
tel_id : int, optional
Telescope ID for telescope-level monitoring. None for array-level.
**kwargs
Implementation-specific parameters (e.g., timestamp_tolerance, query_method).
Returns
-------
dict[str, astropy.units.Quantity | numpy.ndarray] or astropy.coordinates.SkyCoord
Monitoring values at requested time(s). Return type depends on monitoring_type.
Raises
------
KeyError
If monitoring_type unavailable
ValueError
If time out of bounds.
TypeError
If tel_id scope doesn't match monitoring_type requirements.
"""
[docs]
@abstractmethod
def fill_monitoring_container(self, event: ArrayEventContainer):
"""
Fill the monitoring container for a given event.
Populates event.monitoring with telescope-level and array-level monitoring
data for the event's trigger time.
Parameters
----------
event : ArrayEventContainer
The event to fill. Uses event.trigger.time for data selection.
"""
def __enter__(self):
return self
def __exit__(self, exc_type, exc_val, exc_tb):
self.close()
[docs]
def close(self):
"""Close this event source.
No-op by default. Should be overridden by sources needing a cleanup-step
"""
pass