Source code for sweep_design.uncalculated_sweep

import logging
from math import sqrt
from typing import Any, Callable, Optional, Union

import numpy as np

from .axis import ArrayAxis
from .config.base_config import Config
from .config.sweep_config import SweepConfig
from .defaults.sweep_methods import (CallFtatMethod, Ftatr, InterpolateArray,
                                     get_info_from_a_prior_data,
                                     get_info_from_ftat)
from .exc import BadInputError
from .relation import Relation
from .sweep import Sweep
from .help_types import ArrayLike


[docs]class UncalculatedSweep: '''The `UncalculatedSweep` class prepares for the calculation of the signal sweep (`Sweep`). The get_info_from_ftat function is used to extract the frequency versus `time` and amplitude versus `time` functions from the passed `frequency_time` and `amplitude_time` parameters. Analytic functions (`frequency_time` and `amplitude_time`) can be as array of numbers or as callable object(lambda function, common python functions and ect.) Example: frequency_time = lambda t: t*10+1, amplitude_time = lambda t: np.ones(t.size) Raises: BadInputError: raise exception when calling instance without parameter time or when time attribute is not created when instance initialized. ''' def __init__( self, time: Union[ArrayAxis, ArrayLike] = None, frequency_time: Union[Ftatr, ArrayLike, InterpolateArray] = None, amplitude_time: Union[Ftatr, ArrayLike, InterpolateArray] = None, ) -> None: '''Initialize instance of UncalculatedSweep. Args: time (ArrayLike, optional): The `ArrayAxis`, or an ArrayLike object containing numbers(real or complex). Defaults to None. frequency_time (Union[Ftatr, ArrayLike], optional): This parameter, which describes changes in frequency over time, can be either an array_like, or an object from which an instance of the `Relation` class will be created, or an instance of the `Relation` class, or a callable object that returns a numeric sequence. If `None`, then the linear function f = t will be used. Defaults to None. amplitude_time (Union[Ftatr, ArrayLike], optional): This parameter, which describes changes in amplitude modulation over time, can be either an array_like, or an object from which an instance of the `Relation` class will be created, or an instance of the `Relation` class, or a callable object that returns a numeric sequence. If `None`, then the function will be assumed to be constant and equal to 1. Defaults to None. ''' self._integrate_function_default = Config.integrate_function_method self._get_array_axis_from_array_method = Config.get_array_axis_from_array_method if not (isinstance(time, ArrayAxis) or time is None): time = self._get_array_axis_from_array_method(time) if not ( isinstance( frequency_time, (np.ndarray, Relation, InterpolateArray)) or callable(frequency_time) or frequency_time is None ): frequency_time = np.array(frequency_time) if not ( isinstance( amplitude_time, (np.ndarray, Relation, InterpolateArray)) or callable(amplitude_time) or amplitude_time is None ): amplitude_time = np.array(amplitude_time) ( self._time, self._frequency_time, self._amplitude_time, ) = get_info_from_ftat(time, frequency_time, amplitude_time)
[docs] def __call__(self, time: Union[ArrayAxis, ArrayLike] = None, tht0=0.0 ) -> Sweep: '''Calling an instance with appropriate to calculate the sweep signal. If time is not passed or equals None, then the time sequence created when the class instance was initialized will be used. Args: time (Union[ArrayAxis, ArrayLike], optional): The number sequence determines the time. Defaults to None. tht0 (float, optional): Zero phase. Defaults to 0.0. Raises: BadInputError: raise exception when calling instance without parameter time when time attribute is not created when instance initialized. Returns: Sweep: an instance of the Sweep class - the calculated sweep signal. ''' logging.info( "Calling uncalculated sweep.\n" "with params:\nfrequency_time={0}\namplitude_time={1}\ntime={2}" "".format(self._frequency_time, self._amplitude_time, time) ) if time is None and self._time is None: raise BadInputError("Not enough data: time") elif time is not None: if isinstance(time, ArrayAxis): calc_time = time else: calc_time = self._get_array_axis_from_array_method(time) elif time is None and self._time is not None: calc_time = self._time if isinstance(self._frequency_time, InterpolateArray): tht = self._array_tht(self._frequency_time(calc_time)) frequency_time = Relation( calc_time, self._frequency_time(calc_time)) else: tht = self._func_tht(self._frequency_time) frequency_time = Relation( calc_time, self._frequency_time( calc_time.array)) if isinstance(self._amplitude_time, InterpolateArray): amplitude = self._amplitude_time(calc_time) else: amplitude = self._amplitude_time(calc_time.array) sweep = amplitude * \ np.sin(tht(calc_time).y + tht0) amplitude_time = Relation( calc_time, amplitude ) return Sweep( time=calc_time, amplitude=sweep, frequency_time=frequency_time, amplitude_time=amplitude_time, )
[docs] def _func_tht( self, frequency_time: Callable[[np.ndarray], np.ndarray] ) -> Callable[[ArrayAxis], Relation]: """Functional representation of angular sweep.""" def result(time: ArrayAxis) -> Relation: return Relation( *self._integrate_function_default(frequency_time, time)) return result
[docs] def _array_tht( self, frequency_time: np.ndarray ) -> Callable[[ArrayAxis], Relation]: """Angular sweep represented by a numerical sequence.""" def wrapper(time: ArrayAxis) -> Relation: frequency_time_relation = Relation(time, frequency_time) result = np.append([0.0], 2 * np.pi * frequency_time_relation.integrate().y) return Relation(time, result) return wrapper
[docs]class ApriorUncalculatedSweep(UncalculatedSweep): '''`ApriorUncalculatedSweep` Class for constructing a sweep signal from a priori data (from another signal or spectrum). The calculation of the change in frequency with time (`frequency_time`) and the amplitude envelope with time (`amplitude_time`) will depend on the a priori data (`aprior_data`) and on the method (`ftat_method`) by which they will be calculated. The extracted frequency over time (`frequency_time`) and the amplitude envelope over time (`amplitude_time`) will be send to the `UncalculatedSweep` constructor. ''' def __init__( self, time: Any = None, a_prior_data: Any = None, ftat_method: Optional[CallFtatMethod] = None, ) -> None: '''Configuring an instance to create a sweep from a aprior data. Args: time (Any, optional): The number sequence determines the time. Defaults to None. a_prior_data (Any, optional): Data from which will be extracted frequency and amplitude modulation. Defaults to None. ftat_method (CallFtatMethod, optional): If the conversion method (`ftat_method`) is not defined or `None`, then the method is taken from the `SweepConfig` class `freq2time` Method can be overridden if necessary (`sweep_design.math_signals.config.SweepConfig.freq2time`). Defaults to None. ''' if ftat_method is None: ftat_method = SweepConfig.freq2time ( frequency_time, amplitude_time, self._a_prior_signal, ) = get_info_from_a_prior_data(time, a_prior_data, ftat_method) super().__init__(time, frequency_time, amplitude_time)
[docs] def __call__( self, time: Union[ArrayAxis, ArrayLike] = None, tht0=0.0, is_normalize=True ) -> Sweep: '''Calculate the sweep and normalize it. Args: time (Union[ArrayAxis, ArrayLike], optional): The number sequence determines the time. Defaults to None. tht0 (float, optional): Zero phase. Defaults to 0.0. is_normalize (bool, optional): Use normalization a prior data for sweep signal. Defaults to True. Returns: Sweep: an instance of the Sweep class - the calculated sweep signal from a prior data. ''' sweep = super().__call__(time=time, tht0=tht0) if is_normalize: norm_sweep = sweep.get_norm() norm_a_prior = self._a_prior_signal.get_norm() norm = sqrt(norm_a_prior) / sqrt(norm_sweep) sweep.amplitude_time *= norm sweep *= norm sweep.a_prior_signal = self._a_prior_signal return sweep