"""Geolocation observables: AOA, TDOA, FDOA, PDOA, etc.

This module provides functions for computing geolocation observables - measurements
used in positioning and tracking algorithms.

Example: AOA in ENU frame (simplest approach)::

    from gri_utils import observables

    # Get AOA direction cosines (East, North) in ENU frame
    aoa = observables.get_aoa_enu(collector_xyz, source_xyz)

    # Convert back to source position with range
    source_xyz = observables.translate_aoa(collector_xyz, aoa, range_m)

Example: AOA with quaternion (for arbitrary sensor orientation)::

    from scipy.spatial.transform import Rotation
    from gri_utils import observables

    # Sensor has arbitrary orientation defined by quaternion
    aoa = observables.get_aoa_quat(collector_xyz, source_xyz, quat)

    # Convert back to XYZ unit vector
    xyz_unit = observables.aoa_quat_to_xyz(aoa, quat)

Example: TDOA (Time Difference of Arrival)::

    from gri_utils import observables

    # Get time difference of arrival between two collectors
    tdoa = observables.get_tdoa(emit_xyz, collector1_xyz, collector2_xyz)

    # With gradient for optimization
    tdoa, gradient = observables.tdoa_with_gradient(emit_xyz, c1_xyz, c2_xyz)

Example: FDOA (Frequency Difference of Arrival)::

    from gri_utils import observables

    # Get frequency difference of arrival (Doppler difference)
    fdoa = observables.get_fdoa(emit_xyz, emit_vel, c1_xyz, c2_xyz, freq_hz)

    # With gradients for optimization (returns fdoa, grad_pos, grad_vel)
    fdoa, grad_pos, grad_vel = observables.fdoa_with_gradient(
        emit_xyz, emit_vel, c1_xyz, c2_xyz, freq_hz
    )

Example: PDOA (Phase Difference of Arrival) for interferometer arrays::

    from gri_utils import observables

    # Element positions in array frame (reference at origin)
    element_positions = np.array([[0.1, 0, 0], [0, 0.1, 0], [0, 0, 0.1]])

    # Get phase differences for each element
    pdoa = observables.get_pdoa(
        collector_xyz, source_xyz, quat, element_positions, frequency_hz
    )

    # Convert phase differences back to AOA direction cosines
    # Returns shape (K, 2) where K is number of candidates (auto-computed)
    aoas = observables.pdoa_to_aoa(pdoa, element_positions, frequency_hz)
    aoa = aoas[0]  # Get principal solution (closest to boresight)

    # With gradient for optimization
    pdoa, gradient = observables.pdoa_with_gradient(
        collector_xyz, source_xyz, quat, element_positions, frequency_hz
    )

Use the ENU-based functions (get_aoa_enu, translate_aoa, xyz_to_aoa, aoa_to_xyz) when
the collector's local frame is simply ENU at its position.

Use the quaternion functions (get_aoa_quat, aoa_quat_with_gradient, aoa_quat_to_xyz)
when the collector has arbitrary orientation (tilted antenna, aircraft attitude,
satellite pointing, etc.).
"""

from .aoa_enu import (
    aoa_to_enu,
    aoa_to_xyz,
    get_aoa_enu,
    get_aoa_residual,
    translate_aoa,
    xyz_to_aoa,
)
from .aoa_quat import get_aoa_quat, get_aoa_quat_broadcast
from .aoa_quat_gradient import (
    aoa_quat_with_gradient,
    aoa_quat_with_gradient_broadcast,
)
from .aoa_quat_to_xyz import aoa_quat_to_xyz, aoa_quat_to_xyz_broadcast
from .fdoa import get_fdoa, get_fdoa_residual
from .fdoa_gradient import fdoa_with_gradient
from .pdoa import get_pdoa, get_pdoa_broadcast, get_pdoa_residual
from .pdoa_gradient import pdoa_with_gradient, pdoa_with_gradient_broadcast
from .pdoa_to_aoa import pdoa_to_aoa
from .range import get_range, get_range_residual
from .rotation_utils import rotation_to_quat_array
from .tdoa import get_tdoa, get_tdoa_residual
from .tdoa_gradient import tdoa_with_gradient

__all__ = [
    "aoa_quat_to_xyz",
    "aoa_quat_to_xyz_broadcast",
    "aoa_quat_with_gradient",
    "aoa_quat_with_gradient_broadcast",
    "aoa_to_enu",
    "aoa_to_xyz",
    "fdoa_with_gradient",
    "get_aoa_enu",
    "get_aoa_quat",
    "get_aoa_quat_broadcast",
    "get_aoa_residual",
    "get_fdoa",
    "get_fdoa_residual",
    "get_pdoa",
    "get_pdoa_broadcast",
    "get_pdoa_residual",
    "get_range",
    "get_range_residual",
    "get_tdoa",
    "get_tdoa_residual",
    "pdoa_to_aoa",
    "pdoa_with_gradient",
    "pdoa_with_gradient_broadcast",
    "rotation_to_quat_array",
    "tdoa_with_gradient",
    "translate_aoa",
    "xyz_to_aoa",
]
