Skip to main content

Lightweight library for drawing signal processing block diagrams with any orientation and several branches using Matplotlib

Project description

BlockDiagrams

Documentation Status

BlockDiagrams is a lightweight Python library for drawing signal processing block diagrams with any orientation and several branches using Matplotlib.

It simplifies the visual creation of system and signal diagrams with functions to add blocks, lines, arrows, summation nodes, and multipliers.

v1.4.1

Features

  • Draw rectangular blocks with LaTeX math text.
  • Add signal arrows with descriptive labels.
  • Include summation and multiplication nodes for system diagrams.
  • Horizontal, vertical (up and down), or any angle orientation.
  • Easy to use and extend for custom diagrams.
  • Automatic or manual position of elements.
  • Threads for several lines in diagrams.
  • Feedback branches.

New features

v1.4:

  • Added support for feedback branches.
  • Added 'angled_arrow' element.
  • Elements positions accesible through new get_position() method: ('input_pos', 'output_pos' and 'feedback_pos').

v1.3:

  • Allow for different orientation of elements.
  • 'horizontal', 'vertical', 'up', 'down' or any angle.
  • For 'vertical', 'up' and 'down' orientations text is mantained horizontal.

v1.2:

  • 'block' allows bottom and top inputs.
  • 'block' allows center, above or below text.
  • 'block' allows linestyles.
  • 'block_uparrow' unified within 'block'.
  • New get_position() method.

v1.1:

  • Threads feature added.
  • 'combiner' and 'mult_combiner' modified not to include io arrows.

v1.0:

  • Initial version.

Requirements

  • Python >= 3.7
  • Matplotlib
  • NumPy
  • Dataclasses
  • Typing

Installation

The library is not yet published on PyPI. You can clone the repository directly and execute:

git clone https://github.com/usuario/blockdiagrams.git
cd blockdiagrams
pip install -e .

Then simply import the BlockDiagrams.py module into your project.


Basic Usage

from blockdiagrams import DiagramBuilder

db = DiagramBuilder(block_length=1, fontsize=16)

# Diagram drawing
db.add("x(t)", kind="input")
db.add("h_{aa}(t)", kind="block")
db.add("x_c(t)", kind="arrow")
db.add("mult", kind="combiner", input_text="p(t)", operation='mult', input_side='bottom')
db.add("x_p(t)", kind="arrow")
db.add("C/D", kind="block", input_text="T_s", input_side='bottom')
db.add("x_d[n]", kind="arrow")
db.add("D/C", kind="block")
db.add("x_p(t)", kind="arrow")
db.add("h_r(t)", kind="block")
db.add("x_r(t)", kind="output")

#db.show()
db.show(savepath = "diag1.png")

Block Diagram


More complex examples

from blockdiagrams import DiagramBuilder

db = DiagramBuilder(block_length=1, fontsize=16)

# Diagram drawing
db.add("x(t)", kind="input")
db.add("h_{aa}(t)", kind="block")
db.add("x_c(t)", kind="arrow", length=2)
left_pos = db.get_position()
db.add("mult", kind="combiner", input_text="p(t)", operation='mult', input_side='bottom')
db.add("x_p(t)", kind="arrow")
db.add("C/D", kind="block", input_text="T_s", input_side='bottom')
db.add("x_d[n]", kind="arrow")
db.add("h_d[n]", kind="block")
db.add("y_d[n]", kind="arrow")
db.add("D/C", kind="block")
db.add("y_p(t)", kind="arrow")
db.add("h_r(t)", kind="block")
right_pos = db.get_position()
db.add("y_c(t)", kind="output")

# Calculation of position and size of dashed block h_c(t)
position=(left_pos[0]-0.5,left_pos[1]-0.5)
length=right_pos[0]-left_pos[0]+1
height=2.5
db.add("h_c(t)", kind="block", text=None, text_below="h_c(t)", position=position, length=length, height=height, linestyle='--')

db.show(savepath = "diag2.png")

Block Diagram

from blockdiagrams import DiagramBuilder
import numpy as np

db = DiagramBuilder(block_length=1, fontsize=16)

y_pos = np.linspace(3, -3, 5)
x_pos = np.zeros_like(y_pos)
inputs_pos = np.column_stack((x_pos, y_pos))

input_threads = []

# Input branches
for cont in np.arange(inputs_pos.shape[0]):
    thread = "line" + str(cont + 1)
    input_threads.append(thread)

    name = "x_" + str(cont + 1) +"(t)"
    db.add(name, kind="arrow", thread = thread, text_position='before',position=(inputs_pos[cont]))
    name =  "g_" + str(cont + 1) +"(t)"
    db.add(name, kind="block", thread = thread)
    name = "x_{c" + str(cont + 1) + "}(t)"
    db.add(name, kind="line", thread = thread)

# Adder
db.add("x_{sum}(t)", kind="mult_combiner", inputs=input_threads, 
       position='auto', operation='sum')

# Rest of the diagram
db.add("x_2(t)", kind="arrow")
db.add("h_1(t)", kind="block", input_text="K_1", input_side='bottom', length=1.5)
db.add("x_2(t)", kind="arrow")
db.add("mult", kind="combiner", input_text="s(t)", operation='sum', input_side='top')
db.add("x_3(t)", kind="arrow")
db.add("h_2(t)", kind="block", input_text="K_2", input_side='top')
db.add("", kind="arrow")
db.add("Q", kind="block", text_below='Quantizer')
db.add("y(t)", kind="arrow", text_position='after')

db.show(savepath = "diag3.png")

Block Diagram

from blockdiagrams import DiagramBuilder

db = DiagramBuilder(block_length=1, fontsize=16)

angle = 45

db.add("x(t)", kind="line", text_position='before', thread='upper')
pos1 = db.get_position(thread='upper')

db.add("", kind="arrow", orientation=angle, length = 1, thread='upper')
db.add("h_1(t)", kind="block", orientation=angle, text_below = "Filter1", input_text="BW_1", input_side='top', thread='upper')
db.add("", kind="line", orientation=angle, text_position='above', thread='upper')
db.add("y_1(t)", kind="arrow", text_position='above', thread='upper')
db.add("mult", kind="combiner",  input_text="\cos(\omega_0 t)", operation='mult', input_side='bottom', thread='upper')
db.add("y_i(t)", kind="line", text_position='above', thread='upper')
db.add("", kind="arrow", orientation=-angle, length = 1, thread='upper')
db.add("h_3(t)", kind="block", orientation=-angle, text_below = "Filter3", input_text="BW_1", input_side='top', thread='upper')

db.add("", kind="arrow", orientation=-angle, length = 1, thread='lower', position=pos1)
db.add("h_2(t)", kind="block", orientation=-angle, text_above = "Filter2", input_text="BW_2", input_side='bottom', thread='lower')
db.add("", kind="line", orientation=-angle, text_position='above', thread='lower')
db.add("y_2(t)", kind="arrow", text_position='above', thread='lower')
db.add("mult", kind="combiner",  input_text="\sin(\omega_0 t)", operation='mult', input_side='top', thread='lower')
db.add("y_q(t)", kind="line", text_position='above', thread='lower')
db.add("", kind="arrow", orientation=angle, length = 1, thread='lower')
db.add("h_3(t)", kind="block", orientation=angle, text_above = "Filter4", input_text="BW_2", input_side='bottom', thread='lower')

input_threads = ['upper', 'lower']
db.add("", kind="mult_combiner", inputs=input_threads, position="auto", operation='sum')
db.add("y(t)", kind="output")

db.show(savepath = "block_2_branches.png")

Block Diagram

from blockdiagrams import DiagramBuilder
import numpy as np

angle = 'vertical'

db = DiagramBuilder(block_length=1, fontsize=16)

db.add("x_1(t)", kind="input", position=(0, 1), orientation = angle)
db.add("mult", kind="combiner", input_text="e^{-j\\omega_0 t}", input_side='top', operation='mult', orientation = angle)
db.add("x_2(t)", kind="arrow", orientation = angle)
db.add("H(\\omega)", kind="block", input_side='top', input_text="a", orientation = angle)
db.add("x_3(t)", kind="arrow", orientation = angle)
db.add("mult", kind="combiner", input_text="p(t)", input_side = 'bottom', operation='sum', orientation = angle)
db.add("x_p(t)", kind="arrow", orientation = angle)
db.add("C/D", kind="block", orientation = angle)
db.add("x[n]", kind="output", orientation = angle)

db.show(savepath = "block_vertical.png")

Block Diagram

from blockdiagrams import DiagramBuilder
import numpy as np

db = DiagramBuilder(block_length=1, fontsize=16)

# Diagram drawing
db.add("x(t)", kind="input")
db.add("h_{aa}(t)", kind="combiner", operation="sum", signs=["+", "-"])
# Final position of feedback branch
feedback_position = db.get_position().feedback_pos
db.add("u(t)", kind="arrow")
db.add("h_1(t)", kind="block")
db.add("v(t)", kind="arrow")
db.add("h_2(t)", kind="block")
db.add("y(t)", kind="output", length=1)
# Initial position for feedback branch
pos1=db.element_positions[db.get_current_element()].feedback_pos
# Final position of first feedback arrow
pos2 = pos1+[-2,-1.5]

# Feedback
db.add("y(t)", kind="angled_arrow", position=pos1, final_position=pos2, thread='feedback', first_segment='vertical')
db.add("h_3(t)", kind="block", orientation='left', thread='feedback', input_text="K_3", input_side='top')
# Inital position for second feedback arrow
pos3=db.get_thread_position('feedback')
# pos3=db.get_position().output_pos
db.add("z(t)", kind="angled_arrow", position=pos3, final_position=feedback_position)

db.show(savepath = "feedback_diagram.png")

Block Diagram

Additional examples

Additional examples notebook 1 Additional examples notebook 2


Main Functions

  • DiagramBuilder(...): Creator of the class.
  • DiagramBuilder.add(...): Adds elements to the diagram.
  • DiagramBuilder.get_position(...): Gets actual head position of thread.
  • DiagramBuilder.show(...): Renders/saves diagram block.
  • DiagramBuilder.get_current_element(): Returns the current element index.
  • DiagramBuilder.get_position(...): Returns positions (input, output and feedback) of specified element.
  • DiagramBuilder.get_thread_position(...): Returns current position of the specified thread.

Upcoming Improvements

  • Complex plane representations.
  • Signals representation.

Contributing

Contributions are welcome! Please open an issue or pull request on GitHub.


License

This project is licensed under the MIT License.


Contact

For questions or suggestions, feel free to contact me via GitHub.


Thank you for using blockdiagram!

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

blockdiagrams-1.4.1.tar.gz (13.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

blockdiagrams-1.4.1-py3-none-any.whl (13.8 kB view details)

Uploaded Python 3

File details

Details for the file blockdiagrams-1.4.1.tar.gz.

File metadata

  • Download URL: blockdiagrams-1.4.1.tar.gz
  • Upload date:
  • Size: 13.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.10.4

File hashes

Hashes for blockdiagrams-1.4.1.tar.gz
Algorithm Hash digest
SHA256 4966c6eacf3d361d4df2d86baf479788250ed478f261580be102c79e0932f221
MD5 7017c15f827b55d783a3757685d304da
BLAKE2b-256 ab5797143eea11c6c63f11419ad7e58190c5f4824263c308bd8f3f886672305a

See more details on using hashes here.

File details

Details for the file blockdiagrams-1.4.1-py3-none-any.whl.

File metadata

  • Download URL: blockdiagrams-1.4.1-py3-none-any.whl
  • Upload date:
  • Size: 13.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.10.4

File hashes

Hashes for blockdiagrams-1.4.1-py3-none-any.whl
Algorithm Hash digest
SHA256 9d3f723c209d58da2af9a66447f34feca78e39a4bb6b17dd8498031945df7d2e
MD5 d1712c4912dfe74bfc5d0e78ea7acc25
BLAKE2b-256 586428e804bd996495535846b9d83221302c01d7f963d90420b103a659e26ad7

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page