Skip to content

Utilities

Helper functions for time, coordinates, and file parsing.

Coordinates

altaz_to_radec

altaz_to_radec(
    azimuth: NDArray[float64],
    altitude: NDArray[float64],
    times: NDArray[object_],
    facility: Facility,
) -> tuple[NDArray[float64], NDArray[float64]]

Convert horizontal (Az/El) coordinates to equatorial (RA/Dec).

Parameters:

Name Type Description Default
azimuth NDArray[float64]

Azimuth angles in degrees.

required
altitude NDArray[float64]

Elevation angles in degrees.

required
times NDArray[object_]

Array of UTC datetime objects.

required
facility Facility

Observer facility (provides location).

required

Returns:

Type Description
tuple[NDArray[float64], NDArray[float64]]

Tuple of (ra_degrees, dec_degrees) as numpy arrays.

Source code in src/sopp/utils/coordinates.py
def altaz_to_radec(
    azimuth: npt.NDArray[np.float64],
    altitude: npt.NDArray[np.float64],
    times: npt.NDArray[np.object_],
    facility: Facility,
) -> tuple[npt.NDArray[np.float64], npt.NDArray[np.float64]]:
    """Convert horizontal (Az/El) coordinates to equatorial (RA/Dec).

    Args:
        azimuth: Azimuth angles in degrees.
        altitude: Elevation angles in degrees.
        times: Array of UTC datetime objects.
        facility: Observer facility (provides location).

    Returns:
        Tuple of (ra_degrees, dec_degrees) as numpy arrays.
    """
    if len(azimuth) == 0:
        return np.array([], dtype=np.float64), np.array([], dtype=np.float64)

    ts = load.timescale()
    t = ts.from_datetimes(times)

    observer = wgs84.latlon(
        latitude_degrees=facility.coordinates.latitude,
        longitude_degrees=facility.coordinates.longitude,
        elevation_m=facility.elevation,
    )

    eph = load("de421.bsp")
    earth = eph["earth"]

    position = (earth + observer).at(t)
    sky = position.from_altaz(alt_degrees=altitude, az_degrees=azimuth)
    ra, dec, _ = sky.radec()

    return ra._degrees, dec.degrees

Time

generate_time_grid

generate_time_grid(
    start: datetime,
    end: datetime,
    resolution_seconds: float = 1.0,
) -> ndarray

Generate a uniformly spaced array of datetime objects.

Parameters:

Name Type Description Default
start datetime

Start time (UTC).

required
end datetime

End time (UTC).

required
resolution_seconds float

Time step in seconds.

1.0

Returns:

Type Description
ndarray

1D numpy array of datetime objects from start to end (inclusive).

Source code in src/sopp/utils/time.py
def generate_time_grid(
    start: datetime, end: datetime, resolution_seconds: float = 1.0
) -> np.ndarray:
    """Generate a uniformly spaced array of datetime objects.

    Args:
        start: Start time (UTC).
        end: End time (UTC).
        resolution_seconds: Time step in seconds.

    Returns:
        1D numpy array of datetime objects from start to end (inclusive).
    """
    duration = (end - start).total_seconds()

    if duration <= 0:
        return np.array([start], dtype=object)

    steps = int(duration / resolution_seconds) + 1
    offsets = np.arange(steps) * resolution_seconds

    dt_list = [start + timedelta(seconds=x.item()) for x in offsets]

    return np.array(dt_list, dtype=object)

Helpers

parse_time_and_convert_to_utc

parse_time_and_convert_to_utc(
    time: str | datetime,
) -> datetime

Accept either a string or datetime and return UTC datetime.

Source code in src/sopp/utils/helpers.py
def parse_time_and_convert_to_utc(time: str | datetime) -> datetime:
    """Accept either a string or datetime and return UTC datetime."""
    try:
        return read_datetime_string_as_utc(time)
    except TypeError:
        return convert_datetime_to_utc(time)