Source code for ctapipe.reco.disp

"""
Common functions related to the disp reconstruction.
"""

import warnings
from typing import Annotated

import astropy.units as u
import numpy as np
from astropy.table import Table

from ..containers import CoordinateFrameType
from .preprocessing import horizontal_to_telescope

__all__ = [
    "compute_true_disp",
]


def get_tel_pointing(table):
    """
    Get telescope pointing from telescope events table.

    Prefers columns telescope_pointing_{altitude,azimuth} but will fallback
    to subarray_pointing_{lat,lon,frame} for backwards compatibility with older
    datasets.

    Parameters
    ----------
    table : Table
        table of telescope events.

    Returns
    -------
    alt : u.Quantity[angle]
        pointing altitude
    az : u.Quantity[angle]
        pointing azimuth
    """
    prefix = "telescope_pointing"
    tel_alt = f"{prefix}_altitude"
    tel_az = f"{prefix}_azimuth"

    if {tel_alt, tel_az}.issubset(table.colnames):
        return table[tel_alt].quantity, table[tel_az].quantity

    prefix = "subarray_pointing"
    if not np.all(table[f"{prefix}_frame"] == CoordinateFrameType.ALTAZ.value):
        raise ValueError(
            "Subarray pointing information for disp computation"
            " has to be provided in horizontal coordinates"
        )

    warnings.warn(
        "Input table does not contain telescope pointings, falling back to array pointing"
    )
    return table[f"{prefix}_lat"].quantity, table[f"{prefix}_lon"].quantity


[docs] def compute_true_disp( table: Table, project_disp: bool = True ) -> Annotated[u.Quantity, "angle"]: """ Compute true disp parameter from a table of DL1 events. Parameters ---------- table: DL1 telescope events table as created by `ctapipe.io.TableLoader.read_telescope_events`. project_disp: If true, the true source position of the gamma-ray is projected onto the reconstructed shower axis for computing the disp norm. Otherwise, the euclidean distance from the center of gravity to the true source position is used. Returns ------- disp: Disp as a signed quantity """ pointing_alt, pointing_az = get_tel_pointing(table) fov_lon, fov_lat = horizontal_to_telescope( alt=table["true_alt"], az=table["true_az"], pointing_alt=pointing_alt, pointing_az=pointing_az, ) # numpy's trigonometric functions need radians psi = table["hillas_psi"].quantity.to_value(u.rad) cog_lon = table["hillas_fov_lon"].quantity cog_lat = table["hillas_fov_lat"].quantity delta_lon = fov_lon - cog_lon delta_lat = fov_lat - cog_lat true_disp = np.cos(psi) * delta_lon + np.sin(psi) * delta_lat true_sign = np.sign(true_disp) if project_disp: true_norm = np.abs(true_disp) else: true_norm = np.sqrt((fov_lon - cog_lon) ** 2 + (fov_lat - cog_lat) ** 2) return true_norm * true_sign