Files
tcad-bodeplot/README.md
T

423 lines
13 KiB
Markdown
Raw Normal View History

# Difference Equation Analyzer
Difference Equation Analyzer 是用來設計、檢視與驗證離散時間濾波器的工具。它同時支援浮點係數、fixed-point 係數、時域 CSV 驗證,以及透過 Web Serial 將係數寫入 MCU。
Backend 使用 FastAPI、NumPy、SciPy 與 PandasFrontend 使用 Vue 3、Vite、Tailwind CSS 與 Plotly。
## Features
- 設計 Lowpass、Highpass、Bandpass、Notch、1P1Z、2P1Z、2P2Z、PID、SOGI 濾波器。
- 比較 Ideal Float 與 Fixed-Point 的 Bode 頻率響應。
2026-05-27 08:38:40 +08:00
- 支援多個 cascade stages 的頻率響應與時域模擬,並可個別啟用或 bypass。
- 手動微調 b/a 係數、a1/a2、δ/r、Q-format 位元數與 fixed-point 整數係數。
- 上傳 CSV 時域資料,繪製輸入、Ideal Float 輸出與 Mimic Integer 輸出。
2026-05-27 08:38:40 +08:00
- 匯出包含理想浮點與定點整數模擬輸出的 CSV;cascade workflow 可匯出乾淨的 4 欄 CSV。
- 使用瀏覽器 Web Serial API 將 fixed-point 係數寫入 MCU。
2026-05-27 08:38:40 +08:00
- 時域圖表支援 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 設定
2026-05-27 08:38:40 +08:00
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 存取限制與安全標頭 |
2026-05-27 08:38:40 +08:00
| `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 + 滑鼠滾輪微調;觸控裝置可按住數值面板上下拖曳。
2026-05-27 08:38:40 +08:00
### 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` 縮放。
2026-05-27 08:38:40 +08:00
### 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`,前端會預設選第二欄作為訊號欄位。
2026-05-27 08:38:40 +08:00
首頁會自動嘗試載入 `.scratch/examples/preset_signals.csv` 作為 preset waveform;若檔案不存在,UI 仍可正常手動上傳 CSV。preset 路徑可用環境變數覆寫:
```bash
DEA_PRESET_CSV_PATH=/path/to/preset_signals.csv
```
範例 CSV 不再放在 Git 追蹤路徑。大型範例請放在本機 ignored 目錄:
```text
2026-05-27 08:38:40 +08:00
.scratch/examples/
```
2026-05-27 08:38:40 +08:00
常用範例欄位格式:
```text
time,value
```
2026-05-27 08:38:40 +08:00
可從歷史 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 頻率響應 |
2026-05-27 08:38:40 +08:00
| `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` |
2026-05-27 08:38:40 +08:00
| `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
2026-05-27 08:38:40 +08:00
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
```