Source code for panoptes.pocs.utils.location

"""Location helpers for creating and configuring site/observer objects.

This module provides utilities to construct an astroplan Observer and related
astropy EarthLocation from PANOPTES configuration, and to configure IERS table
fetching used by astropy/astroplan for accurate time/earth orientation.
"""

from dataclasses import dataclass

from astroplan import Observer
from astropy import units as u
from astropy.coordinates import EarthLocation
from astropy.utils.iers import Conf as iers_conf

from panoptes.utils import error
from panoptes.utils.config.client import get_config
from panoptes.utils.utils import get_quantity_value

from panoptes.pocs.utils.logger import get_logger

logger = get_logger()


[docs] @dataclass class SiteDetails: """Container for site-specific objects and metadata. Attributes: observer (astroplan.Observer): The astroplan Observer for the site. earth_location (astropy.coordinates.EarthLocation): The underlying EarthLocation. location (dict): Raw location configuration values used to construct the above. """ observer: Observer earth_location: EarthLocation location: dict
[docs] def download_iers_a_file(iers_url: str = None): """Download and configure the IERS A table URL. This points astropy's IERS auto URL at a PANOPTES mirror (if configured) and sets the auto-download behavior based on config. Args: iers_url (str | None): Optional override URL for the IERS A file. If not provided, uses the value from configuration key 'scheduler.iers_url'. Returns: None Notes: Also sets astropy's iers_conf.auto_download to the value of 'scheduler.iers_auto' (default False). """ iers_url = iers_url or get_config("scheduler.iers_url") if iers_url is not None: logger.info(f"Getting IERS data at {iers_url=}") iers_conf.iers_auto_url.set(iers_url) logger.debug('Setting auto_download to "scheduler.iers_auto" to False') iers_conf.auto_download.set(get_config("scheduler.iers_auto", False))
[docs] def create_location_from_config() -> SiteDetails: """Construct site objects (Observer, EarthLocation) from configuration. Reads the 'location' section from the PANOPTES configuration and constructs a dictionary of location parameters as well as the corresponding astropy EarthLocation and astroplan Observer. Returns: SiteDetails: Container with observer, earth_location, and the raw location dict. Raises: panoptes.utils.error.PanError: If the location information is missing or invalid. Notes: Expected location keys include: name, latitude, longitude, timezone, pressure, elevation, and horizon values (horizon, flat_horizon, focus_horizon, observe_horizon). """ logger.debug("Setting up site details") try: config_site = get_config("location", default=None) if config_site is None: raise error.PanError(msg="location information not found in config.") name = config_site.get("name", "Nameless Location") def get_config_with_unit(key, default_value=None, default_unit=None): """Small helper function to get the config item and ensure a unit.""" v = config_site.get(key, default_value) if default_unit is not None: v = get_quantity_value(v) * default_unit return v timezone = get_config_with_unit("timezone") latitude = get_config_with_unit("latitude", default_unit=u.degree) longitude = get_config_with_unit("longitude", default_unit=u.degree) elevation = get_config_with_unit("elevation", default_value=0, default_unit=u.meter) pressure = get_config_with_unit("pressure", default_value=0.68, default_unit=u.bar) horizon = get_config_with_unit("horizon", default_value=30, default_unit=u.degree) flat_horizon = get_config_with_unit("flat_horizon", default_value=-6, default_unit=u.degree) focus_horizon = get_config_with_unit("focus_horizon", default_value=-12, default_unit=u.degree) observe_horizon = get_config_with_unit("observe_horizon", default_value=-18, default_unit=u.degree) location = { "name": name, "latitude": latitude, "longitude": longitude, "elevation": elevation, "timezone": timezone, "pressure": pressure, "horizon": horizon, "flat_horizon": flat_horizon, "focus_horizon": focus_horizon, "observe_horizon": observe_horizon, } logger.debug(f"Location: {location}") # Create an EarthLocation for the mount earth_location = EarthLocation(lat=latitude, lon=longitude, height=elevation) observer = Observer(location=earth_location, name=name, timezone=timezone) site_details = SiteDetails(location=location, earth_location=earth_location, observer=observer) return site_details except Exception as e: raise error.PanError(msg=f"Bad site information: {e!r}")