# -*- coding: utf-8 -*- from qtpy.QtWidgets import ( QFileDialog, QGridLayout, QLabel, QLineEdit, QPushButton, QSpinBox, ) from b_asic.signal_generator import ( Constant, FromFile, Gaussian, Impulse, SignalGenerator, Sinusoid, Step, Uniform, ZeroPad, ) class SignalGeneratorInput(QGridLayout): """Abstract class for graphically configuring and generating signal generators.""" def __init__(self, logger, *args, **kwargs): super().__init__(*args, **kwargs) self._logger = logger def get_generator(self) -> SignalGenerator: """Return the SignalGenerator based on the graphical input.""" raise NotImplementedError class DelayInput(SignalGeneratorInput): """ Abstract class for graphically configuring and generating signal generators that have a single delay parameter. """ def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.delay_label = QLabel("Delay") self.addWidget(self.delay_label, 0, 0) self.delay_spin_box = QSpinBox() self.delay_spin_box.setRange(0, 2147483647) self.addWidget(self.delay_spin_box, 0, 1) def get_generator(self) -> SignalGenerator: raise NotImplementedError class ImpulseInput(DelayInput): """ Class for graphically configuring and generating a :class:`~b_asic.signal_generators.Impulse` signal generator. """ def get_generator(self) -> SignalGenerator: return Impulse(self.delay_spin_box.value()) class StepInput(DelayInput): """ Class for graphically configuring and generating a :class:`~b_asic.signal_generators.Step` signal generator. """ def get_generator(self) -> SignalGenerator: return Step(self.delay_spin_box.value()) class ZeroPadInput(SignalGeneratorInput): """ Class for graphically configuring and generating a :class:`~b_asic.signal_generators.ZeroPad` signal generator. """ def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.input_label = QLabel("Input") self.addWidget(self.input_label, 0, 0) self.input_sequence = QLineEdit() self.addWidget(self.input_sequence, 0, 1) def get_generator(self) -> SignalGenerator: input_values = [] for val in self.input_sequence.text().split(","): val = val.strip() try: if not val: val = 0 val = complex(val) except ValueError: self._logger.warning(f"Skipping value: {val}, not a digit.") continue input_values.append(val) return ZeroPad(input_values) class FromFileInput(SignalGeneratorInput): """ Class for graphically configuring and generating a :class:`~b_asic.signal_generators.FromFile` signal generator. """ def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.file_label = QLabel("Browse") self.addWidget(self.file_label, 0, 0) self.file_browser = QPushButton("No file selected") self.file_browser.clicked.connect( lambda i: self.get_input_file(i, self.file_browser) ) self.addWidget(self.file_browser, 0, 1) def get_generator(self) -> SignalGenerator: return FromFile(self.file_browser.text()) def get_input_file(self, i, file_browser): module, accepted = QFileDialog().getOpenFileName() file_browser.setText(module) return class SinusoidInput(SignalGeneratorInput): """ Class for graphically configuring and generating a :class:`~b_asic.signal_generators.Sinusoid` signal generator. """ def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.frequency_label = QLabel("Frequency") self.addWidget(self.frequency_label, 0, 0) self.frequency_input = QLineEdit() self.addWidget(self.frequency_input, 0, 1) self.phase_label = QLabel("Phase") self.addWidget(self.phase_label, 1, 0) self.phase_input = QLineEdit() self.addWidget(self.phase_input, 1, 1) def get_generator(self) -> SignalGenerator: frequency = self.frequency_input.text().strip() try: if not frequency: frequency = 0.1 frequency = float(frequency) except ValueError: self._logger.warning(f"Cannot parse frequency: {frequency} not a number.") frequency = 0.1 phase = self.phase_input.text().strip() try: if not phase: phase = 0 phase = float(phase) except ValueError: self._logger.warning(f"Cannot parse phase: {phase} not a number.") phase = 0 return Sinusoid(frequency, phase) class GaussianInput(SignalGeneratorInput): """ Class for graphically configuring and generating a :class:`~b_asic.signal_generators.Gaussian` signal generator. """ def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.scale_label = QLabel("Standard deviation") self.addWidget(self.scale_label, 0, 0) self.scale_input = QLineEdit() self.scale_input.setText("1.0") self.addWidget(self.scale_input, 0, 1) self.loc_label = QLabel("Average value") self.addWidget(self.loc_label, 1, 0) self.loc_input = QLineEdit() self.loc_input.setText("0.0") self.addWidget(self.loc_input, 1, 1) self.seed_label = QLabel("Seed") self.addWidget(self.seed_label, 2, 0) self.seed_spin_box = QSpinBox() self.seed_spin_box.setRange(0, 2147483647) self.addWidget(self.seed_spin_box, 2, 1) def get_generator(self) -> SignalGenerator: scale = self.scale_input.text().strip() try: if not scale: scale = 1 scale = float(scale) except ValueError: self._logger.warning(f"Cannot parse scale: {scale} not a number.") scale = 1 loc = self.loc_input.text().strip() try: if not loc: loc = 0 loc = float(loc) except ValueError: self._logger.warning(f"Cannot parse loc: {loc} not a number.") loc = 0 return Gaussian(self.seed_spin_box.value(), loc, scale) class UniformInput(SignalGeneratorInput): """ Class for graphically configuring and generating a :class:`~b_asic.signal_generators.Uniform` signal generator. """ def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.low_label = QLabel("Lower bound") self.addWidget(self.low_label, 0, 0) self.low_input = QLineEdit() self.low_input.setText("-1.0") self.addWidget(self.low_input, 0, 1) self.high_label = QLabel("Upper bound") self.addWidget(self.high_label, 1, 0) self.high_input = QLineEdit() self.high_input.setText("1.0") self.addWidget(self.high_input, 1, 1) self.seed_label = QLabel("Seed") self.addWidget(self.seed_label, 2, 0) self.seed_spin_box = QSpinBox() self.seed_spin_box.setRange(0, 2147483647) self.addWidget(self.seed_spin_box, 2, 1) def get_generator(self) -> SignalGenerator: low = self.low_input.text().strip() try: if not low: low = -1.0 low = float(low) except ValueError: self._logger.warning(f"Cannot parse low: {low} not a number.") low = -1.0 high = self.high_input.text().strip() try: if not high: high = 1.0 high = float(high) except ValueError: self._logger.warning(f"Cannot parse high: {high} not a number.") high = 1.0 return Uniform(self.seed_spin_box.value(), low, high) class ConstantInput(SignalGeneratorInput): """ Class for graphically configuring and generating a :class:`~b_asic.signal_generators.Constant` signal generator. """ def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.constant_label = QLabel("Constant") self.addWidget(self.constant_label, 0, 0) self.constant_input = QLineEdit() self.constant_input.setText("1.0") self.addWidget(self.constant_input, 0, 1) def get_generator(self) -> SignalGenerator: constant = self.constant_input.text().strip() try: if not constant: constant = 1.0 constant = complex(constant) except ValueError: self._logger.warning(f"Cannot parse constant: {constant} not a number.") constant = 0.0 return Constant(constant) _GENERATOR_MAPPING = { "Constant": ConstantInput, "Gaussian": GaussianInput, "Impulse": ImpulseInput, "Sinusoid": SinusoidInput, "Step": StepInput, "Uniform": UniformInput, "ZeroPad": ZeroPadInput, "FromFile": FromFileInput, }