423 lines
13 KiB
Markdown
423 lines
13 KiB
Markdown
# 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
|
||
```
|