Source code for model.illuminator_visibility

"""Module for reading and parsing illuminator-telescope visibility tables."""

import logging

_logger = logging.getLogger(__name__)


[docs] class IlluminatorTelescopeVisibility: """ Reader and parser for illuminator-telescope visibility tables. Parses visibility data defining which telescopes can be illuminated by each illuminator, accounting for shadowing, blocking, topography, and distance constraints. The input is a list of structured records. Schema version 0.1.0 model records store the same data as a ``columns``/``rows`` mapping and are normalized before parsing. The record structure is: .. code-block:: python [ {"illuminator_id": "ILLN-01", "telescope_id": "LSTN-01", "visible": false}, {"illuminator_id": "ILLN-01", "telescope_id": "MSTN-02", "visible": true}, ] Parameters ---------- visibility_data : list of dict One record per illuminator-telescope combination. Raises ------ ValueError If the data format is invalid or missing required columns. """ def __init__(self, visibility_data): """Initialize the visibility table reader.""" _logger.info("Reading illuminator visibility table") self._parse_visibility_data(visibility_data) self._validate_data() self._valid_pairs = [(ill, tel) for (ill, tel), vis in self._pairs.items() if vis] _logger.info( f"Found {len(self._valid_pairs)} valid illuminator-telescope pairs " f"({len(self._illuminators)} illuminators x {len(self._telescopes)} telescopes)" ) def _parse_visibility_data(self, visibility_data): """ Parse visibility records into internal lookup structures. Parameters ---------- visibility_data : list of dict One structured record per illuminator-telescope pair. """ visibility_data = _normalize_visibility_records(visibility_data) if not isinstance(visibility_data, list): raise ValueError(f"Expected a list of visibility records, got {type(visibility_data)}") # Build internal data structures self._illuminators = [] self._telescopes = [] self._pairs = {} # (illuminator, telescope) -> bool for row in visibility_data: try: illuminator = row["illuminator_id"] telescope = row["telescope_id"] visible = bool(row["visible"]) except (KeyError, TypeError) as exc: raise ValueError( "Visibility records must contain illuminator_id, telescope_id, and visible" ) from exc if illuminator not in self._illuminators: self._illuminators.append(illuminator) if telescope not in self._telescopes: self._telescopes.append(telescope) self._pairs[(illuminator, telescope)] = visible _logger.debug( f"Parsed visibility table with {len(self._illuminators)} illuminators " f"and {len(self._telescopes)} telescopes" ) def _validate_data(self): """ Validate that the parsed data is consistent. Raises ------ ValueError If the data has no illuminators or no telescopes. """ if len(self._illuminators) == 0: raise ValueError("Visibility table contains no illuminators") if len(self._telescopes) == 0: raise ValueError("Visibility table contains no telescopes")
[docs] def get_telescopes(self): """ Get list of all telescopes in the visibility table. Returns ------- list of str List of telescope identifiers. """ return list(self._telescopes)
[docs] def get_valid_pairs(self): """ Get all valid illuminator-telescope pairs. A pair is valid if the visibility value is True (telescope is illuminated and not shadowed/blocked). Returns ------- list of tuple List of (illuminator_id, telescope_id) tuples for all valid pairs. """ return list(self._valid_pairs)
@property def n_telescopes(self): """Get the number of telescopes in the table.""" return len(self._telescopes) @property def n_valid_pairs(self): """Get the total number of valid illuminator-telescope pairs.""" return len(self._valid_pairs)
def _normalize_visibility_records(visibility_data): """Return structured records from a schema 0.1 row-table mapping.""" if not isinstance(visibility_data, dict): return visibility_data columns = visibility_data.get("columns") rows = visibility_data.get("rows") if not isinstance(columns, list) or not isinstance(rows, list): raise ValueError("Visibility row table must contain list-valued 'columns' and 'rows'") if any(not isinstance(row, list) or len(row) != len(columns) for row in rows): raise ValueError("Visibility row table rows must match the declared columns") return [dict(zip(columns, row, strict=True)) for row in rows]