Source code for prsctrl.data.functions

"""
Basic functions, not specific to PrsData

Notation:
prefix "l": value as measured by Lock-In
prefix "s": standard deviation
"""
import numpy as np

# nm_to_eV etc.
from devctrl.data.spectrum.functions import *

[docs] def rms_to_pp_sin(rms: float) -> float: return rms * 2 * np.sqrt(2)
[docs] def rms_to_pp_square(rms: float) -> float: return rms * 2
[docs] def fexp(number): """ Get the base 10 exponent of a number """ return int(round(np.log10(number), 0))
# Data calculations
[docs] def dIX(lR, slR, theta, stheta, dI_offset, sdI_offset): """ Calculate the in-phase (X) signal component delta I_X = delta R * cos(theta) - delta U_offset Currently, the error of the offset is not included in the error of delta I_X. :param lR: R from lock-in :param slR: standard error of R from lock-in :param theta: offset-corrected theta from lock-in :param stheta: standard error of offset-corrected theta from lock-in :param dI_offset: R-offset from lock-in :param sdI_offset: standard error of R-offset from lock-in :return: delta I_X """ dI = lR * np.cos(np.deg2rad(theta)) dI -= dI_offset # TODO use offset error sdI = np.sqrt((slR * np.cos(np.deg2rad(theta)))**2 + (lR * np.sin(np.deg2rad(theta)) * np.deg2rad(stheta))**2) return dI, sdI
[docs] def dIY(lR, slR, theta, stheta): """ Calculate quadrature (out-of-phase, Y) signal component delta I_Y = delta R * sin(theta) The lock-in R offset is not substracted, since it is assumed to be in-phase :param lR: R from lock-in :param slR: standard error of R from lock-in :param theta: offset-corrected theta from lock-in :param stheta: standard error of offset-corrected theta from lock-in :return: delta I_Y """ dIY = lR * np.sin(np.deg2rad(theta)) sdIY = np.sqrt((slR * np.sin(np.deg2rad(theta)))**2 + (lR * np.cos(np.deg2rad(theta)) * np.deg2rad(stheta))**2) return dIY, sdIY
[docs] def dI(lR, slR, theta, stheta, dI_offset, sdI_offset): """ Calculate the total (R) signal component. delta I = sqrt((delta I_X)^2 + (delta I_Y)^2) * (-1) if |theta| >= 90 The lock-in R offset is only subtracted from delta I_X, since it is assumed to be in-phase. The sign is flipped when |theta| >= 90, since that is assumed to be not a 180° phase shift, but a negative signal. This assumption is valid if the phase never "creeps" towards 90°, but is always either near 0° or near 180°. If the phase is near 90°, it is not possible to decipher whether this is a phase shift or a sign flip! :param lR: R from lock-in :param slR: standard error of R from lock-in :param theta: offset-corrected theta from lock-in :param stheta: standard error of offset-corrected theta from lock-in :param dI_offset: R-offset from lock-in :param sdI_offset: standard error of R-offset from lock-in :return: delta I """ dI_X, sdI_X = dIX(lR, slR, theta, stheta, dI_offset, sdI_offset) dI_Y, sdI_Y = dIY(lR, slR, theta, stheta) dI = np.sqrt(dI_X**2 + dI_Y**2) # Flip sign when |theta|>90° (negative X) # Can not use phase here, since we subtract an offset from X, theta is not actually the correct angle. # The correct angle can be calculated from offset-corrected dI-X and dI-Y if dI_X < 0: dI = -dI sdI = np.sqrt(1 / dI**2 * ((dI_X * sdI_X)**2 + (dI_Y * sdI_Y)**2)) return dI, sdI
[docs] def dI_I(dI, sdI, I, sI) -> tuple[float, float]: """ Calculate normalized change in intensity :return: value, error """ dI_I = dI / I sdI_I = np.sqrt((sdI / I)**2 + (dI * sI/I**2)**2) return dI_I, sdI_I
[docs] def A(I0, sI0, R, sR, T, sT): """ Calculate "DC Absorption" value from reference, reflection and transmission :return: value, error """ # 1 = T + R + A return I0 - T - R, np.sqrt(sI0**2 + sR**2 + sT**2)
[docs] def dA(dR, sdR, dT, sdT): """ Calculate change in absorption absorption from reflection and transmission :return: value, error """ return -dR - dT, np.sqrt(sdR**2 + sdT**2)