Source code for denspp.offline.analog.amplifier.int_ana

import numpy as np
from dataclasses import dataclass
from denspp.offline.analog.common_func import CommonAnalogFunctions
from denspp.offline.analog.dev_noise import ProcessNoise, SettingsNoise, RecommendedSettingsNoise


[docs] @dataclass class SettingsINT: """Individual data class to configure an analog voltage integrator Attributes: vdd: Positive supply voltage [V] vss: Negative supply voltage [V] vmargin: Margin range from supply voltage for non-idealities [V] fs_ana: Sampling frequency of input [Hz] tau: Time constant of integrator circuit [s] res_in: Input resistance of the circuit [Ohm] offset_v: Offset voltage of the amplifier [V] offset_i: Offset current of the amplifier [V] do_invert: Do inversion of integration output [True / False] noise_en: Enable noise on output [True / False] noise_edev: Spectal noise voltage density of circuit [V/sqrt(Hz)] """ vdd: float vss: float # Amplifier characteristics tau: float res_in: float offset_v: float offset_i: float do_invert: bool # Modes for activating non-idealities noise_en: bool noise_edev: float @property def vcm(self) -> float: return (self.vdd + self.vss) / 2 @property def u_error(self) -> float: return -(self.offset_v + self.offset_i * self.res_in) @property def u_supply_range(self) -> float: return self.vdd - self.vss
RecommendedSettingsINT = SettingsINT( vdd=0.6, vss=-0.6, tau=100e-3, res_in=10e3, offset_v=1e-3, offset_i=1e-9, do_invert=False, noise_en=True, noise_edev=10e-9 )
[docs] class IntegratorStage(CommonAnalogFunctions): _handler_noise: ProcessNoise _settings: SettingsINT _sampling_rate: float __print_device = "voltage integrator" def __init__(self, settings_dev: SettingsINT, fs: float, settings_noise: SettingsNoise=RecommendedSettingsNoise): """Class for emulating an analogue integrator for voltage and current transient signals :param settings_dev: Dataclass for handling the delay amplifier :param fs: Sampling frequency [Hz] :param settings_noise: Dataclass for handling the noise simulation """ super().__init__() self.define_voltage_range(volt_low=settings_dev.vss, volt_hgh=settings_dev.vdd) self._handler_noise = ProcessNoise(settings_noise, fs) self._sampling_rate = fs self._settings = settings_dev @property def tau_active_scale(self) -> float: """Getting the time constant tau of integrator""" return 1 / self._settings.tau / self._sampling_rate def __noise_generation_resistance(self, size: int) -> np.ndarray: """Generating of noise using input resistance""" if self._settings.noise_en: u_out = self._handler_noise.gen_noise_awgn_volt(size, self._settings.res_in) else: u_out = np.zeros((size,)) return u_out def __noise_generation_circuit(self, size: int) -> np.ndarray: """Generating of noise using circuit noise properties""" if self._settings.noise_en: u_out = self._handler_noise.gen_noise_awgn_dev(size, self._settings.noise_edev) else: u_out = np.zeros((size, )) return u_out def __do_inversion(self, u_int: np.ndarray) -> np.ndarray: """Doing the inversion of signal input""" u_out = u_int if not self._settings.do_invert else -u_int return u_out @staticmethod def __do_accumulation_sample(x_inp: np.ndarray, x_inn: np.ndarray, scale: float=1.0) -> np.ndarray: """Performs an accumulation of input signals Args: x_inp: Positive input signal x_inn: Negative input signal scale: Scaling value for integration [V/V] Returns: Numpy array with accumulated input """ u_out = np.sum(x_inp - x_inn, axis=0) * scale return u_out def __do_accumulation_passive(self, x_inp: np.ndarray, x_inn: np.ndarray, scale: float=1.0, do_push: bool=False) -> np.ndarray: """Performs a passive-accumulation of input signals Args: x_inp: Positive input signal x_inn: Negative input signal scale: Scaling value for integration [V/V] do_push: Element is push- or pull-element Returns: Numpy array with signal of accumulated input """ u_out = np.zeros(x_inp.shape) + (self._settings.vss if not do_push else self._settings.vdd) for idx, u_top in enumerate(x_inp[1:], start=1): u_bot = (x_inn[idx] if x_inn.size > 1 else x_inn) if isinstance(x_inn, np.ndarray) else x_inn du = scale * self.__do_inversion(u_top - u_bot) u_out[idx] = self.clamp_voltage(u_out[idx - 1] + du) return u_out def __do_accumulation_active(self, x_inp: np.ndarray, x_inn: np.ndarray | float, scale: float=1.0) -> np.ndarray: """Performs an active-accumulation of input signals Args: x_inp: Positive input signal x_inn: Negative input signal scale: Scaling value for integration [V/V] Returns: Numpy array with signal of accumulated input """ u_out = np.zeros(x_inp.shape) + self._settings.vcm for idx, u_top in enumerate(x_inp[1:], start=1): u_bot = (x_inn[idx] if x_inn.size > 1 else x_inn) if isinstance(x_inn, np.ndarray) else x_inn u_int = u_out[idx-1] + scale * self.__do_inversion(u_top - u_bot) u_out[idx] = self.clamp_voltage(u_int) return u_out def __do_accumulation_resistance(self, u_inp: np.ndarray, u_inn: np.ndarray, scale: float=1.0) -> np.ndarray: """Performs an active-accumulation of input signals with additional input resistance Args: u_inp: Positive input voltage [V] u_inn: Negative input voltage [V] scale: Scaling value for integration [V/V] Returns: Numpy array with voltage signal of accumulated input """ u_top = u_inp + self.__noise_generation_resistance(u_inp.size) return self.__do_accumulation_active(u_top, u_inn, scale)
[docs] def do_ideal_integration_sample(self, u_inp: np.ndarray, u_inn: np.ndarray | float) -> np.ndarray: """Performs an ideal active-integration behaviour Args: u_inp: Positive input voltage or current [V | A] u_inn: Negative input voltage [V] Returns: Numpy array with voltage sample """ u_n = self.__noise_generation_circuit(u_inp.size) u_out = self.__do_accumulation_sample(u_inp + u_n, u_inn, self.tau_active_scale) return self.clamp_voltage(u_out)
[docs] def do_ideal_integration(self, u_inp: np.ndarray, u_inn: np.ndarray | float) -> np.ndarray: """Performs an ideal active-integration behaviour Args: u_inp: Positive input voltage or current [V | A] u_inn: Negative input voltage [V] Returns: Numpy array with voltage signal """ u_out = self.__do_accumulation_active(u_inp, u_inn, self.tau_active_scale) u_out += self.__noise_generation_circuit(u_out.size) return self.clamp_voltage(u_out)
[docs] def do_opa_volt_integration(self, u_inp: np.ndarray, u_inn: np.ndarray) -> np.ndarray: """Performs an active-integration behaviour using operational amplifiers (OPA) Args: u_inp: Positive input voltage [V] u_inn: Negative input voltage [V] Returns: Numpy array with voltage signal """ u_top = u_inp + self._settings.u_error u_out = self.__do_accumulation_resistance(u_top, u_inn, self.tau_active_scale) + self._settings.offset_v u_out += self.__noise_generation_circuit(u_out.size) return self.clamp_voltage(u_out)
[docs] def do_opa_curr_integration(self, iin: np.ndarray, uref: np.ndarray) -> np.ndarray: """Performs a capacitive passiv-integration behaviour Args: iin: Input current [V] uref: Reference voltage of integrator [V] Returns: Numpy array with voltage signal """ u_out = self.__do_accumulation_active(iin, 0.0) + uref u_out += self.__noise_generation_circuit(u_out.size) return self.clamp_voltage(u_out)
[docs] def do_cap_curr_integration(self, iin: np.ndarray) -> np.ndarray: """Performs a capacitive passiv-integration behaviour Args: iin: Input current [V] Returns: Numpy array with voltage signal """ u_out = self.__do_accumulation_passive(iin, 0.0) return self.clamp_voltage(u_out)