.. DO NOT EDIT. .. THIS FILE WAS AUTOMATICALLY GENERATED BY SPHINX-GALLERY. .. TO MAKE CHANGES, EDIT THE SOURCE PYTHON FILE: .. "examples/firstorderiirfilter.py" .. LINE NUMBERS ARE GIVEN BELOW. .. only:: html .. note:: :class: sphx-glr-download-link-note :ref:`Go to the end ` to download the full example code. .. rst-class:: sphx-glr-example-title .. _sphx_glr_examples_firstorderiirfilter.py: ====================================== First-order IIR Filter with Simulation ====================================== In this example, a direct form first-order IIR filter is designed. First, we need to import the operations that will be used in the example: .. GENERATED FROM PYTHON SOURCE LINES 10-14 .. code-block:: Python from b_asic.core_operations import ConstantMultiplication from b_asic.special_operations import Delay, Input, Output .. GENERATED FROM PYTHON SOURCE LINES 15-17 Then, we continue by defining the input and delay element, which we can optionally name. .. GENERATED FROM PYTHON SOURCE LINES 17-21 .. code-block:: Python input = Input(name="My input") delay = Delay(name="The only delay") .. GENERATED FROM PYTHON SOURCE LINES 22-23 There are a few ways to connect signals. Either explicitly, by instantiating them: .. GENERATED FROM PYTHON SOURCE LINES 23-26 .. code-block:: Python a1 = ConstantMultiplication(0.5, delay) .. GENERATED FROM PYTHON SOURCE LINES 27-28 By operator overloading: .. GENERATED FROM PYTHON SOURCE LINES 28-31 .. code-block:: Python first_addition = a1 + input .. GENERATED FROM PYTHON SOURCE LINES 32-35 Or by creating them, but connecting the input later. Each operation has a function :func:`~b_asic.operation.Operation.input`that is used to access a specific input (or output, by using :func:`~b_asic.operation.Operation.output`). .. GENERATED FROM PYTHON SOURCE LINES 35-39 .. code-block:: Python b1 = ConstantMultiplication(0.7) b1.input(0).connect(delay) .. rst-class:: sphx-glr-script-out .. code-block:: none .. GENERATED FROM PYTHON SOURCE LINES 40-45 The latter is useful when there is not a single order to create the signal flow graph, e.g., for recursive algorithms. In this example, we could not connect the output of the delay as that was not yet available. There is also a shorthand form to connect signals using the ``<<=`` operator: .. GENERATED FROM PYTHON SOURCE LINES 45-48 .. code-block:: Python delay <<= first_addition .. GENERATED FROM PYTHON SOURCE LINES 49-50 Naturally, it is also possible to write expressions when instantiating operations: .. GENERATED FROM PYTHON SOURCE LINES 50-53 .. code-block:: Python output = Output(b1 + first_addition) .. GENERATED FROM PYTHON SOURCE LINES 54-56 Now, we should create a signal flow graph, but first it must be imported (normally, this should go at the top of the file). .. GENERATED FROM PYTHON SOURCE LINES 56-59 .. code-block:: Python from b_asic.sfg import SFG # noqa: E402 .. GENERATED FROM PYTHON SOURCE LINES 60-63 The signal flow graph is defined by its inputs and outputs, so these must be provided. As, in general, there can be multiple inputs and outputs, there should be provided as a list or a tuple. .. GENERATED FROM PYTHON SOURCE LINES 63-66 .. code-block:: Python firstorderiir = SFG([input], [output]) .. GENERATED FROM PYTHON SOURCE LINES 67-70 If this is executed in an enriched terminal, such as a Jupyter Notebook, Jupyter QtConsole, or Spyder, just typing the variable name will return a graphical representation of the signal flow graph. .. GENERATED FROM PYTHON SOURCE LINES 70-73 .. code-block:: Python firstorderiir .. raw:: html
%3 in0 My input (in0) add0 add0 in0:e->add0 1 add0.0 add0->add0.0 out0 out0 add1 add1 add1->out0:w cmul0 cmul0 cmul0->add0 0 add0.0->add1 1 t0 The only delay (t0) add0.0->t0 t0.0 t0->t0.0 cmul1 cmul1 cmul1->add1 0 t0.0->cmul0 t0.0->cmul1


.. GENERATED FROM PYTHON SOURCE LINES 74-75 For now, we can print the precedence relations of the SFG .. GENERATED FROM PYTHON SOURCE LINES 75-77 .. code-block:: Python firstorderiir.print_precedence_graph() .. rst-class:: sphx-glr-script-out .. code-block:: none ------------------------------------------------------------------------------------------------------------------------ 1.1 id: in0, name: My input, inputs: {}, outputs: {0: ['add0']} 1.2 id: t0, name: The only delay, initial_value: 0, inputs: {0: ['add0']}, outputs: {0: ['cmul1', 'cmul0']} ------------------------------------------------------------------------------------------------------------------------ 2.1 id: cmul1, name: no_name, value: 0.7, inputs: {0: ['t0']}, outputs: {0: ['add1']} 2.2 id: cmul0, name: no_name, value: 0.5, inputs: {0: ['t0']}, outputs: {0: ['add0']} ------------------------------------------------------------------------------------------------------------------------ 3.1 id: add0, name: no_name, mul_j: False, shift_output: 0, inputs: {0: ['cmul0'], 1: ['in0']}, outputs: {0: ['t0', 'add1']} ------------------------------------------------------------------------------------------------------------------------ 4.1 id: add1, name: no_name, mul_j: False, shift_output: 0, inputs: {0: ['cmul1'], 1: ['add0']}, outputs: {0: ['out0']} ------------------------------------------------------------------------------------------------------------------------ .. GENERATED FROM PYTHON SOURCE LINES 78-134 Executing ``firstorderiir.precedence_graph`` will show something like .. graphviz:: digraph { rankdir=LR subgraph cluster_0 { label=N0 "in0.0" [label=in0 height=0.1 shape=rectangle width=0.1] "t0.0" [label=t0 height=0.1 shape=rectangle width=0.1] } subgraph cluster_1 { label=N1 "cmul1.0" [label=cmul1 height=0.1 shape=rectangle width=0.1] "cmul0.0" [label=cmul0 height=0.1 shape=rectangle width=0.1] } subgraph cluster_2 { label=N2 "add0.0" [label=add0 height=0.1 shape=rectangle width=0.1] } subgraph cluster_3 { label=N3 "add1.0" [label=add1 height=0.1 shape=rectangle width=0.1] } "in0.0" -> add0 add0 [label=add0 shape=ellipse] in0 -> "in0.0" in0 [label=in0 shape=cds] "t0.0" -> cmul1 cmul1 [label=cmul1 shape=ellipse] "t0.0" -> cmul0 cmul0 [label=cmul0 shape=ellipse] t0Out -> "t0.0" t0Out [label=t0 shape=square] "cmul1.0" -> add1 add1 [label=add1 shape=ellipse] cmul1 -> "cmul1.0" cmul1 [label=cmul1 shape=ellipse] "cmul0.0" -> add0 add0 [label=add0 shape=ellipse] cmul0 -> "cmul0.0" cmul0 [label=cmul0 shape=ellipse] "add0.0" -> t0In t0In [label=t0 shape=square] "add0.0" -> add1 add1 [label=add1 shape=ellipse] add0 -> "add0.0" add0 [label=add0 shape=ellipse] "add1.0" -> out0 out0 [label=out0 shape=cds] add1 -> "add1.0" add1 [label=add1 shape=ellipse] } As seen, each operation has an id, in addition to the optional name. This can be used to access the operation. For example, .. GENERATED FROM PYTHON SOURCE LINES 134-136 .. code-block:: Python firstorderiir.find_by_id("cmul0") .. rst-class:: sphx-glr-script-out .. code-block:: none ConstantMultiplication(value=0.5) .. GENERATED FROM PYTHON SOURCE LINES 137-142 Note that this operation differs from ``a1`` defined above as the operations are copied and recreated once inserted into a signal flow graph. The signal flow graph can also be simulated. For this, we must import :class:`.Simulation`. .. GENERATED FROM PYTHON SOURCE LINES 142-145 .. code-block:: Python from b_asic.simulation import Simulation # noqa: E402 .. GENERATED FROM PYTHON SOURCE LINES 146-151 The :class:`.Simulation` class require that we provide inputs. These can either be arrays of values or we can use functions that provides the values when provided a time index. Let us create a simulation that simulates a short impulse response: .. GENERATED FROM PYTHON SOURCE LINES 151-154 .. code-block:: Python sim = Simulation(firstorderiir, [[1, 0, 0, 0, 0]]) .. GENERATED FROM PYTHON SOURCE LINES 155-156 To run the simulation for all input samples, we do: .. GENERATED FROM PYTHON SOURCE LINES 156-159 .. code-block:: Python sim.run() .. rst-class:: sphx-glr-script-out .. code-block:: none [0.15] .. GENERATED FROM PYTHON SOURCE LINES 160-164 The returned value is the output after the final iteration. However, we may often be interested in the results from the whole simulation. The results from the simulation, which is a dictionary of all the nodes in the signal flow graph, can be obtained as .. GENERATED FROM PYTHON SOURCE LINES 164-167 .. code-block:: Python sim.results .. rst-class:: sphx-glr-script-out .. code-block:: none {'in0': array([1, 0, 0, 0, 0]), 't0': array([0. , 1. , 0.5 , 0.25 , 0.125]), 'cmul1': array([0. , 0.7 , 0.35 , 0.175 , 0.0875]), 'cmul0': array([0. , 0.5 , 0.25 , 0.125 , 0.0625]), 'add0': array([1. , 0.5 , 0.25 , 0.125 , 0.0625]), 'add1': array([1. , 1.2 , 0.6 , 0.3 , 0.15]), 'out0': array([1. , 1.2 , 0.6 , 0.3 , 0.15])} .. GENERATED FROM PYTHON SOURCE LINES 168-170 Hence, we can obtain the results that we are interested in and, for example, plot the output and the value after the first addition: .. GENERATED FROM PYTHON SOURCE LINES 170-179 .. code-block:: Python import matplotlib.pyplot as plt # noqa: E402 plt.plot(sim.results["out0"], label="Output") plt.plot(sim.results["add0"], label="After first addition") plt.legend() plt.show() .. image-sg:: /examples/images/sphx_glr_firstorderiirfilter_001.png :alt: firstorderiirfilter :srcset: /examples/images/sphx_glr_firstorderiirfilter_001.png :class: sphx-glr-single-img .. GENERATED FROM PYTHON SOURCE LINES 180-181 To compute and plot the frequency response, it is possible to use mplsignal .. GENERATED FROM PYTHON SOURCE LINES 181-188 .. code-block:: Python from mplsignal.freq_plots import freqz_fir # noqa: E402 freqz_fir(sim.results["out0"]) plt.show() .. image-sg:: /examples/images/sphx_glr_firstorderiirfilter_002.png :alt: firstorderiirfilter :srcset: /examples/images/sphx_glr_firstorderiirfilter_002.png :class: sphx-glr-single-img .. GENERATED FROM PYTHON SOURCE LINES 189-195 As seen, the output has not converged to zero, leading to that the frequency-response may not be correct, so we want to simulate for a longer time. Instead of just adding zeros to the input array, we can use a function that generates the impulse response instead. There are a number of those defined in B-ASIC for convenience, and the one for an impulse response is called :class:`.Impulse`. .. GENERATED FROM PYTHON SOURCE LINES 195-200 .. code-block:: Python from b_asic.signal_generator import Impulse # noqa: E402 sim = Simulation(firstorderiir, [Impulse()]) .. GENERATED FROM PYTHON SOURCE LINES 201-204 Now, as the functions will not have an end, we must run the simulation for a given number of cycles, say 30. This is done using :func:`~b_asic.simulation.Simulation.run_for` instead: .. GENERATED FROM PYTHON SOURCE LINES 204-207 .. code-block:: Python sim.run_for(30) .. rst-class:: sphx-glr-script-out .. code-block:: none [4.470348358154297e-09] .. GENERATED FROM PYTHON SOURCE LINES 208-209 Now, plotting the impulse results gives: .. GENERATED FROM PYTHON SOURCE LINES 209-213 .. code-block:: Python plt.plot(sim.results["out0"]) plt.show() .. image-sg:: /examples/images/sphx_glr_firstorderiirfilter_003.png :alt: firstorderiirfilter :srcset: /examples/images/sphx_glr_firstorderiirfilter_003.png :class: sphx-glr-single-img .. GENERATED FROM PYTHON SOURCE LINES 214-215 And the frequency-response: .. GENERATED FROM PYTHON SOURCE LINES 215-218 .. code-block:: Python freqz_fir(sim.results["out0"]) plt.show() .. image-sg:: /examples/images/sphx_glr_firstorderiirfilter_004.png :alt: firstorderiirfilter :srcset: /examples/images/sphx_glr_firstorderiirfilter_004.png :class: sphx-glr-single-img .. rst-class:: sphx-glr-timing **Total running time of the script:** (0 minutes 0.327 seconds) .. _sphx_glr_download_examples_firstorderiirfilter.py: .. only:: html .. container:: sphx-glr-footer sphx-glr-footer-example .. container:: sphx-glr-download sphx-glr-download-jupyter :download:`Download Jupyter notebook: firstorderiirfilter.ipynb ` .. container:: sphx-glr-download sphx-glr-download-python :download:`Download Python source code: firstorderiirfilter.py ` .. container:: sphx-glr-download sphx-glr-download-zip :download:`Download zipped: firstorderiirfilter.zip ` .. only:: html .. rst-class:: sphx-glr-signature `Gallery generated by Sphinx-Gallery `_