Source code for sofia_redux.scan.channels.mode.non_linear_response
# Licensed under a 3-clause BSD style license - see LICENSE.rst
import numpy as np
from sofia_redux.scan.channels.mode.response import Response
from sofia_redux.scan.signal.signal import Signal
__all__ = ['NonLinearResponse']
[docs]
class NonLinearResponse(Response):
def __init__(self, mode, gain_provider=None, name=None):
"""
Create a non-linear response mode.
A mode is an object that is applied to a given channel group, defining
what constitutes its "gain" and how to operate thereon. This is
also dependent on a gain provider.
The non-linear response mode contains the additional `get_signal`
method to extract a Signal object from an integration. In this case,
the final signal is dependent on a parent mode.
Parameters
----------
mode : Mode, optional
The channel group owned by the mode.
gain_provider : str or GainProvider, optional
If a string is provided a `FieldGainProvider` will be set to
operate on the given field of the channel group.
name : str, optional
The name of the mode. If not provided, will be determined from the
channel group name (if available).
"""
name = self.__class__.__name__ + '-' + mode.name
super().__init__(channel_group=mode.channel_group,
gain_provider=gain_provider,
name=name)
self.parent_mode = mode
[docs]
def get_signal(self, integration):
"""
Get a signal from an integration.
The signal values are initially set to zero before being updated by
the parent signal with drifts removed. Note that this may appear
confusing at first: the signal belongs to the integration and is
created for the integration if necessary.
Parameters
----------
integration : Integration
Returns
-------
signal : Signal
"""
parent_signal = integration.get_signal(self.parent_mode)
signal = Signal(integration,
mode=self,
values=np.zeros(parent_signal.size, dtype=float),
is_floating=False)
self.update_signal(integration)
return signal
[docs]
def update_signal(self, integration):
"""
Update the signal based on the parent signal values.
The signal values are set to the square of the parent signal values
plus parent drifts. i.e. (parent values + parent drifts)^2. The
drifts are then moved from this squared parent signal values.
In other words, values = (parent_values + parent_drifts)^2 - f(drifts).
Parameters
----------
integration : Integration
Returns
-------
None
"""
parent_signal = integration.get_signal(self.parent_mode)
signal = integration.get_signal(self)
parent_drifts = parent_signal.drifts
parent_values = parent_signal.value
if parent_drifts is not None:
drift_indices = (np.arange(parent_values.size)
// parent_signal.drift_n)
parent_drifts = parent_drifts[drift_indices]
signal.value[...] = (parent_values + parent_drifts) ** 2
else:
signal.value[...] = parent_values ** 2
# Remove drifts from the squared signal
if parent_drifts is not None:
n_frames = parent_signal.drift_n * parent_signal.resolution
signal.remove_drifts(n_frames=n_frames, is_reconstructable=False)
[docs]
def derive_gains(self, integration, robust=True):
"""
Return gains and weights derived from an integration.
The returned values are the integration gains plus the mode gains.
Weights are determined from only the integration.
Parameters
----------
integration : Integration
robust : bool, optional
If `True`, derives the gain increment from the integration using
the "robust" definition. This is only applicable if the
integration is not phase modulated.
Returns
-------
gains, weights : numpy.ndarray (float), numpy.ndarray (float)
The gains and weights derived from the integration and mode. Note
that all non-finite values are reset to zero weight and zero value.
"""
self.set_gains(np.zeros(self.channel_group.size, dtype=float))
self.sync_all_gains(integration, sum_wc2=None, is_temp_ready=True)
# Calculate the new nonlinearity signal
self.update_signal(integration)
# Calculate new gains
return super().derive_gains(integration, robust=robust)