Files
PPT/docs/spectrum-rendering-options.md

241 lines
12 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 无线电频谱与瀑布图前端渲染方案调研
_技术选型与性能验证方案 · Vue Web 前端 · 2026-08-04_
---
## 📋 摘要
本文面向全频或多频率窗口的实时监测页面,比较商业图表库、开源信号图表库和专用 WebGL2 渲染器。目标页面需要同时显示实时频谱曲线与瀑布图,支持阈值、框选、缩放和游标,并允许在一个页面中显示 1 个全频窗口或 16/32/64 个频率窗口。
调研结论是:允许商业授权时,SciChart.js 是功能完整度和开发效率较均衡的方案;必须采用宽松开源许可证时,没有现成组件能同时满足多窗口、高刷新率、瀑布图和完整交互。开源路线应采用 Vue 管理控制面、Web Worker 处理数据、单 WebGL2 上下文绘制全部窗口,并以 GPU 环形纹理保存瀑布历史。
当前性能原型选择开源专用方案,以验证浏览器真实容量上限,同时保留 SciChart.js 作为后续商业方案对照组。
## 🎯 需求与边界
### 核心需求
- 支持 1 个连续全频视图,也支持 4/8/16/32/64 个频率窗口
- 每个窗口同时显示当前频谱曲线与历史瀑布图
- 支持阈值线、超阈值着色、矩形框选、缩放和频率游标
- 支持可配置 FFT 点数、数据刷新率、瀑布深度和频率范围
- 高频数据不能进入 Vue 深层响应式系统
- 数据到达速度超过渲染速度时保持低延迟,不积压旧帧
### 非目标
- 不定义正式后端协议和鉴权方案
- 不处理设备控制、解调和信号识别算法
- 不追求在移动设备上同时展示 64 个完整窗口
- 不把百万 FFT 点逐点映射到不足数千像素的屏幕
> 📌 **关键定义:** “全频段”描述连续频率范围,不等于必须将全部原始 FFT 点直接提交给 GPU。缩放前必须根据视口进行保峰值的多级降采样。
## 🔬 调研方法
本次调研核对了 npm 最新版本元数据、项目许可证、公开 API、官方示例和实际开源实现。候选方案按五个维度评估:曲线、瀑布、多图资源模型、交互能力和授权风险。
```mermaid
flowchart LR
accTitle: 频谱渲染技术选型流程
accDescr: 从功能筛选、渲染模型检查、许可证审查到性能原型验证的完整选型流程
requirements[📋 明确负载模型] --> candidates[🔍 筛选候选库]
candidates --> rendering{⚡ GPU 增量渲染?}
rendering -->|是| licensing{🔐 许可可接受?}
rendering -->|否| baseline[📊 仅作功能基线]
licensing -->|是| benchmark[🧪 进入性能测试]
licensing -->|否| custom[🔧 专用 WebGL2]
custom --> benchmark
benchmark --> decision([✅ 根据实测定型])
classDef process fill:#dbeafe,stroke:#2563eb,stroke-width:2px,color:#1e3a5f
classDef decision fill:#fef9c3,stroke:#ca8a04,stroke-width:2px,color:#713f12
classDef success fill:#dcfce7,stroke:#16a34a,stroke-width:2px,color:#14532d
class requirements,candidates,baseline,custom,benchmark process
class rendering,licensing decision
class decision success
```
## 📊 方案对比
### 综合比较
| 方案 | 渲染内核 | 瀑布能力 | 多窗口模型 | 交互 | 许可证 | 结论 |
| --- | --- | --- | --- | --- | --- | --- |
| SciChart.js 5.2.62 | WebGL2 + WASM | Uniform Heatmap | 单 Surface + SubCharts | 完整 | 商业 | 商业首选 |
| LightningChart JS 9.0.0 | WebGL | Scrolling Heatmap | Dashboard | 完整 | 商业 | 性能强,许可复杂 |
| ECharts 6.1.0 | Canvas/SVG | Heatmap | 多 Grid/实例 | 完整 | Apache-2.0 | 低频功能基线 |
| Highcharts 13.0.0 | SVG + Boost | Heatmap | 多 Chart | 完整 | 商业 | 通用性强,针对性弱 |
| SigPlot 3.1.7 | Canvas2D | 流式 2D Layer | 多实例 | 较完整 | Apache-2.0 | SDR 开源基线 |
| uPlot 1.6.32 | Canvas2D | 无内置 | 多实例 | 插件 | MIT | 只适合曲线 |
| webgl-plot 1.1.2 | WebGL | 无内置 | 需自研 | 需自研 | MIT | 曲线底层组件 |
| gl-spectrogram 1.1.8 | WebGL | 内置 | 多实例 | 很弱 | MIT | 仅作代码参考 |
| OpenWebRX | Canvas2D | 内置 | 单接收视图 | SDR 交互 | AGPL-3.0 | 架构参考 |
| 专用 WebGL2 | WebGL2 | 环形纹理 | 单上下文分视口 | 按需实现 | 项目自有 | 开源性能首选 |
### 商业组件
SciChart.js 提供 WebGL2/WASM 渲染、Uniform Heatmap、Box/Line Annotation、RubberBand 选择及 SubCharts API。其公开类型说明明确建议:需要大量图表同时更新时,应使用单 Surface 配合 SubCharts,而不是创建大量独立 WebGL 上下文。[^1]
LightningChart JS 提供 `HeatmapScrollingGridSeries`、Spectrogram 示例、多通道实时曲线和 Dashboard。官方给出的性能目标非常激进,但许可包含开发与部署授权、联网校验和特定用途约束,采购前需要单独确认。[^2]
### 通用开源图表库
ECharts 的 `brush``markLine``dataZoom` 和 Heatmap 能快速完成交互原型,但它的主渲染器基于 Canvas/SVG 场景图。大量热力图单元和高频 `setOption` 更新会增加主线程和对象管理压力,因此不作为 32/64 窗口高刷新率主视图。[^3]
uPlot 是高效的 Canvas2D 时序曲线库,但项目文档明确建议在海量 60 FPS 流式信号场景使用 WebGL;同时没有内置瀑布图。[^4] webgl-plot 能以 WebGL 绘制实时波形,并支持 OffscreenCanvas,但缺少瀑布、坐标轴和标注系统。[^5]
### SDR 与瀑布开源实现
SigPlot 面向软件无线电,具有一维、二维流式 Layer 和 SDR 交互,采用 Apache-2.0 许可证。其二维实现主要更新 Canvas 图像缓冲,因此适合功能基线,不应在未经测试时假设能承载 64 个窗口。[^6]
gl-spectrogram 使用 WebGL 双纹理和 Framebuffer 实现瀑布,但每次 `push` 会将已有纹理整体平移到另一纹理。该方式适合单个音频频谱图,不适合大量窗口;更合理的做法是保持纹理不动,仅循环更新一行。[^7]
OpenWebRX 是成熟的 SDR 应用参考。其前端逐行创建 `ImageData`、执行 JavaScript 颜色映射,再调用 `putImageData` 写入多个 Canvas;许可证为 AGPL-3.0。它适合研究交互和协议,不适合作为宽松授权的高性能组件依赖。[^8]
## 💡 推荐架构
### 数据与渲染通路
```mermaid
flowchart LR
accTitle: 实时频谱前端数据通路
accDescr: 模拟器或 WebSocket 数据经过 Worker 解码和降采样后,以最新帧策略进入单 WebGL2 渲染器
source[🌐 模拟器或 WebSocket] --> worker[⚙️ Worker 解码与 LOD]
worker --> latest[(💾 最新帧缓冲)]
latest --> upload[⚡ 上传当前纹理行]
upload --> waterfall[📊 GPU 环形瀑布]
upload --> curve[📈 当前频谱曲线]
controls[🔧 Vue 控制与交互] --> renderer[⚙️ 单 WebGL2 Surface]
waterfall --> renderer
curve --> renderer
renderer --> display([✅ 多频率窗口])
classDef process fill:#dbeafe,stroke:#2563eb,stroke-width:2px,color:#1e3a5f
classDef data fill:#ede9fe,stroke:#7c3aed,stroke-width:2px,color:#3b0764
classDef success fill:#dcfce7,stroke:#16a34a,stroke-width:2px,color:#14532d
class source,worker,upload,waterfall,curve,controls,renderer process
class latest data
class display success
```
### GPU 环形纹理
瀑布纹理使用 `R8``R16F` 保存归一化强度。每个 FFT 帧只执行一行 `texSubImage2D`,写入位置按以下方式循环:
```text
writeRow = (writeRow + 1) % historyRows
sampleRow = (writeRow - screenRow + historyRows) % historyRows
```
Fragment Shader 通过 `sampleRow` 改变显示顺序,不移动历史纹理。颜色使用一维 LUT 纹理完成映射。当前曲线可以从最新一行采样,避免复制第二份强度数据。
多窗口共享一个 WebGL2 Context,通过 `viewport``scissor` 划分绘制区域。这样不会触发浏览器 WebGL Context 数量限制,也降低重复纹理、着色器和状态切换成本。
### Vue 集成原则
- Vue 只保存配置、布局、阈值、选区和统计值
- FFT 数组保持为非响应式 TypedArray
- Web Worker 使用可转移 ArrayBuffer 池,避免持续分配
- 主线程只保留最新帧,旧帧直接回收
- `requestAnimationFrame` 决定绘制节奏,数据接收不直接触发 Vue Render
- 阈值和框选使用轻量覆盖层,指针移动时才更新
## ⚡ 容量模型
### 数据带宽
使用 16 位强度值时:
```text
带宽 = 窗口数 × 每窗口 FFT 点数 × 2 字节 × 更新频率
```
| 场景 | 计算规模 | 约合带宽 |
| --- | ---: | ---: |
| 32 个窗口 | 32 × 2048 × 30 Hz | 3.75 MiB/s |
| 64 个窗口 | 64 × 4096 × 20 Hz | 10 MiB/s |
| 单个全频 | 1,048,576 × 20 Hz | 40 MiB/s |
全频缩放前的有效水平分辨率通常只有 1,000 至 4,000 像素。曲线应按像素保留 `min/max` 包络,瀑布按像素保留 `max` 或可配置聚合;否则既浪费带宽,也可能因平均降采样丢失窄带峰值。
### GPU 内存
采用单字节强度纹理时:
```text
瀑布内存 = 窗口数 × FFT 点数 × 历史行数
```
| 场景 | 纹理规模 | 强度纹理内存 |
| --- | ---: | ---: |
| 32 × 2048 × 256 | 16,777,216 texels | 16 MiB |
| 64 × 4096 × 256 | 67,108,864 texels | 64 MiB |
| 1M × 512 | 536,870,912 texels | 512 MiB |
最后一种布局不合理,必须在进入瀑布纹理前完成 LOD。
## 🧪 性能验证计划
### 测试矩阵
| 维度 | 测试值 |
| --- | --- |
| 窗口数 | 1、8、16、32、64 |
| FFT 点数 | 512、1024、2048、4096、8192 |
| 数据频率 | 10、20、30、60 Hz |
| 瀑布深度 | 64、128、256、512 行 |
| 页面布局 | 全频单图、多列窗口 |
| 交互 | 阈值拖动、框选、缩放、游标 |
### 验收指标
| 指标 | 目标 |
| --- | ---: |
| P95 帧耗时 | 小于 16.7 ms |
| 交互反馈延迟 | 小于 50 ms |
| 数据到显示延迟 | 小于 100 ms |
| 主线程长任务 | 10 分钟内无持续出现 |
| 内存 | 30 分钟运行无持续增长 |
| 数据积压 | 不积压,允许合并中间帧 |
性能页面必须显示实际 FPS、数据接收率、上传耗时、绘制耗时、丢弃帧数、估算吞吐和纹理内存。测试结果只能代表指定浏览器、GPU、分辨率和设备像素比,不能直接外推到全部终端。
## ✅ 选型结论
当前采用以下决策:
1. 使用专用 WebGL2 GPU 环形纹理实现开源性能基线
2. 使用 Vue 构建控制面,不让 Vue 管理实时 FFT 数组
3. 使用 Worker 模拟后端并生成二进制 TypedArray 数据
4. 使用单 WebGL2 Context 绘制全部频率窗口
5. 将 SigPlot 保留为开源功能对照,不作为最终内核
6. 将 SciChart.js 保留为商业对照,待开源基线结果出来后再决定是否评估试用版
选择专用实现并非预先认定其一定优于商业库,而是为了先获得不受许可约束的真实容量数据,并明确浏览器、GPU、数据带宽和布局的性能边界。
## 🔗 参考资料
[^1]: SciChart. (2026). "SciChart.js npm package and SubCharts APIs." _npm / SciChart Documentation_. <https://www.npmjs.com/package/scichart>
[^2]: LightningChart. (2026). "LightningChart JS interactive examples and heatmap features." _LightningChart_. <https://lightningchart.com/lightningchart-js-interactive-examples/>
[^3]: Apache ECharts. (2026). "Canvas vs. SVG." _Apache ECharts Handbook_. <https://echarts.apache.org/handbook/en/best-practices/canvas-vs-svg/>
[^4]: uPlot Contributors. (2025). "uPlot performance and streaming guidance." _GitHub_. <https://github.com/leeoniya/uPlot>
[^5]: Chitnis, D. (2026). "webgl-plot: high-performance 2D plotting." _GitHub_. <https://github.com/danchitnis/webgl-plot>
[^6]: Spectric Labs. (2025). "SigPlot: interactive plotting for SDR applications." _GitHub_. <https://github.com/spectriclabs/sigplot>
[^7]: audio-lab. (2022). "gl-spectrogram WebGL renderer." _GitHub_. <https://github.com/audio-lab/gl-spectrogram>
[^8]: OpenWebRX Contributors. (2026). "OpenWebRX source and AGPL license." _GitHub_. <https://github.com/jketterl/openwebrx>
---
_最后更新:2026-08-04_