# 无线电频谱与瀑布图前端渲染方案调研 _技术选型与性能验证方案 · 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_. [^2]: LightningChart. (2026). "LightningChart JS interactive examples and heatmap features." _LightningChart_. [^3]: Apache ECharts. (2026). "Canvas vs. SVG." _Apache ECharts Handbook_. [^4]: uPlot Contributors. (2025). "uPlot performance and streaming guidance." _GitHub_. [^5]: Chitnis, D. (2026). "webgl-plot: high-performance 2D plotting." _GitHub_. [^6]: Spectric Labs. (2025). "SigPlot: interactive plotting for SDR applications." _GitHub_. [^7]: audio-lab. (2022). "gl-spectrogram WebGL renderer." _GitHub_. [^8]: OpenWebRX Contributors. (2026). "OpenWebRX source and AGPL license." _GitHub_. --- _最后更新:2026-08-04_