# 2026 NASA 3! CSIE Wi-Fi
## Meetings
:::spoiler 3/17
#### 已完成
- 找出系館所有 AP
- 討論要選那些點測試訊號
- 決定記錄 BSSID、RSSI、Channel、Rx/Tx Ra
#### TODO
- 測 AP 數據
:::
:::spoiler 3/20
#### 已完成
- 測 B1 的 AP
- 測 1F 的 AP
#### TODO
- 補測 1F 某些教室的 AP
:::
:::spoiler 4/3
#### 已完成
- 跟無線組組長開會
#### TODO
- 期中前
- 做一個 UI 把之前測的數據放上去
- 期中後
- 把這個地圖改成動態
- 目標:讓使用者可以向 NASA 團隊回報壞掉的 AP、訊號不佳的情況
:::
:::spoiler 4/5
#### 已完成
- 完成網站的基本架構(使用 HTML)
#### TODO
- 導入 leaflet
:::
:::spoiler 4/7
#### 已完成
- 導入 leaflet
- 加入互動式功能
#### TODO
- 改成 React
:::
:::spoiler 4/10
#### 已完成
- 把網站前端改成 Vite + React
#### TODO
- 把網站搬到 VM 上
- 準備期中 demo
:::
:::spoiler 4/19
#### 已完成
- 搬到 VM 上
- 做 demo 簡報
- 討論期中後的目標
- 錄 demo 影片
#### TODO
- 完成 1F 的頁面
:::
:::spoiler 4/21
#### 已完成
- 1F 頁面
- 把 B1 的點補齊
- 寫了更新網頁的 shell script
- 補完之前的會議紀錄
#### TODO
- 和無線組組長討論期中目標
- 看看能不能要到 SNMP 的唯讀權限
:::
:::spoiler 4/28
#### 已完成
- 開會
- 問SNMP權限
- 要不要LDAP? 可能要
- 速度指標可以考慮用使用者測試到的速度/到計中的速度
- 架構
#### TODO
- 寫後端🥲
:::
:::spoiler 5/5
#### 已完成
- 把單點的資訊改成熱力圖
- 新增二至五樓的頁面
- 新增測速功能
#### TODO
- 寫API文件🥲
- 導入SNMP數據🥲
:::
:::spoiler 5/26
#### 已完成
- 進入 SNMP agent
#### TODO
- 期末 demo
- ~~登入系統~~
- database 的 HA(備份、安全性)
- SNMP 資料(做了測速系統但明顯無效)
- webhook 回報
- 改成 UI/UX 組的地圖
- 製作AP點狀圖
:::
## Workflow
- Open an issue
- Lables: `frontend` for frontend related issues, `backend` for backend related issues, `bug` for fix, `enhancement` for feature, etc.
- Work on a branches other than `main` branch
- Commit with propoer commit messages
- Open a pull request
- Merge into `main` if no diffs
- Else, solve conficts
- `git pull origin main`
:::warning
:warning: 記得要寫 Document
:::
## Job Distribution
### Layout
```wifi_backend/
├── manage.py
├── requirements.txt
├── .env
│
├── config/ # Django project 總設定
│ ├── __init__.py
│ ├── settings.py # 需在此設定 Celery 與 JWT 參數
│ ├── urls.py
│ ├── asgi.py
│ ├── wsgi.py
│ └── celery.py # Celery 排程器的起始設定檔
│
├── auth_api/ #處理 LDAP 登入與 JWT 核發
│ ├── __init__.py
│ ├── services.py
│ ├── views.py # 提供 /api/login 端點
│ └── urls.py
│
├── devices/ # AP 設備靜態資料 (SQLite)
│ ├── models.py
│ ├── serializers.py
│ ├── views.py
│ ├── urls.py
│ └── admin.py
│
├── measurements/ # 測量結果與報修紀錄 (SQLite / InfluxDB)
│ ├── models.py
│ ├── serializers.py
│ ├── views.py
│ ├── urls.py
│ └── admin.py
│
├── iperf_api/ # iperf 測速 API (主動測量)
│ ├── __init__.py
│ ├── services.py
│ ├── serializers.py
│ ├── views.py
│ └── urls.py
│
├── snmp_api/ # SNMP 讀取 API (背景輪詢)
│ ├── __init__.py
│ ├── services.py
│ ├── tasks.py # 定義每分鐘定時執行的 Celery Task
│ ├── views.py # 提供給前端畫歷史折線圖的 API
│ └── urls.py
│
└── heatmap/ # 給前端 Leaflet 用的熱力圖 JSON
├── __init__.py
├── views.py
└── urls.py
```
## Data
::: spoiler B1
### AP正下方
#### csie-5G
|測點ID|測量時間|BSSID |Channel |RSSI(dBm) |Rx rate |Tx rate|Rx rate (PHY)|Tx rate (PHY)|
| - | - | -| -|-|-|-|-|-|
|B02|0320.16:18|F8‑E7‑1E‑66‑45‑3C|36|-37|220.7|188.4|400|400|
|B00|0320.16:33|30‑87‑D9‑71‑6B‑2C|44|-36|168.2|159.9|360|400|
|B15|0320.16:42|30‑87‑D9‑71‑96‑4C|36|-39|194.4|194.4|360|400|
|B04|0320.16:48|34‑8F‑27‑5E‑7A‑8C|116|-35|241.1|212.5|400|400|
|B09|0320.17:00|30‑87‑D9‑71‑96‑EC|100|-57|150.3|176.4|270|324|
|B05|0320.17:20|30‑87‑D9‑71‑7F‑CC|108|-36|218.2|275.9|360|400|
#### csie
|測點ID|測量時間|BSSID |Channel |RSSI(dBm) |Rx rate |Tx rate|Rx rate (PHY)|Tx rate (PHY)|
| - | - | -| -|-|-|-|-|-|
|B02|0320.16:18|F8‑E7‑1E‑26‑45‑39|6|-37|49.2|38.2|144.4|144.4|
|B00|0320.16:37|30‑87‑D9‑31‑6B‑29|1|-59|9.4|11.6|130|52|
|B15|0320.16.41|30‑87‑D9‑31‑96‑49|1|-52|13.9|60|144|104|
|B04|0320.16:46|34‑8F‑27‑1E‑7A‑89|6|-34|47.5|27.0|144|144|
|B09|0320.16:57|30‑87‑D9‑31‑96‑E9|6|-34|41.7|24.3|144|144|
|B05|0320.17:17|30‑87‑D9‑31‑7F‑C9|1|-35|19.4|4.2|65|144|
### 兩個AP中間
#### csie-5G
|測點ID|測量時間|連到的AP|RSSI(dBm) |Rx rate |Tx rate|Rx rate (PHY)|Tx rate (PHY)|
| - | - | -| -|-|-|-|-|
|M1|0320.17:25|B02|-59|114.0|108.7|270|240|
|M2|0320.16:54|B15|-49|201.3|201.7|360|400|
|M3|0320.17:05|B15|-44|178.2|194.9|360|360|
|M4|0320.17:23|B15|-62|97.3|125.9|216|216|
#### csie
|測點ID|測量時間|連到的AP|RSSI(dBm) |Rx rate |Tx rate|Rx rate (PHY)|Tx rate (PHY)|
| - | - | -| -|-|-|-|-|
|M1|0320.17:27|B02|-51|31.1|44.9|130|115|
|M2|0320.16:50|B04|-40|14.1|18.4|130|117|
|M3|0320.17:02|B04|-64|4.5|13.6|104|117|
|M4|0320.17:22|B09|-49|36.2|16.2|144|130|
### 遠點
#### csie-5G
|測點ID|測量時間|連到的AP|RSSI(dBm) |Rx rate |Tx rate|Rx rate (PHY)|Tx rate (PHY)|
| - | - | -| -|-|-|-|-|
|R1|0320.17:28|B00|-57|133.9|115.5|324|300|
|R2|0320.17:09|B15|-48|192.6|216.1|360|400|
|R3|0320.17:11|B15|-56|166.8|170.4|270|243|
#### csie
|測點ID|測量時間|連到的AP|RSSI(dBm) |Rx rate |Tx rate|Rx rate (PHY)|Tx rate (PHY)|
| - | - | -| -|-|-|-|-|
|R1|0320.17:29|B00|-63|6.6|5.5|104|117|
|R2|0320.17:08|B09|-53|22.3|17.3|144|104|
|R3|0320.17:10|B09|-52|37.2|17.4|117|130|
:::
:::spoiler 1F

### AP正下方
#### csie-5G
|測點ID|測量時間|BSSID |Channel |RSSI(dBm) |Rx rate |Tx rate|Rx rate (PHY)|Tx rate (PHY)|
| - | - | -| -|-|-|-|-|-|
|R102|0324.9:46|30‑87‑D9‑71‑79‑EC|108|-30|256.4|231.5|400|400|
|R103(人多)|0324.13:15|30‑87‑D9‑71‑98‑CC|100|-40|172.2|159|400|400|
|R106(有問題)|0324.9:55|30‑87‑D9‑71‑99‑4C|60|-62|141|157.7|270|270|
|R110|0324.10:02|30‑87‑D9‑71‑52‑4C|132|-35|273.4|235.5|400|400|
|R101|0324.10:18|30‑87‑D9‑71‑55‑4C|44|-34|250|241.3|400|400|
|R104(人多)|0324.13:21|34‑8F‑27‑5A‑E4‑CC|116|-33|162.4|92.7|360|400|
|R105|0324.10:19|30‑87‑D9‑31‑6B‑AC|36|-33|244.7|264.9|360|400|
|R107|0324.10:28|30‑87‑D9‑71‑59‑8C|44|-40|253.4|236.3|400|400|
|R111|0324.10:31|30‑87‑D9‑71‑83‑0C|132|-38|227.1|239.1|360|360|
#### csie
|測點ID|測量時間|BSSID |Channel |RSSI(dBm) |Rx rate |Tx rate|Rx rate (PHY)|Tx rate (PHY)|
| - | - | -| -|-|-|-|-|-|
|R102|0324.9:44|30‑87‑D9‑31‑79‑E9|1|-41|40.1|31.1|130|130|
|R104|0324.13:21|34‑8F‑27‑1A‑E4‑C9|11|-27|24.4|56.4|144|144|
|R106(有問題)|0324.9:50|30‑87‑D9‑31‑52‑49|6|-60|25.7|27.2|144|78|
|R110|0324.10:01|30‑87‑D9‑31‑52‑49|6|-25|68.4|35.4|130|144|
|R101|0324.10:09|30‑87‑D9‑31‑55‑49|6|-31|43.4|41.7|144|130|
|R105|0324.10:19|30‑87‑D9‑31‑6B‑A9|6|-29|55.5|31.8|144|144|
|R107|0324.10:27|30‑87‑D9‑31‑59‑89|6|-40|49.7|27.3|130|144|
|R111|0324.10:30|30‑87‑D9‑31‑83‑09|11|-31|37.0|54.3|144|144|
### 兩個AP中間
#### csie-5G
|測點ID|測量時間|連到的AP|RSSI(dBm) |Rx rate |Tx rate|Rx rate (PHY)|Tx rate (PHY)|
| - | - | -| -|-|-|-|-|
|M5|0324.10:56|R107|-54|212.0|231.3|324|360|
|M6|0324.10:52|R107|-55|203.8|232.5|270|360|
#### csie
|測點ID|測量時間|連到的AP|RSSI(dBm) |Rx rate |Tx rate|Rx rate (PHY)|Tx rate (PHY)|
| - | - | -| -|-|-|-|-|
|M5|0324.10:52|R107|-49|42.3|28.5|130|144|
|M6|0324.10:52|R111|-49|45.5|22.2|130|130|
:::
### 測量時的觀察
- 從舊館走到新管會先連到N3,N3跟N4需移動較遠的距離才會做切換
- 在N1下方可能會連到牆內的N1,導致訊號不佳
- N2,N3,N6不易切換
- 在M3,csie_5g連到B04很怪
- 在B05正下方會連到B09(B05_AP會閃黃燈)(csie的部分)
- R106信號特別弱,很常連上後又斷掉(AP亮黃燈)
- 手機不一定可以連到
- R104在紀錄時人有點多
## API文件
:::spoiler heatmap
### POST /api/heatmap/measurements/create/
(新增一筆wifi測量資料)
#### request body
```json
{
"floor": "basement",
"point_id": "b00",
"x": 65.2,
"y": 51.3,
"note": "新館空曠區域",
"ssid": "csie-5G",
"bssid": "xx:xx:xx:xx:xx:xx",
"channel": 36,
"rssi": -36,
"rx_rate": 866.7,
"tx_rate": 780.0
}
```
#### success response
```json
{
"message": "created",
"data": {
"id": 1,
"floor": "basement",
"point_id": "b00",
"x": 65.2,
"y": 51.3,
"note": "新館空曠區域",
"ssid": "csie-5G",
"bssid": "xx:xx:xx:xx:xx:xx",
"channel": 36,
"rssi": -36,
"rx_rate": 866.7,
"tx_rate": 780.0,
"measured_at": "2026-05-11T10:30:00+08:00"
}
}
```
#### error response
```json
{
"message": "invalid data",
"errors": {
"rssi": \["This field is required."]
}
}
```
### GET /api/heatmap/measurements/
(取得wifi資料列表)
|參數|說明|範例
|--|--|--|
|floor|篩選樓層|basement,floor1
|ssid|篩選wifi名稱|csie,csie-5G|
|bssid|篩選AP MAC位址|aa\:bb\:cc\:dd\:ee:ff|
|limit|限制回傳筆數,預設500,最大2000|100|
#### example request
GET /api/heatmap/measurements/?floor=basement&ssid=csie-5G&limit=100
### GET /api/heatmap/
(取得給前端heatmap的資料)
|參數|說明|範例|
|--|--|--|
|floor|篩選樓層|basement,floor1|
|ssid|篩選wifi名稱|csie,csie-5G|
|bssid|篩選AP MAC位址|aa\:bb\:cc\:dd\:ee:ff|
|min_rssi|篩選RSSI下限,保留大於等於此值的資料|-70|
|max_rssi|篩選RSSI上限,保留小於等於此值的資料|-35|
|limit|限制回傳筆數,預設 5000,最大 10000|1000|
#### example request
GET /api/heatmap/?floor=basement&ssid=csie-5G&min_rssi=-70&limit=1000
#### response
```json
{
"count": 1,
"metric": "rssi",
"coordinate": "xy-percent",
"floor": "basement",
"data": [
{
"id": 1,
"floor": "basement",
"x": 65.2,
"y": 51.3,
"value": 1.0,
"rssi": -36,
"point_id": "b00",
"note": "新館空曠區域",
"ssid": "csie-5G",
"bssid": "xx:xx:xx:xx:xx:xx",
"channel": 36,
"rx_rate": 866.7,
"tx_rate": 780.0,
"measured_at": "2026-05-11T10:30:00+08:00"
}
]
}
```
### Heatmap Value
`value` 是由 `rssi` 轉換而來,數值越大代表訊號越強。
例如:
- RSSI = -36,value = 0.85
- RSSI = -60,value = 0.45
- RSSI = -90,value = 0.03
:::
:::spoiler client
### POST api/iperf/run
(執行 iperf3 網路測速)
#### request body
| 參數 | 說明 | 範例 |
| -------- | -------- | -------- |
| target_ip | 目標設備的 IP 位址(須為合法的 IPv4/IPv6,且目標設備需運行 iperf3 -s) | "192.168.1.100" |
| duration | 測量時間(秒),預設值為 5,限制範圍為 1 到 30 | 10
```json
{
"target_ip": "192.168.1.100",
"duration": 10
}
```
#### success response (HTTP 200 OK)
```josn
{
"success": true,
"target_ip": "192.168.1.100",
"download_mbps": 850.55
}
```
#### error response - Validation Error (HTTP 400 Bad Request)
當輸入不合法的 IP 格式或超出範圍的測量時間時回傳:
```josn
{
"target_ip": [
"Enter a valid IPv4 or IPv6 address."
]
}
```
#### error response - Service Unavailable (HTTP 503 Service Unavailable)
當 iperf3 執行失敗(例如目標機器沒開 iperf3 -s)或連線逾時回傳:
```json
{
"success": false,
"error": "連線失敗。請確認目標 IP 正確,且已執行 'iperf3 -s'。"
}
```
> `error` 欄位的值也可能是 "測速連線逾時,目標機器未回應。" 或 "系統發生未預期錯誤。"
### GET /api/iperf/download/
(下載 100MB 的隨機檔案,用於 HTTP 下載測速)
#### example request
```GET /api/iperf/download/```
#### response (HTTP 200 OK)
- Content-Type: `application/octet-stream`
- Response Body: 100MB 的隨機二進位資料 (Dummy data)
- Headers: 包含強制禁止快取的標頭,確保每次測速都是實際走網路傳輸:
- `Cache-Control`: `no-cache`, `no-store`, `must-revalidate`
- `Pragma`: `no-cache`
- `Expires`: `0`
:::
## REST API互動基本流程
proxmox上的vm -(HTTP request)-> Ruckus vSZ Controller REST API -(JSON response)-> ap解析資料