Overview -------- Optics ====== :py:class:`OpticalComponents` are elements that form the basis of the :py:mod:`optics` module. Each component transforms radiance data according to its Mueller matrix. An optical component requires an orientation defined by an :py:class:`skretrieval.OpticalGeometry`, a radiance object (n x m numpy array of :py:class:`sasktran.StokesVector`) and the the wavelengths of the radiances (numpy array of size n). .. code-block:: python from skcomponents.optics import VerticalPolarizer from skretrieval import OpticalGeometry from sasktran import Geometry, StokesVector # create a vertical polarizer vert_pol = VerticalPolarizer() # basis of the stokes vector. stokes_basis = np.eye(3) # create some test radiance data (this would normally be produced by a radiative transfer model) model_wavel_nm = np.array([500]) radiance = np.array([[StokesVector([1, 1, 0, 0], stokes_basis), StokesVector([1, -1, 0, 0], stokes_basis)]]) # define the geometry of the optical component optical_geometry = OpticalGeometry(observer=np.array([1, 0, 0]), look_vector=np.array([0, 1, 0]), local_up=np.array([0, 0, 1]), mjd=53000.0) # pass radiance through the polarizer radiance = vert_pol.model_radiance(optical_geometry, model_wavel_nm, radiance=radiance) To create an optical system you can add components together. As an example, we can create a system than produces circularly polarized light: .. code-block:: python from skcomponents.optics import VerticalPolarizer, QuarterWavePlate optics = VerticalPolarizer() + QuarterWavePlate() radiance = optics.model_radiance(optical_geometry, model_wavel_nm, radiance=radiance) Processors ========== A :py:class:`SignalProcessor` is a component that transforms signals after they have been converted from radiance to photons by a sensor. The purpose of a processor can be wide ranging, from simulating noise on a signal to inverting an interferogram. .. code-block:: python from skcomponents.processing import ADC, PoissonCounting # define a signal signal = np.ones((3, 3)) * 1000 # create the processors adc = ADC(bits=8, min_value=0, max_value=1000) counter = PoissonCounting() # pass signal through processors signal = counter.process_signal(signal) signal = adc.process_signal(signal) Putting Everything Together =========================== Optical components and signal processors are typically used in the context of a :py:class:`skretrieval.MeasurementSimulator`. Using :py:class:`OpticalComponent`, :py:class:`Sensor`, and :py:class:`SignalProcessor` can be incorporated in this framework by subclassing the desired :py:class`Sensor` and overriding the :py:meth:`calculate_radiance` method as shown below: .. code-block:: python from skretrieval.core import SpectralImager from skcomponents.optics import VerticalPolarizer from skcomponents.processing import ADC class Instrument(SpectralImager): ... def model_radiance(self, optical_geometry: OpticalGeometry, model_wavel_nm: np.array, model_geometry: Geometry, radiance: np.array, wf=None) -> RadianceSpectralImage: # pass radiance through a vertical polarizer optics = VerticalPolarizer() if wf is None: radiance = optics.model_radiance(optical_geometry, model_wavel_nm, model_geometry, radiance) else: radiance, wf = optics.model_radiance(optical_geometry, model_wavel_nm, model_geometry, radiance, wf) # convert radiance to photons on each pixel model_value = super().model_radiance(optical_geometry, model_wavel_nm, model_geometry, radiance, wf=wf) # process the signal through electronic components adc = ADC(bits=12, min_value=0, max_value=5) model_value.data['radiance'].values = adc.process_signal(model_value.data['radiance'].values) return model_value