From a3a88c9830a5eca9316b5ae6cf72ead31c358ed4 Mon Sep 17 00:00:00 2001 From: ws50529 Date: Thu, 14 May 2026 13:59:45 +0800 Subject: [PATCH] refactor: clean up dependencies and modernize build assets for improved stability --- dea/bode.py | 53 +- dea/schemas.py | 4 + dea_api.py | 14 +- src/App.vue | 6 +- src/app-options.js | 2224 ++++++++++++++++++++--------------------- src/style.css | 351 +++++-- static/build/app.css | 2 +- static/build/app.js | 4 +- tests/test_dea_api.py | 24 +- 9 files changed, 1476 insertions(+), 1206 deletions(-) mode change 100644 => 100755 src/app-options.js diff --git a/dea/bode.py b/dea/bode.py index e888b25..dea37c1 100755 --- a/dea/bode.py +++ b/dea/bode.py @@ -1,26 +1,49 @@ import numpy as np +from fastapi import HTTPException from scipy import signal from .config import BODE_MAX_MULTIPLIER, BODE_POINTS -from .schemas import BodeParams +from .schemas import BodeCompareParams, BodeParams from .validation import require_finite, validate_coefficients -def calculate_bode_response(params: BodeParams): - fs_val = require_finite(params.fs, "fs") - b_vals = validate_coefficients(params.b, "b") - a_vals = validate_coefficients(params.a, "a") - f_min = params.fs / 50000.0 - f_max = params.fs * BODE_MAX_MULTIPLIER - f_eval = np.logspace(np.log10(f_min), np.log10(f_max), BODE_POINTS) - +def _bode_series(b_values, a_values, f_eval, fs_val): + b_vals = validate_coefficients(b_values, "b") + a_vals = validate_coefficients(a_values, "a") _, h = signal.freqz(b_vals, a_vals, worN=f_eval, fs=fs_val) - mag = 20 * np.log10(np.abs(h) + 1e-12) - phase = np.angle(h, deg=True) - return { - "freq": f_eval.tolist(), - "mag": mag.tolist(), - "phase": phase.tolist(), + "mag": (20 * np.log10(np.abs(h) + 1e-12)).tolist(), + "phase": np.angle(h, deg=True).tolist(), } + +def _frequency_axis(fs): + fs_val = require_finite(fs, "fs") + f_min = fs_val / 50000.0 + f_max = fs_val * BODE_MAX_MULTIPLIER + return fs_val, np.logspace(np.log10(f_min), np.log10(f_max), BODE_POINTS) + + +def calculate_bode_response(params: BodeParams): + fs_val, f_eval = _frequency_axis(params.fs) + series = _bode_series(params.b, params.a, f_eval, fs_val) + return { + "freq": f_eval.tolist(), + "mag": series["mag"], + "phase": series["phase"], + } + + +def calculate_bode_compare_response(params: BodeCompareParams): + fs_val, f_eval = _frequency_axis(params.ideal.fs) + fixed_fs_val = require_finite(params.fixed.fs, "fixed.fs") + if not np.isclose(fixed_fs_val, fs_val): + raise HTTPException(status_code=400, detail="ideal 與 fixed 的 fs 必須相同") + + ideal = _bode_series(params.ideal.b, params.ideal.a, f_eval, fs_val) + fixed = _bode_series(params.fixed.b, params.fixed.a, f_eval, fs_val) + return { + "freq": f_eval.tolist(), + "ideal": ideal, + "fixed": fixed, + } diff --git a/dea/schemas.py b/dea/schemas.py index 5977015..61e2ba2 100755 --- a/dea/schemas.py +++ b/dea/schemas.py @@ -38,3 +38,7 @@ class BodeParams(BaseModel): a: List[float] = Field(min_length=1, max_length=MAX_COEFFICIENTS) fs: float = Field(gt=0) + +class BodeCompareParams(BaseModel): + ideal: BodeParams + fixed: BodeParams diff --git a/dea_api.py b/dea_api.py index 560f733..0d5fabd 100755 --- a/dea_api.py +++ b/dea_api.py @@ -2,7 +2,7 @@ from fastapi import FastAPI, File, Form, HTTPException, Request, UploadFile from fastapi.responses import RedirectResponse, StreamingResponse from fastapi.staticfiles import StaticFiles -from dea.bode import calculate_bode_response +from dea.bode import calculate_bode_compare_response, calculate_bode_response from dea.config import ( BODE_MAX_MULTIPLIER, BODE_POINTS, @@ -19,7 +19,7 @@ from dea.csv_processing import ( read_csv_upload, ) from dea.filter_design import design_response -from dea.schemas import BodeParams, DesignParams +from dea.schemas import BodeCompareParams, BodeParams, DesignParams from dea.security import ( SECURITY_HEADERS, add_security_headers, @@ -75,6 +75,16 @@ def calculate_bode(params: BodeParams): raise HTTPException(status_code=400, detail=str(e)) +@app.post("/api/bode/compare") +def calculate_bode_compare(params: BodeCompareParams): + try: + return calculate_bode_compare_response(params) + except HTTPException: + raise + except Exception as e: + raise HTTPException(status_code=400, detail=str(e)) + + @app.post("/api/filter") async def filter_csv( file: UploadFile = File(...), diff --git a/src/App.vue b/src/App.vue index fd84c8e..c56c167 100644 --- a/src/App.vue +++ b/src/App.vue @@ -73,7 +73,7 @@ 濾波器設計 + class="text-[10px] font-bold role-bg-secondary-soft role-border-secondary-soft role-text-secondary role-hover-secondary-soft border px-2 py-1 rounded transition-colors uppercase">重設設計