# Difference Equation Analyzer Difference Equation Analyzer 是用來設計、檢視與驗證離散時間濾波器的工具。它同時支援浮點係數、fixed-point 係數、時域 CSV 驗證,以及透過 Web Serial 將係數寫入 MCU。 Backend 使用 FastAPI、NumPy、SciPy 與 Pandas;Frontend 使用 Vue 3、Vite、Tailwind CSS 與 Plotly。 ## Features - 設計 Lowpass、Highpass、Bandpass、Notch、1P1Z、2P1Z、2P2Z、PID、SOGI 濾波器。 - 比較 Ideal Float 與 Fixed-Point 的 Bode 頻率響應。 - 支援多個 cascade stages 的頻率響應與時域模擬,並可個別啟用或 bypass。 - 手動微調 b/a 係數、a1/a2、δ/r、Q-format 位元數與 fixed-point 整數係數。 - 上傳 CSV 時域資料,繪製輸入、Ideal Float 輸出與 Mimic Integer 輸出。 - 匯出包含理想浮點與定點整數模擬輸出的 CSV;cascade workflow 可匯出乾淨的 4 欄 CSV。 - 使用瀏覽器 Web Serial API 將 fixed-point 係數寫入 MCU。 - 時域圖表支援 zoom 後以 `start_idx` / `end_idx` 向後端重取可視區間資料,保留 IIR 完整歷史狀態。 - 支援 Dark Mode 與響應式 UI。 ## Quick Start ```bash cd /home/wisetop/diff-eq-analyzer python3 -m venv .venv .venv/bin/pip install -r requirements.txt npm install npm run build bash certs/generate_cert.sh .venv/bin/uvicorn dea_api:app --host 0.0.0.0 --port 8000 --ssl-keyfile certs/key.pem --ssl-certfile certs/cert.pem ``` 開啟: ```text https://192.168.2.58:8000/ui/ ``` 本專案固定從 FastAPI 提供的 HTTPS UI 使用。不要用 Vite dev server URL 測試 CSV 或 MCU 功能,否則 `/api/...` 可能會回 `Not Found`。 自簽憑證會讓瀏覽器顯示安全性警告,第一次開啟時需手動信任或選擇繼續前往。 ## Project Layout ```text dea_api.py FastAPI 入口與 HTTP route dea/ 後端核心模組 bode.py 頻率響應計算 config.py 限制值與伺服器端 serial 設定 csv_processing.py CSV 快取、驗證、單濾波器/cascade 濾波與匯出 filter_design.py 濾波器設計 mcu.py MCU serial port 列表與命令寫入 schemas.py Pydantic request schema security.py LAN 存取限制與安全標頭 validation.py 係數、頻率與有限值驗證 src/ Vue 3 前端原始碼 certs/ HTTPS 自簽憑證與產生腳本 static/build/ Vite production build 輸出 tests/ unittest 測試 dea.service systemd service 範例 chirp_signal.csv CSV 上傳測試範例 ``` ## Architecture | Layer | Responsibility | | --- | --- | | FastAPI | 提供 API、serve `/ui/`、套用 LAN 存取限制與安全標頭 | | `dea/` | 濾波器設計、單濾波器/cascade Bode plot、CSV 處理、fixed-point simulation、驗證 | | Vue UI | 管理係數、Q-format、CSV workflow、Plotly 圖表與 Web Serial 狀態 | | Vite build | 將 `src/` 打包到 `static/build/`,由 FastAPI 在 production 提供 | CSV workflow: 1. 前端選擇 CSV。 2. 前端只讀取檔案前段內容建立欄位與 preview,避免為了 preview 把大型 CSV 全部載入瀏覽器記憶體。 3. 前端呼叫 `POST /api/csv/upload`,後端將檔案暫存並回傳 `file_id`。 4. 上傳完成前,filter 按鈕會保持 disabled,避免尚未取得 `file_id` 就送出處理。 5. 後續 `POST /api/filter` 與 `POST /api/filter/download` 使用 `file_id`,避免反覆傳大檔。 CSV 預設暫存在: ```text /tmp/diff-eq-analyzer-csv/ ``` 可用環境變數覆寫: ```bash DEA_TEMP_CSV_DIR=/path/to/csv-cache ``` 暫存 CSV 預設保留 24 小時。可用環境變數調整,設為 `0` 可停用清理: ```bash DEA_TEMP_CSV_TTL_SECONDS=86400 ``` ## Usage ### Filter Design 在左側控制面板選擇濾波器類型並調整參數。系統會更新 b/a 係數,並繪製 Ideal Float 與 Fixed-Point 的頻率響應。 支援濾波器: ```text Lowpass, Highpass, Bandpass, Notch, 1P1Z, 2P1Z, 2P2Z, PID, SOGI-Alpha, SOGI-Beta ``` ### a1/a2 Fine Tuning 針對二階濾波器,UI 支援 δ/r 參數化微調。 | Mode | Formula | | --- | --- | | Float | `a1 = -1 - r`, `a2 = r + δ` | | Fixed | `a1 = -a0 - r`, `a2 = r + δ` | 反推關係: | Mode | δ | r | | --- | --- | --- | | Float | `a1 + a2 + 1` | `-1 - a1` | | Fixed | `a1 + a2 + a0` | `-a0 - a1` | 桌機可用 Shift + 滑鼠滾輪微調;觸控裝置可按住數值面板上下拖曳。 ### Cascade Stages UI 以 cascade stage accordion 管理多段濾波器。每個 stage 顯示序號、濾波器型態、Active/Bypass 狀態、係數摘要與 Q-format 摘要;可用上移/下移調整串接順序。 完整的濾波器設計、浮點係數、fixed-point 係數與 MCU 寫值控制只會出現在目前選取的 active stage accordion 內。同時間只有一個 active stage 承載完整 editor,避免多份表單同時修改同一組全域 active-stage state。展開非 active stage 時,UI 會先切換該 stage 為 active stage。 送出 Bode 或 CSV 時,前端會先同步目前 active stage,再把所有 stages 依畫面順序打包送到後端。單一 stage 時仍等同 legacy 單濾波器 workflow。 後端相容兩種路徑: | Mode | API Behavior | | --- | --- | | Legacy single filter | `/api/bode`、`/api/bode/compare`、`/api/filter` 仍接受原本的 `b/a/b_int/a_int` 參數 | | Cascade | `/api/bode/compare_cascade` 接受 stages;`/api/filter` 與 `/api/filter/download` 可接受 `stages` form payload | Cascade 設計刻意不直接 merge `origin/cascadedfilters` 的整包檔案,而是在 `main` 上重作功能,避免帶入大型 CSV、暫存檔、log、舊文件與 build noise。 ### Fixed-Point Time Simulation 時域分析同時計算兩條路徑: | Path | Description | | --- | --- | | Ideal Float | 使用 SciPy `signal.lfilter()` 作為理想浮點參考 | | Mimic Integer | 將輸入與係數量化為整數,模擬 MCU / DSP fixed-point 差分方程 | Q-format 參數: | Parameter | Meaning | | --- | --- | | `shift_in` | 輸入訊號量化位元數 | | `shift_out` | 輸出訊號量化位元數 | | `shift_b` | 前饋 b 係數量化位元數 | | `shift_a` | 回授 a 係數量化位元數 | | `use_round` | 右移時使用 Floor (SRA) 或 Round (+0.5) | 整數模擬採高精度狀態變數架構:前饋項保留在 `Q_in+b` 精度,回授項除以 `A0` 對齊,再於最終輸出依 `Q_out` 縮放。 ### Time-Domain LOD Zoom 時域圖表使用 Plotly zoom / pan 時,前端會把目前可視時間範圍換算成 sample index,透過 `/api/filter` 傳送 `start_idx` 與 `end_idx`。後端仍先用完整訊號計算 IIR 輸出,保留濾波器歷史狀態正確性,再只切出可視區間並 downsample 回傳,避免 zoom in 後只能看到粗略的全域降採樣資料。 時間軸單位會在首次完整載入時鎖定為 `s`、`ms` 或 `μs`,避免 zoom 後重新換單位造成 Plotly `uirevision` 座標錯位。前端也使用 `isRedrawingTimePlot` 避免 `Plotly.react()` 觸發 relayout callback 造成重繪迴圈。 ### CSV Workflow 限制與行為: | Item | Value | | --- | --- | | 檔案大小上限 | 300 MB | | 時域運算列數上限 | 1,200,000 rows | | 圖表點數上限 | 5000 points | | 格式要求 | 必須有標題列與至少一筆資料 | 若第一欄像 `time`、`t`、`sec`、`x`、`index`,前端會預設選第二欄作為訊號欄位。 首頁會自動嘗試載入 `.scratch/examples/preset_signals.csv` 作為 preset waveform;若檔案不存在,UI 仍可正常手動上傳 CSV。preset 路徑可用環境變數覆寫: ```bash DEA_PRESET_CSV_PATH=/path/to/preset_signals.csv ``` 範例 CSV 不再放在 Git 追蹤路徑。大型範例請放在本機 ignored 目錄: ```text .scratch/examples/ ``` 常用範例欄位格式: ```text time,value ``` 可從歷史 commit 匯出範例到本機 ignored 路徑使用: ```bash mkdir -p .scratch/examples git show 075f20d:chirp_signal.csv > .scratch/examples/chirp_signal.csv git show 0140b049:chirp_signal_small_s.csv > .scratch/examples/chirp_signal_small_s.csv git show 0140b049:step_signal.csv > .scratch/examples/step_signal.csv git show 8caa8918:static/preset_signals.csv > .scratch/examples/preset_signals.csv ``` `.scratch/` 已被 `.gitignore` 忽略,適合放大型 CSV、手動測試資料與暫存分析檔。 ### CSV Export `/api/filter/download` 預設保留 legacy 行為:輸出原始 CSV 欄位,並新增 `[欄位]_filtered_ideal` 與 `[欄位]_filtered_fixed`。 若前端傳送 `compact=true`,後端會輸出專供分析用的 4 欄 CSV: | Column | Meaning | | --- | --- | | `Time (s)` | 由 sample index 與前端傳入的 `fs` 計算 | | 原輸入欄位 | 選定訊號欄位原始值 | | `[欄位]_filtered_ideal` | Ideal Float 輸出 | | `[欄位]_filtered_fixed` | Mimic Integer / fixed-point 輸出 | ### MCU Web Serial Web Serial 是主要 MCU 寫入方式,伺服器不需要 serial 權限。 | Action | Description | | --- | --- | | 連線 | 點擊「選擇連接埠並連線」,在瀏覽器彈窗選擇 MCU | | 寫值 | 點擊「寫值到 MCU」 | | 限制 | Chrome / Edge,且必須透過 HTTPS 或 localhost | 寫入命令格式: ```text bodeplot=b0,b1,b2,a1,a2 ``` ## API | Method | Endpoint | Purpose | | --- | --- | --- | | `POST` | `/api/design` | 依濾波器參數產生 b/a 係數 | | `POST` | `/api/bode` | 計算單組 b/a 頻率響應 | | `POST` | `/api/bode/compare` | 比較單一濾波器 Ideal Float 與 Fixed-Point 頻率響應 | | `POST` | `/api/bode/compare_cascade` | 比較 cascade stages 的 Ideal Float 與 Fixed-Point 頻率響應 | | `GET` | `/api/csv/preset` | 讀取本機 ignored preset CSV metadata,供首頁自動載入 | | `POST` | `/api/csv/upload` | 上傳 CSV 並回傳 `file_id` | | `POST` | `/api/filter` | 使用 `file_id` 回傳單濾波器或 cascade 時域圖表資料;可傳 `start_idx/end_idx` 做 LOD zoom | | `POST` | `/api/filter/download` | 使用 `file_id` 匯出結果 CSV;可傳 `compact=true` 匯出 4 欄 CSV | | `GET` | `/api/mcu/ports` | 列出伺服器端 serial ports | | `POST` | `/api/mcu/write` | 後端模式寫入 MCU 命令 | ## Development ### Frontend ```bash npm install npm run test:frontend npm run build ``` 開發模式: ```bash npm run dev ``` CSV 與 API 流程保留 `dev-v2` 的兩段式設計:先 upload 取得 `file_id`,再用 `file_id` filter 或 download。`vite.config.js` 仍保留 `main` 的 HTTPS deployment 設定,不在 Vite 內設定 `/api` proxy。因此 `npm run dev` 只適合檢查前端畫面;CSV upload、filter API、MCU API 請固定使用: ```text https://192.168.2.58:8000/ui/ ``` ### Backend ```bash .venv/bin/pip install -r requirements.txt .venv/bin/uvicorn dea_api:app --host 0.0.0.0 --port 8000 --ssl-keyfile certs/key.pem --ssl-certfile certs/cert.pem ``` ### HTTPS Certificate ```bash bash certs/generate_cert.sh ``` 會在 `certs/` 產生: ```text cert.pem key.pem ``` ### systemd ```bash sudo cp dea.service /etc/systemd/system/ sudo systemctl daemon-reload sudo systemctl enable dea.service sudo systemctl start dea.service ``` 查看狀態: ```bash sudo systemctl status dea.service sudo journalctl -u dea.service -f ``` ### Backend Serial Fallback 若無法使用 Web Serial,可改用後端 serial API。環境變數: ```bash MCU_SERIAL_PORT=/dev/ttyUSB2 MCU_BAUD_RATE=115200 MCU_WRITE_TIMEOUT=1.0 ``` systemd 可在 `[Service]` 加入: ```ini Environment="MCU_SERIAL_PORT=/dev/ttyUSB2" Environment="MCU_BAUD_RATE=115200" Environment="MCU_WRITE_TIMEOUT=1.0" ``` ## Testing Python syntax check: ```bash .venv/bin/python -m py_compile dea_api.py tests/test_dea_api.py ``` Backend tests 使用 Python 內建 `unittest`: ```bash .venv/bin/python -m unittest discover -s tests ``` Frontend build check: ```bash npm run build ``` 建議完整驗證: ```bash .venv/bin/pip install -r requirements.txt .venv/bin/python -m unittest discover -s tests npm run build sudo systemctl restart dea.service ``` ## Security And Limits | Item | Value | | --- | --- | | Network access | 只允許 private / loopback IP | | CSV size | 300 MB | | Time-domain rows | 1,200,000 rows | | Plot points | 5000 points | | Coefficients | 最多 64 個 | HTTP responses 會加上 CSP、`Referrer-Policy`、`X-Content-Type-Options: nosniff`、`X-Frame-Options: DENY`。 ## Troubleshooting ### CSV 上傳顯示 `Not Found` 請確認目前網址是: ```text https://192.168.2.58:8000/ui/ ``` 如果從 `http://localhost:5173` 或其他 Vite dev server URL 開啟,`/api/csv/upload` 會送到前端 dev server,而不是 FastAPI,因此會回 `Not Found`。 ### 無法連線 MCU 1. 確認使用 `https://192.168.2.58:8000/ui/`。 2. 確認瀏覽器支援 Web Serial API。 3. 使用 Chrome 或 Edge。 4. 檢查 USB 線與 MCU 裝置狀態。 ### 頁面沒有更新 1. 執行 `npm run build`。 2. 重啟服務:`sudo systemctl restart dea.service`。 3. 瀏覽器按 `Ctrl+F5` 強制重新整理。 ### systemd 顯示 `status=203/EXEC` 通常是 `.venv` 或 `uvicorn` 路徑不正確。可重建 venv: ```bash rm -rf .venv python3 -m venv .venv .venv/bin/pip install -r requirements.txt sudo systemctl restart dea.service ``` ### 找不到 pytest 本專案不使用 pytest。請改用: ```bash .venv/bin/python -m unittest discover -s tests ```