Source code for ska_pst.common.telescope_configuration

# -*- coding: utf-8 -*-
#
# This file is part of the SKA PST project
#
# Distributed under the terms of the BSD 3-clause new license.
# See LICENSE for more info.

"""
Module is designed to be an abstraction for getting telescope specific configuration.

Current implementation uses a configuration file but the ``get_telescope_config``
can but reimplemented to get configuration for the SKA Telemodel or other sources.

The configuration also includes the CBF/PST configuration values that are specific
for different frequency bands.
"""

from __future__ import annotations

import dataclasses
import pathlib
from typing import Any, Dict, List

import yaml
from dacite import from_dict
from ska_pst.common.constants import SKA_PST_LOW_SUBSYSTEM_ID, SKA_PST_SUBSYSTEM_IDS

from .cbf_pst_config import CbfPstConfig

__all__ = [
    "TelescopeConfig",
    "FrequencyBandConfig",
    "ReceiverConfig",
]


[docs]@dataclasses.dataclass(kw_only=True, frozen=True) class TelescopeConfig: """A data class used to model telescope specific configuration used by PST.""" name: str """ The name of the telescope. This should be either ``SKALow`` or ``SKAMid``. """ frequency_bands: Dict[str, FrequencyBandConfig] = dataclasses.field(default_factory=dict) """ Configuration specific to the frequency bands for a given telescope. For SKALow there is only 1 frequency band, "low", while SKAMid has multiple frequency bands. The configuration that should be used for a given scan request will depend on what telescope the scan is for and the frequency band if the telescope is SKAMid. """
[docs] def get_cbf_pst_config( self: TelescopeConfig, frequency_band: str | None = None, **kwargs: Any ) -> CbfPstConfig: """ Get the configuration specific for a frequency band. This will return the configuration that is specific to a frequency band. The standard for SKA is that if the frequency_band is not set or is "low" then it corresponds to the Low telescope, which has only one band. Frequency bands of 1, 2, 3, 4, 5a, or 5b will return specific configuration. :param frequency_band: the frequency band to get configuration for, defaults to None :type frequency_band: str | None, optional :return: configuration for the frequency band. :rtype: CbfPstConfig """ frequency_band = frequency_band or "low" return self.frequency_bands[frequency_band].cbf_pst_config
[docs]@dataclasses.dataclass(kw_only=True, frozen=True) class FrequencyBandConfig: """A data class used to model specific configuration for a given frequency band.""" receiver_config: ReceiverConfig """Configuration specific to the receiver for a given frequency band.""" cbf_pst_config: CbfPstConfig """Configuration specific to the CBF/PST interface for a given frequency band."""
[docs]@dataclasses.dataclass(kw_only=True, frozen=True) class ReceiverConfig: """A data class used to model specific configuration of a receiver.""" receiver_id: str """ The name/id of the receiver. This value should match the ``receiver_id`` in the CSP/PST scan configuration. """ feed_polarization: str """The native polarisation of the feed for the receiver.""" feed_handedness: int """ The handedness of the feed of the receiver. For value of +1 for XYZ forming RH set with Z in the direction of propagation. Looking up into the feed of a prime-focus receiver or at the sky). For FD_HAND = +1, the rotation from A (or X) to B (or Y) is counter clockwise or in the direction of increasing Feed Angle (FA) or Position Angle (PA). For circular feeds, FD_HAND = +1 for IEEE LCP on the A (or X) probe. """ feed_angle: float """ The feed angle of the receiver. Feed angle of the E-vector of the receiver for an equal in-phase response from the A(X) and B(Y) probes, measured in the direction of increasing feed angle or position angle (clockwise when looking down on a prime focus receiver). """ feed_tracking_mode: str """ The tracking mode for the feed of the receiver. * FA - constant feed angle and that the feed stays fixed with respect to the telescope's reference frame. * CPA - the feed rotates to maintain a constant phase angle (i.e. it tracks the variation of the parallactic angle.). When the coordinate mode is GALACTIC, PA is with respect to Galactic north and similarly for coordinate mode ECLIPTIC then PA is with respect to ecliptic north. * SPA - the feed angle is held fixed at an angle such that the requested PA is obtained at the mid-point of the observation. * TPA - is only relevant for scan observations - the feed is rotated to maintain a constant angle with respect to the scan direction. """ feed_position_angle: float """ The requested angle of feed reference. For feed_mode = 'FA' this is respect to the telescope's reference frame (feed_angle = 0), and for feed_mode = 'CPA' this is with respect to the celestial north (parallactic angle = 0) or with respect to the Galactic north for coordinate_mode = 'GALACTIC'. """
def _load_telescope_configs() -> Dict[str, TelescopeConfig]: file = pathlib.Path(__file__).parent / "telescope_config.yaml" assert file.exists(), f"expected {file} to exist" with open(file, "r") as f: config: List[dict] = yaml.safe_load(f) return {d["name"]: from_dict(data_class=TelescopeConfig, data=d) for d in config} TELESCOPE_CONFIGS: Dict[str, TelescopeConfig] = _load_telescope_configs()
[docs]def get_telescope_config(telescope: str) -> TelescopeConfig: """ Get the telescope configuration for a given telescope. :param telescope: the name of the telescope. :type telescope: str :return: the configuration specific to the telescope. :rtype: TelescopeConfig """ return TELESCOPE_CONFIGS[telescope]
[docs]def get_telescope_config_by_pst_subsystem(subsystem_id: str) -> TelescopeConfig: """ Get the telescope configuration for a given PST subsystem. This will map subsystem to to the corresponding SKA telescope and then call ``get_telescope_config`` for the telescope. :param subsystem_id: the PST subsystem to get the telescope configuration for. :type subsystem_id: str :return: the telescope configuration for a given PST subsystem. :rtype: TelescopeConfig """ assert ( subsystem_id in SKA_PST_SUBSYSTEM_IDS ), f"expected '{subsystem_id}' to be in {SKA_PST_SUBSYSTEM_IDS}" if subsystem_id == SKA_PST_LOW_SUBSYSTEM_ID: return get_telescope_config("SKALow") else: return get_telescope_config("SKAMid")
[docs]def get_udp_nsamp_for_format(udp_format: str) -> int: """ Get the UDP_NSAMP for a given UDP Format. NOTE: This method is only used by the ``WeightsFileReader`` both in the SEND Python code and the ``ska_pst.testutils`` package. In both cases the value should get this value based off the telescope and frequency band rather than calling this method. :return: the UDP_NSAMP for the given UDP format. :rtype: int :raises AssertionError: if the UDP format is unknown. """ for config in TELESCOPE_CONFIGS.values(): for freq_band_config in config.frequency_bands.values(): if freq_band_config.cbf_pst_config.udp_format == udp_format: return freq_band_config.cbf_pst_config.udp_nsamp raise AssertionError(f"unknown UDP format of {udp_format}")