Source code for exosim.tasks.radiometric.utils.compute_saturation

from collections import OrderedDict

import numpy as np
from astropy.table import QTable

from exosim.log import with_logger
from exosim.tasks import radiometric
from exosim.utils.klass_factory import find_task


@with_logger
[docs] def compute_saturation( table: QTable, payloadConfig: dict, input: str, channels_path=None, logger=None, ) -> QTable: """ Computes and adds saturation metrics to the radiometric table for each channel. This function uses :class:`~exosim.tasks.radiometric.SaturationChannel` to determine, for each channel, the time to saturation, the integration time, and the maximum and minimum signal levels within each spectral bin. It also computes the frame time for each channel using the appropriate frame time task. Parameters ---------- table : `~astropy.table.QTable` Table containing channel names, wavelengths, and other radiometric data. payloadConfig : dict Configuration dictionary for each channel. May be an OrderedDict mapping channel names to parameter dicts, or a single dict. input : str Path to the input file required by the saturation task. channels_path : str, optional Path to the channels in the input file. logger : `logging.Logger`, optional Injected logger instance for debug/info messages. Returns ------- tuple of (`~astropy.table.QTable`, `~astropy.table.QTable`) - The full table with added columns: ``saturation_time``, ``integration_time``, ``max_signal_in_bin``, ``min_signal_in_bin``, ``observation_efficiency``, ``frame_time``. - A view of key columns: ``ch_name``, ``wavelength``, ``saturation_time``, ``integration_time``, ``max_signal_in_bin``, ``min_signal_in_bin``, ``observation_efficiency``, ``frame_time``. Notes ----- The function processes all channels defined in the payload configuration. For each channel, it computes the saturation and frame time metrics and appends them as new columns to the radiometric table. The computation results for each channel are also returned as a separate table view containing only the relevant columns. """ logger.debug("Entered compute_saturation() with %d rows", len(table)) saturations = [] integration_time = [] max_signal_in_bin = [] min_signal_in_bin = [] saturation_task = radiometric.SaturationChannel() channels = payloadConfig["channel"] if isinstance(channels, OrderedDict): channel_items = channels.items() else: channel_items = [(None, channels)] for ch_name, params in channel_items: logger.debug("Computing saturation for channel %r", ch_name) sat, max_, min_ = saturation_task( table=table, description=params, input_file=input, channels_path=channels_path, ) logger.debug( "Channel %r: sat=%r entries, sat sample=%r", ch_name, len(sat), sat[0] ) saturations += sat max_signal_in_bin += max_ min_signal_in_bin += min_ table["saturation_time"] = saturations table["max_signal_in_bin"] = max_signal_in_bin table["min_signal_in_bin"] = min_signal_in_bin logger.info("Added saturation metrics: %d rows updated", len(table)) integration_time = [] for ch_name, params in channel_items: logger.debug("Computing integration time for channel %r", ch_name) # Check if radiometric key exists and contains integration_time_task radiometric_params = params.get("radiometric", {}) compute_integration_time = ( find_task( radiometric_params.get("integration_time_task"), radiometric.ComputeIntegrationTime(), ) if "integration_time_task" in radiometric_params else radiometric.ComputeIntegrationTime() ) integration_t = compute_integration_time( saturation_table=table, description=params, channel_name=ch_name if isinstance(channels, OrderedDict) else None, ) integration_time += list(integration_t) table["integration_time"] = integration_time logger.info("Added integration_time column with %d entries", len(integration_time)) observation_efficiency = [] for ch_name, params in channel_items: logger.debug("Computing observation efficiency for channel %r", ch_name) # Check if radiometric key exists and contains observation_efficiency_task radiometric_params = params.get("radiometric", {}) compute_observation_efficiency = ( find_task( radiometric_params.get("observation_efficiency_task"), radiometric.ComputeObservationEfficiency(), ) if "observation_efficiency_task" in radiometric_params else radiometric.ComputeObservationEfficiency() ) efficiency = compute_observation_efficiency( radiometric_table=table, description=params, channel_name=ch_name if isinstance(channels, OrderedDict) else None, ) n_bins = len(table[table["ch_name"] == ch_name]) if ch_name else len(table) observation_efficiency += list(efficiency * np.ones(n_bins, dtype=float)) table["observation_efficiency"] = observation_efficiency # Convert to numpy arrays before division operation observation_efficiency_array = np.array(observation_efficiency, dtype=float) table["frame_time"] = table["integration_time"] / observation_efficiency_array logger.info("Added frame_time column with %d entries", len(table["frame_time"])) return ( table, table[ "ch_name", "wavelength", "saturation_time", "integration_time", "max_signal_in_bin", "min_signal_in_bin", "observation_efficiency", "frame_time", ], )