diff --git a/b_asic/signal_generator.py b/b_asic/signal_generator.py index 549c0efdf9e727e374863ba4e3d0d6b24dd6d50f..466e1f1e94a6bd988fe0f444cbd1aa5705331c98 100644 --- a/b_asic/signal_generator.py +++ b/b_asic/signal_generator.py @@ -4,8 +4,9 @@ B-ASIC signal generators These can be used as input to Simulation to algorithmically provide signal values. """ +from math import pi, sin from numbers import Number -from typing import Callable +from typing import Callable, Sequence class SignalGenerator: @@ -94,6 +95,49 @@ class Constant(SignalGenerator): return self._constant +class ZeroPad(SignalGenerator): + """ + Signal generator that pads a sequence with zeros. + + Parameters + ---------- + data : 1-D array + The data that should be padded. + """ + + def __init__(self, data: Sequence[complex]) -> Callable[[int], complex]: + self._data = data + self._len = len(data) + + def __call__(self, time: int) -> complex: + if 0 <= time < self._len: + return self._data[time] + return 0.0 + + +class Sinusoid(SignalGenerator): + """ + Signal generator that generates a sinusoid. + + Parameters + ---------- + frequency : float + The normalized frequency of the sinusoid. Should normally be in the + interval [0, 1], where 1 corresponds to half the sample rate. + phase : float, default: 0 + The normalized phase offset. + """ + + def __init__( + self, frequency: float, phase: float = 0.0 + ) -> Callable[[int], complex]: + self._frequency = frequency + self._phase = phase + + def __call__(self, time: int) -> complex: + return sin(pi * (self._frequency * time + self._phase)) + + class AddGenerator: """ Signal generator that adds two signals. diff --git a/test/test_signal_generator.py b/test/test_signal_generator.py index ad07861ec3e2e7a04e613133d7bd37b2d558bde8..0605120840e64ed06ba154d8a300cb1dff7e091d 100644 --- a/test/test_signal_generator.py +++ b/test/test_signal_generator.py @@ -1,13 +1,19 @@ -from b_asic.signal_generator import Constant, Impulse, Step +from math import sqrt + +import pytest + +from b_asic.signal_generator import Constant, Impulse, Sinusoid, Step, ZeroPad def test_impulse(): g = Impulse() + assert g(-1) == 0 assert g(0) == 1 assert g(1) == 0 assert g(2) == 0 g = Impulse(1) + assert g(-1) == 0 assert g(0) == 0 assert g(1) == 1 assert g(2) == 0 @@ -15,11 +21,13 @@ def test_impulse(): def test_step(): g = Step() + assert g(-1) == 0 assert g(0) == 1 assert g(1) == 1 assert g(2) == 1 g = Step(1) + assert g(-1) == 0 assert g(0) == 0 assert g(1) == 1 assert g(2) == 1 @@ -27,30 +35,57 @@ def test_step(): def test_constant(): g = Constant() + assert g(-1) == 1 assert g(0) == 1 assert g(1) == 1 assert g(2) == 1 g = Constant(0.5) + assert g(-1) == 0.5 assert g(0) == 0.5 assert g(1) == 0.5 assert g(2) == 0.5 +def test_zeropad(): + g = ZeroPad([0.4, 0.6]) + assert g(-1) == 0 + assert g(0) == 0.4 + assert g(1) == 0.6 + assert g(2) == 0 + + +def test_sinusoid(): + g = Sinusoid(0.5) + assert g(0) == 0 + assert g(1) == 1 + assert g(2) == pytest.approx(0) + assert g(3) == -1 + + g = Sinusoid(0.5, 0.25) + assert g(0) == pytest.approx(sqrt(2) / 2) + assert g(1) == pytest.approx(sqrt(2) / 2) + assert g(2) == pytest.approx(-sqrt(2) / 2) + assert g(3) == pytest.approx(-sqrt(2) / 2) + + def test_addition(): g = Impulse() + Impulse(2) + assert g(-1) == 0 assert g(0) == 1 assert g(1) == 0 assert g(2) == 1 assert g(3) == 0 g = 1 + Impulse(2) + assert g(-1) == 1 assert g(0) == 1 assert g(1) == 1 assert g(2) == 2 assert g(3) == 1 g = Impulse(1) + 1 + assert g(-1) == 1 assert g(0) == 1 assert g(1) == 2 assert g(2) == 1 @@ -59,18 +94,21 @@ def test_addition(): def test_subtraction(): g = Impulse() - Impulse(2) + assert g(-1) == 0 assert g(0) == 1 assert g(1) == 0 assert g(2) == -1 assert g(3) == 0 g = 1 - Impulse(2) + assert g(-1) == 1 assert g(0) == 1 assert g(1) == 1 assert g(2) == 0 assert g(3) == 1 g = Impulse(2) - 1 + assert g(-1) == -1 assert g(0) == -1 assert g(1) == -1 assert g(2) == 0 @@ -79,6 +117,13 @@ def test_subtraction(): def test_multiplication(): g = Impulse() * 0.5 + assert g(-1) == 0 assert g(0) == 0.5 assert g(1) == 0 assert g(2) == 0 + + g = 2 * Sinusoid(0.5, 0.25) + assert g(0) == pytest.approx(sqrt(2)) + assert g(1) == pytest.approx(sqrt(2)) + assert g(2) == pytest.approx(-sqrt(2)) + assert g(3) == pytest.approx(-sqrt(2))