Spaces:
Sleeping
Sleeping
| import gradio as gr | |
| import numpy as np | |
| from control.matlab import tf, pole, zero, frequency_response, bode | |
| from sympy.polys.polytools import poly_from_expr | |
| import plotly.graph_objects as go | |
| from plotly.subplots import make_subplots | |
| def poly_handler(poly_str: str) -> list[float]: | |
| try: | |
| if poly_str.isdigit(): | |
| poly_coffs = [poly_str] | |
| else: | |
| poly = poly_from_expr(poly_str)[0] | |
| poly_coffs = poly.all_coeffs() | |
| return list(map(float, poly_coffs)) | |
| except Exception as e: | |
| raise ValueError(f"Error parsing polynomial: {e}") | |
| def bode_information(poly_upper: str, poly_lower: str) -> tuple: | |
| upper_poly_coffs = poly_handler(poly_upper) | |
| lower_poly_coffs = poly_handler(poly_lower) | |
| num, den = np.array(upper_poly_coffs), np.array(lower_poly_coffs) | |
| try: | |
| trans_func_g = tf(num, den) | |
| except Exception as e: | |
| raise ValueError(f"Error creating transfer function: {e}") | |
| pole_result = pole(trans_func_g) | |
| zero_result = zero(trans_func_g) | |
| mag, phase, omega = frequency_response(trans_func_g) | |
| return mag, phase, omega | |
| def to_latex(poly_upper: str, poly_lower: str) -> str: | |
| upper_poly_coffs = poly_handler(poly_upper) | |
| lower_poly_coffs = poly_handler(poly_lower) | |
| def process_item(i: float, c: float): | |
| c = f"+ {c}" if c >= 0 else f"- {-c}" | |
| if i == 0: | |
| return f"{c}" | |
| if i == 1: | |
| return f"{c} s" | |
| return f"{c} s^{i}" | |
| upper_str = "".join( | |
| [process_item(i, c) for i, c in enumerate(upper_poly_coffs[::-1]) if c != 0] | |
| ) | |
| lower_str = "".join( | |
| [process_item(i, c) for i, c in enumerate(lower_poly_coffs[::-1]) if c != 0] | |
| ) | |
| if upper_str.startswith("+ "): | |
| upper_str = upper_str[2:] | |
| if lower_str.startswith("+ "): | |
| lower_str = lower_str[2:] | |
| latex_str = r"\frac{" + upper_str + r"}{" + lower_str + r"}" | |
| return latex_str | |
| def bode_graph(mag: np.ndarray, phase: np.ndarray, omega: np.ndarray): | |
| mag_db = 20 * np.log10(mag) | |
| fig = make_subplots( | |
| rows=1, | |
| cols=2, | |
| subplot_titles=( | |
| "Bode Plot - Magnitude", | |
| "Bode Plot - Phase", | |
| ), | |
| ) | |
| fig.add_trace( | |
| go.Scatter(x=omega, y=mag_db, mode="lines", name="Magnitude"), row=1, col=1 | |
| ) | |
| fig.add_trace( | |
| go.Scatter(x=omega, y=phase, mode="lines", name="Phase"), row=1, col=2 | |
| ) | |
| fig.update_yaxes(title_text="Magnitude (dB)", row=1, col=1) | |
| fig.update_yaxes(title_text="Phase (degrees)", row=1, col=2) | |
| fig.update_xaxes(type="log", title_text="Frequency (rad/s)", row=1, col=1) | |
| fig.update_xaxes(type="log", title_text="Frequency (rad/s)", row=1, col=2) | |
| fig.update_layout(title_text="Bode Plot") | |
| return fig | |
| def run_it(upper_poly: str, lower_poly: str): | |
| mag, phase, omega = bode_information(upper_poly, lower_poly) | |
| latex_str = to_latex(upper_poly, lower_poly) | |
| fig = bode_graph(mag, phase, omega) | |
| return fig, f"$$H(s)={latex_str}$$" | |
| def main(): | |
| with gr.Blocks() as demo: | |
| gr.Markdown("# Bode Plotter") | |
| gr.Markdown("This app allows you to plot Bode plots for transfer functions.") | |
| gr.Markdown( | |
| "Enter the transfer function in the form of a string, e.g., '1/(s^2 + 2*s + 1)'." | |
| ) | |
| upper_input = gr.Textbox( | |
| label="Numerator Polynomial", | |
| placeholder="Enter the numerator polynomial (e.g., '1 - 2*s + 3*s^2')", | |
| lines=2, | |
| ) | |
| lower_input = gr.Textbox( | |
| label="Denominator Polynomial", | |
| placeholder="Enter the denominator polynomial (e.g., '1 + 4*s + 5*s^2')", | |
| lines=2, | |
| ) | |
| run_btn = gr.Button("Plot Bode", variant="primary") | |
| gr.Markdown("### Bode Plot") | |
| with gr.Column(): | |
| latex_output = gr.Markdown(label="Transfer Function LaTeX") | |
| bode_plot = gr.Plot(label="Bode Plot") | |
| run_btn.click( | |
| run_it, | |
| inputs=[ | |
| upper_input, | |
| lower_input, | |
| ], | |
| outputs=[bode_plot, latex_output], | |
| ) | |
| demo.launch() | |
| if __name__ == "__main__": | |
| main() | |