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

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


[docs] @dataclass class SettingsDLY: """Individual data class to configure the delay amplifier Attributes: vdd: Positive supply voltage [V] vss: Negative supply voltage [V] fs_ana: Sampling frequency of input [Hz] t_dly: Delay value for shifting the input [s] offset: Offset voltage of the amplifier [V] noise_en: Enable noise on output [True/False] noise_edev: Input voltage noise spectral density [V/sqrt(Hz)] """ vdd: float vss: float fs_ana: float # Amplifier characteristics t_dly: float offset: float # Noise properties noise_en: bool noise_edev: float
RecommendedSettingsDLY = SettingsDLY( vdd=0.6, vss=-0.6, fs_ana=50e3, t_dly=0.1e-3, offset=0e-6, noise_en=False, noise_edev=100e-9 )
[docs] class DlyAmp(CommonAnalogFunctions): _handler_noise: ProcessNoise _settings: SettingsDLY __print_device = "delay amplifier" def __init__(self, settings_dev: SettingsDLY, settings_noise: SettingsNoise=RecommendedSettingsNoise) -> None: """Class for emulating an analogue delay amplifier :param settings_dev: Dataclass for handling the delay amplifier :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, settings_dev.fs_ana) self._settings = settings_dev @property def num_dly_taps(self) -> int: return int(self._settings.fs_ana * self._settings.t_dly)
[docs] def do_simple_delay(self, u_inp: np.ndarray) -> np.ndarray: """Performing a simple delay stage using taps Args: u_inp: Applied voltage input [V] Returns: Corresponding numpy array with shifted input """ uout = np.zeros(u_inp.shape) + self.vcm uout[self.num_dly_taps:] = u_inp[:-self.num_dly_taps] return self.clamp_voltage(uout)
[docs] def do_recursive_delay(self, u_inp: np.ndarray) -> np.ndarray: """Performing a recursive delay stage using taps Args: u_inp: Applied voltage input [V] Returns: Corresponding numpy array with shifted input """ uout = np.zeros(u_inp.shape) uout[:self.num_dly_taps] = u_inp[-self.num_dly_taps:] uout[self.num_dly_taps:] = u_inp[:-self.num_dly_taps] return self.clamp_voltage(uout)
[docs] def do_allpass_first_order(self, uin: np.ndarray, f_b: float=1.0) -> np.ndarray: """Performing a 1st order all-pass filter (IIR) for adding time delay Args: uin: Input voltage [V] f_b: Frequency of used delay [Hz] Returns: Corresponding numpy array with shifted voltage signal """ val = np.tan(np.pi * f_b / self._settings.fs_ana) iir_c0 = (val - 1) / (val + 1) b = [iir_c0, 1.0] a = [1.0, iir_c0] return self.clamp_voltage(lfilter(b, a, uin))
[docs] def do_allpass_second_order(self, uin: np.ndarray, f_b: float=1.0, bandwidth: float=0.5) -> np.ndarray: """Performing a 2nd order all-pass filter (IIR) for adding time delay Args: uin: Input voltage [V] f_b: Frequency of used delay [Hz] bandwidth: Bandwidth frequency [Hz] Returns: Corresponding numpy array with shifted voltage signal """ val = np.tan(np.pi * bandwidth / self._settings.fs_ana) iir_c0 = (val - 1) / (val + 1) iir_c1 = -np.cos(2 * np.pi * f_b / self._settings.fs_ana) b = [-iir_c0, iir_c1*(1-iir_c0), 1.0] a = [1.0, iir_c1*(1-iir_c0), -iir_c0] return self.clamp_voltage(lfilter(b, a, uin))