KYLiN724 commited on
Commit
334ba8c
·
1 Parent(s): 2b2deaf

init commit

Browse files
Files changed (5) hide show
  1. README.md +4 -1
  2. app.py +144 -0
  3. pyproject.toml +14 -0
  4. requirements.txt +71 -0
  5. uv.lock +0 -0
README.md CHANGED
@@ -5,9 +5,12 @@ colorFrom: yellow
5
  colorTo: blue
6
  sdk: gradio
7
  sdk_version: 5.33.0
 
8
  app_file: app.py
9
  pinned: false
10
  license: mit
11
  ---
12
 
13
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
5
  colorTo: blue
6
  sdk: gradio
7
  sdk_version: 5.33.0
8
+ python_version: 3.12
9
  app_file: app.py
10
  pinned: false
11
  license: mit
12
  ---
13
 
14
+ # Bode Plotter
15
+
16
+ > Written by KYLiN
app.py ADDED
@@ -0,0 +1,144 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import numpy as np
3
+ from control.matlab import tf, pole, zero, frequency_response, bode
4
+ from sympy.polys.polytools import poly_from_expr
5
+ import plotly.graph_objects as go
6
+ from plotly.subplots import make_subplots
7
+
8
+
9
+ def poly_handler(poly_str: str) -> list[float]:
10
+ try:
11
+ if poly_str.isdigit():
12
+ poly_coffs = [poly_str]
13
+ else:
14
+ poly = poly_from_expr(poly_str)[0]
15
+ poly_coffs = poly.all_coeffs()
16
+
17
+ return list(map(float, poly_coffs))
18
+
19
+ except Exception as e:
20
+ raise ValueError(f"Error parsing polynomial: {e}")
21
+
22
+
23
+ def bode_information(poly_upper: str, poly_lower: str) -> tuple:
24
+ upper_poly_coffs = poly_handler(poly_upper)
25
+ lower_poly_coffs = poly_handler(poly_lower)
26
+
27
+ num, den = np.array(upper_poly_coffs), np.array(lower_poly_coffs)
28
+
29
+ try:
30
+ trans_func_g = tf(num, den)
31
+ except Exception as e:
32
+ raise ValueError(f"Error creating transfer function: {e}")
33
+
34
+ pole_result = pole(trans_func_g)
35
+ zero_result = zero(trans_func_g)
36
+ mag, phase, omega = frequency_response(trans_func_g)
37
+
38
+ return mag, phase, omega
39
+
40
+
41
+ def to_latex(poly_upper: str, poly_lower: str) -> str:
42
+ upper_poly_coffs = poly_handler(poly_upper)
43
+ lower_poly_coffs = poly_handler(poly_lower)
44
+
45
+ def process_item(i: float, c: float):
46
+ c = f"+ {c}" if c >= 0 else f"- {-c}"
47
+
48
+ if i == 0:
49
+ return f"{c}"
50
+ if i == 1:
51
+ return f"{c} s"
52
+
53
+ return f"{c} s^{i}"
54
+
55
+ upper_str = "".join(
56
+ [process_item(i, c) for i, c in enumerate(upper_poly_coffs[::-1]) if c != 0]
57
+ )
58
+ lower_str = "".join(
59
+ [process_item(i, c) for i, c in enumerate(lower_poly_coffs[::-1]) if c != 0]
60
+ )
61
+
62
+ if upper_str.startswith("+ "):
63
+ upper_str = upper_str[2:]
64
+ if lower_str.startswith("+ "):
65
+ lower_str = lower_str[2:]
66
+
67
+ latex_str = r"\frac{" + upper_str + r"}{" + lower_str + r"}"
68
+
69
+ return latex_str
70
+
71
+
72
+ def bode_graph(mag: np.ndarray, phase: np.ndarray, omega: np.ndarray):
73
+ mag_db = 20 * np.log10(mag)
74
+ fig = make_subplots(
75
+ rows=1,
76
+ cols=2,
77
+ subplot_titles=(
78
+ "Bode Plot - Magnitude",
79
+ "Bode Plot - Phase",
80
+ ),
81
+ )
82
+
83
+ fig.add_trace(
84
+ go.Scatter(x=omega, y=mag_db, mode="lines", name="Magnitude"), row=1, col=1
85
+ )
86
+ fig.add_trace(
87
+ go.Scatter(x=omega, y=phase, mode="lines", name="Phase"), row=1, col=2
88
+ )
89
+
90
+ fig.update_yaxes(title_text="Magnitude (dB)", row=1, col=1)
91
+ fig.update_yaxes(title_text="Phase (degrees)", row=1, col=2)
92
+ fig.update_xaxes(type="log", title_text="Frequency (rad/s)", row=1, col=1)
93
+ fig.update_xaxes(type="log", title_text="Frequency (rad/s)", row=1, col=2)
94
+ fig.update_layout(title_text="Bode Plot")
95
+
96
+ return fig
97
+
98
+
99
+ def run_it(upper_poly: str, lower_poly: str):
100
+ mag, phase, omega = bode_information(upper_poly, lower_poly)
101
+ latex_str = to_latex(upper_poly, lower_poly)
102
+ fig = bode_graph(mag, phase, omega)
103
+
104
+ return fig, f"$$H(s)={latex_str}$$"
105
+
106
+
107
+ def main():
108
+ with gr.Blocks() as demo:
109
+ gr.Markdown("# Bode Plotter")
110
+ gr.Markdown("This app allows you to plot Bode plots for transfer functions.")
111
+ gr.Markdown(
112
+ "Enter the transfer function in the form of a string, e.g., '1/(s^2 + 2*s + 1)'."
113
+ )
114
+ upper_input = gr.Textbox(
115
+ label="Numerator Polynomial",
116
+ placeholder="Enter the numerator polynomial (e.g., '1 - 2*s + 3*s^2')",
117
+ lines=2,
118
+ )
119
+ lower_input = gr.Textbox(
120
+ label="Denominator Polynomial",
121
+ placeholder="Enter the denominator polynomial (e.g., '1 + 4*s + 5*s^2')",
122
+ lines=2,
123
+ )
124
+ run_btn = gr.Button("Plot Bode", variant="primary")
125
+ gr.Markdown("### Bode Plot")
126
+
127
+ with gr.Column():
128
+ latex_output = gr.Markdown(label="Transfer Function LaTeX")
129
+ bode_plot = gr.Plot(label="Bode Plot")
130
+
131
+ run_btn.click(
132
+ run_it,
133
+ inputs=[
134
+ upper_input,
135
+ lower_input,
136
+ ],
137
+ outputs=[bode_plot, latex_output],
138
+ )
139
+
140
+ demo.launch()
141
+
142
+
143
+ if __name__ == "__main__":
144
+ main()
pyproject.toml ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [project]
2
+ name = "bode-plotter"
3
+ version = "0.1.0"
4
+ description = "Add your description here"
5
+ readme = "README.md"
6
+ requires-python = ">=3.12"
7
+ dependencies = [
8
+ "black>=25.1.0",
9
+ "control>=0.10.1",
10
+ "gradio>=5.33.0",
11
+ "loguru>=0.7.3",
12
+ "plotly>=6.1.2",
13
+ "sympy>=1.14.0",
14
+ ]
requirements.txt ADDED
@@ -0,0 +1,71 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ aiofiles==24.1.0
2
+ annotated-types==0.7.0
3
+ anyio==4.9.0
4
+ black==25.1.0
5
+ certifi==2025.4.26
6
+ charset-normalizer==3.4.2
7
+ click==8.2.1
8
+ contourpy==1.3.2
9
+ control==0.10.1
10
+ cycler==0.12.1
11
+ fastapi==0.115.12
12
+ ffmpy==0.6.0
13
+ filelock==3.18.0
14
+ fonttools==4.58.1
15
+ fsspec==2025.5.1
16
+ gradio==5.33.0
17
+ gradio-client==1.10.2
18
+ groovy==0.1.2
19
+ h11==0.16.0
20
+ hf-xet==1.1.3
21
+ httpcore==1.0.9
22
+ httpx==0.28.1
23
+ huggingface-hub==0.32.4
24
+ idna==3.10
25
+ jinja2==3.1.6
26
+ kiwisolver==1.4.8
27
+ loguru==0.7.3
28
+ markdown-it-py==3.0.0
29
+ markupsafe==3.0.2
30
+ matplotlib==3.10.3
31
+ mdurl==0.1.2
32
+ mpmath==1.3.0
33
+ mypy-extensions==1.1.0
34
+ narwhals==1.41.0
35
+ numpy==2.2.6
36
+ orjson==3.10.18
37
+ packaging==25.0
38
+ pandas==2.3.0
39
+ pathspec==0.12.1
40
+ pillow==11.2.1
41
+ platformdirs==4.3.8
42
+ plotly==6.1.2
43
+ pydantic==2.11.5
44
+ pydantic-core==2.33.2
45
+ pydub==0.25.1
46
+ pygments==2.19.1
47
+ pyparsing==3.2.3
48
+ python-dateutil==2.9.0.post0
49
+ python-multipart==0.0.20
50
+ pytz==2025.2
51
+ pyyaml==6.0.2
52
+ requests==2.32.3
53
+ rich==14.0.0
54
+ ruff==0.11.13
55
+ safehttpx==0.1.6
56
+ scipy==1.15.3
57
+ semantic-version==2.10.0
58
+ shellingham==1.5.4
59
+ six==1.17.0
60
+ sniffio==1.3.1
61
+ starlette==0.46.2
62
+ sympy==1.14.0
63
+ tomlkit==0.13.3
64
+ tqdm==4.67.1
65
+ typer==0.16.0
66
+ typing-extensions==4.14.0
67
+ typing-inspection==0.4.1
68
+ tzdata==2025.2
69
+ urllib3==2.4.0
70
+ uvicorn==0.34.3
71
+ websockets==15.0.1
uv.lock ADDED
The diff for this file is too large to render. See raw diff