Source code for denspp.offline.analog.adc.adc_basic

import numpy as np
from fractions import Fraction
from scipy.signal import square, resample_poly

from .adc_settings import SettingsADC
from denspp.offline.analog.common_func import CommonAnalogFunctions, CommonDigitalFunctions
from denspp.offline.analog.dev_noise import SettingsNoise, DefaultSettingsNoise, ProcessNoise


[docs] class BasicADC(CommonAnalogFunctions, CommonDigitalFunctions): _settings: SettingsADC _handler_noise: ProcessNoise def __init__(self, settings_dev: SettingsADC, settings_noise: SettingsNoise = DefaultSettingsNoise): """Basic class for applying an Analogue-Digital-Converter (ADC) on the raw data :param settings_dev: Configuration class for defining properties of ADC :param settings_noise: Configuration class for defining noise properties of device """ super().__init__() self.define_voltage_range(volt_low=settings_dev.vref[1], volt_hgh=settings_dev.vref[0]) self.define_limits(bit_signed=settings_dev.is_signed, total_bitwidth=settings_dev.Nadc, frac_bitwidth=0) self._handler_noise = ProcessNoise(settings_noise, settings_dev.fs_ana) self._settings = settings_dev # --- Internal characteristic self.noise_eff_out = 0.0 self.__dv_range = self._settings.vref[0] - self._settings.vref[1] # --- Resampling stuff (self.__p_ratio, self.__q_ratio) = ( Fraction(self._settings.fs_adc / self._settings.fs_ana) .limit_denominator(100) .as_integer_ratio() ) # --- Internal voltage values self.__input_snh = 0.0 @property def snr_ideal(self) -> float: """Getting the ideal Signal-to-Noise ratio""" return 10 * np.log10(4) * self._settings.Nadc + 10 * np.log10(3 / 2) def __do_snh_sample(self, uin: np.ndarray, do: bool | np.ndarray) -> np.ndarray: """Performing sample-and-hold (S&H) stage for buffering input value""" u_out = uin if do: u_out = self.__input_snh self.__input_snh = uin return u_out
[docs] def do_snh_stream(self, uin: np.ndarray, f_snh: float) -> np.ndarray: """Performing sample-and-hold (S&H) stage for buffering input value""" t = np.arange(0, uin.size, 1) / self._settings.fs_ana clk_fsh = square(2 * np.pi * t * f_snh, duty=0.5) do_snh = np.where(np.diff(clk_fsh) >= 0.5) do_snh += 1 u_out = np.zeros(shape=uin.shape) for idx, do_snh in enumerate(do_snh): u_out[idx] = self.__do_snh_sample(uin[idx], do_snh) return u_out
def _do_resample(self, uin: np.ndarray) -> np.ndarray: """Do resampling of input values""" if uin.size == 1: u_out = uin else: u_out = uin[0] + resample_poly(uin - uin[0], self.__p_ratio, self.__q_ratio) return u_out def _gen_noise(self, size: int) -> np.ndarray: """Generate the transient input noise of the amplifier""" u_noise = self._handler_noise.gen_noise_awgn_pwr( size = size, e_n=-self.snr_ideal ) return u_noise
[docs] def adc_ideal(self, uin: np.ndarray) -> tuple[np.ndarray, np.ndarray, np.ndarray]: """Using the ideal ADC Args: uin: Input voltage Returns: Tuple with three numpy arrays [x_out = Output digital value, u_out = Output digitized voltage, u_err = Quantization error] """ # Pre-Processing uin_adc = self.clamp_voltage(uin) uin0 = self._do_resample(uin_adc) uin0 += self._gen_noise(uin0.size) # ADC conversion x_out = np.floor((uin0 - self._settings.vcm) / self._settings.lsb) x_out = self.clamp_digital(x_out) u_out = self._settings.vref[1] + x_out * self._settings.lsb # Calculating quantization error u_err = uin0 - u_out return x_out, u_out, u_err
@staticmethod def _generate_sar_empty_data(shape) -> tuple[np.ndarray, np.ndarray, np.ndarray]: u_out = np.zeros(shape=shape, dtype=np.float32) x_out = np.zeros(shape=shape, dtype=np.int16) u_err = np.zeros(shape=shape, dtype=np.float32) return x_out, u_out, u_err @staticmethod def _generate_dsigma_empty_data(shape) -> tuple[np.ndarray, np.ndarray]: x_out_hs = np.zeros(shape=shape, dtype=np.int32) x_bit = np.zeros(shape=shape, dtype=np.int32) return x_out_hs, x_bit