FEATURED · 精选文章

Serial Studio 频谱频率标记(FFT Frequency Markers):基于 spec 0019 的谱线标注、报警频带与监视体系实战指南

发布时间 / 2026/9/17 2:17:43
来源 / 创域科博编辑部
栏目 / 资讯中心
Serial Studio 频谱频率标记(FFT Frequency Markers):基于 spec 0019 的谱线标注、报警频带与监视体系实战指南 Serial Studio 频谱频率标记FFT Frequency Markers基于 spec 0019 的谱线标注、报警频带与监视体系实战指南【免费下载链接】Serial-StudioOpen-source telemetry dashboard. Supports UART, BLE, MQTT, Modbus, CAN Bus and more.项目地址: https://gitcode.com/GitHub_Trending/se/Serial-Studio本指南围绕 Serial Studio 的doc/claude/specs/0019-fft-freq-markers规范spec / plan / tasks 三阶段文档展开如何在 FFT 频谱与 Waterfall 频谱图上为振动、声学等工程场景添加带名称、颜色和 dB 阈值的频率标记与报警频带并通过项目编辑器与远程 API 持久化管理。读完本文你将掌握标记的数据模型与序列化规则、编辑器对话框的完整操作、FFT/Waterfall 双视图的渲染与实时监视原理以及 API 读写与校验测试的实战用法。1. 背景为什么频谱需要一个看名单Spectral Watchlist振动与声学工程关心的是已知频率齿轮啮合线如 800 Hz、1 倍转频1x-RPM的不平衡线、轴承缺陷频带、需要避开的共振点。在引入本特性之前Serial Studio 的 FFT 控件只绘制整条频谱曲线把每一个赫兹等同对待——用户需要靠肉眼去定位800 附近的那个鼓包记住它代表什么再凭经验判断其电平是否合格。这带来三个痛点无法给有工程意义的频率打上名称标签无法一眼看出被监视的谱线是否越过了告警或报警电平项目文件中不保存任何此类领域知识换台机器、换个项目就要重来。有趣的是数据集在幅度域早已解决了等价问题报警频带alarm bands带严重级别、颜色和标签的彩色 min/max 区域可以在 Project Editor 的专用对话框中编辑、持久化到项目文件并驱动 Bar/Gauge/Meter/LED 等控件。spec 0019 的目标正是让频率域获得同等对待——参照 DAW 均衡器和频谱分析仪的交互范式带柔和渐变的半透明彩色区域、带标签的频率线再加上超出阈值即点亮的逐标记电平阈值。该规范由 4 个阶段文档构成doc/claude/specs/0019-fft-freq-markers/spec.mdWHAT/WHY、plan.mdHOW、tasks.md有序清单以及收尾的架构笔记doc/claude/architecture/dashboard.md。全部 9 个任务T1–T9已在 2026-07-17 完成实现并标记为 done。2. 数据模型与持久化FrequencyMarker结构与 JSON 序列化T12.1 结构定义标记挂在每个开启 FFT 的数据集上。核心结构定义在 core/Core/DataModel/Frame.h/** * brief Labeled frequency marker (point or band) attached to an FFT-enabled Dataset. */ struct alignas(8) FrequencyMarker { double frequency 0; /// Marker frequency in Hz (band start when endFrequency is set) double endFrequency 0; /// Band end in Hz; frequency means a point marker double warningDb std::numeric_limitsdouble::quiet_NaN(); /// Warning level (NaN unset) double alarmDb std::numeric_limitsdouble::quiet_NaN(); /// Alarm level (NaN unset) QString color; /// Optional hex override; empty - automatic (theme palette) QString label; /// Optional human label shown on the marker chip }; static_assert(sizeof(FrequencyMarker) % alignof(FrequencyMarker) 0, Unaligned FrequencyMarker struct);要点点标记 vs 频带endFrequency frequency或未设置即点标记否则为频带阈值为 NaN 语义warningDb/alarmDb默认是quiet_NaN()表示未设置。NaN 永远不会被序列化也不会参与比较对齐保证alignas(8)static_assert保证结构与数据集的 8 字节对齐约定一致与旁边的AlarmBand结构Frame.h保持同样的纪律。数据集侧在 Frame.h 新增成员std::vectorFrequencyMarker fftMarkers;空 无标记紧邻已有的std::vectorAlarmBand alarmBands;。注意数据集本身还携带 FFT 相关配置fftSamples默认 256、fftSamplingRate默认 100、fftBallisticsRelease默认 300 ms 显示衰减、fftWindow默认 5 Blackman-Harris、fftLogX对数频率轴开关等这些决定了标记的监视范围与显示方式。2.2 JSON 键Keys新增键定义在 core/Core/DataModel/FrameKeys.hinline constexpr KeyView FFTMarkers(fftMarkers); inline constexpr KeyView EndFrequency(endFreq); inline constexpr KeyView WarningDb(warningDb); inline constexpr KeyView AlarmDb(alarmDb);其中Frequency、Label、Color复用既有键。一个典型标记的 JSON 形态来自 plan.md{freq: 800, endFreq: 850, label: Gear mesh, color: #ff5722, warningDb: -40, alarmDb: -25}除freq外所有字段都可选可选字段在写出时被省略NaN 阈值、空字符串、endFreq freq永不序列化。2.3 序列化规则写路径serialize(const FrequencyMarker)是内联实现见 core/Core/DataModel/Frame.h[[nodiscard]] inline QJsonObject serialize(const FrequencyMarker m) { QJsonObject obj; obj.insert(Keys::Frequency, m.frequency); if (m.endFrequency m.frequency) obj.insert(Keys::EndFrequency, m.endFrequency); if (!std::isnan(m.warningDb)) obj.insert(Keys::WarningDb, m.warningDb); if (!std::isnan(m.alarmDb)) obj.insert(Keys::AlarmDb, m.alarmDb); if (!m.color.isEmpty()) obj.insert(Keys::Color, m.color); if (!m.label.isEmpty()) obj.insert(Keys::Label, m.label); return obj; }关键的加法式模式纪律additive-schema discipline沿袭 spec 0014/0017fftMarkers数组仅在非空时写入数据集 JSON——未加标记的旧项目保存后与旧版本逐字节一致T8 的 pytest 专门验证无标记项目不含fftMarkers键可选子键endFreq、warningDb、alarmDb、color、label一律省略而非写null或空串无需 schema 版本升级旧构建读到未知键会直接忽略老客户端不受影响。2.4 反序列化与校验读路径DataModel::read(FrequencyMarker, const QJsonObject)实现在 core/Core/DataModel/Frame.cpp校验规则非常明确bool DataModel::read(FrequencyMarker m, const QJsonObject obj) { constexpr double nan std::numeric_limitsdouble::quiet_NaN(); constexpr double max_freqHz 2147483648.0; if (obj.isEmpty()) return false; m.frequency SerialStudio::toDouble(ss_jsr(obj, Keys::Frequency, 0)); m.endFrequency SerialStudio::toDouble(ss_jsr(obj, Keys::EndFrequency, 0)); m.warningDb obj.contains(Keys::WarningDb) ? SerialStudio::toDouble(obj.value(Keys::WarningDb)) : nan; m.alarmDb obj.contains(Keys::AlarmDb) ? SerialStudio::toDouble(obj.value(Keys::AlarmDb)) : nan; m.color ss_jsr(obj, Keys::Color, ).toString().simplified(); m.label ss_jsr(obj, Keys::Label, ).toString().simplified(); if (!std::isfinite(m.frequency) || m.frequency 0.0 || m.frequency max_freqHz) return false; if (!std::isfinite(m.endFrequency) || m.endFrequency m.frequency) m.endFrequency 0.0; else m.endFrequency qMin(m.endFrequency, max_freqHz); if (std::isfinite(m.warningDb) std::isfinite(m.alarmDb) m.warningDb m.alarmDb) std::swap(m.warningDb, m.alarmDb); if (!PropertyHooks::isValidColor(m.color)) m.color.clear(); return true; }四层校验逻辑校验项规则违规处理频率合法性有限数、 0、且 ≤max_freqHzint 采样率下最大可能 Nyquist返回 false条目被丢弃反向频带endFrequency frequency降级为点标记endFreq 清零阈值倒置warningDb alarmDb交换两者normalize 而非丢弃颜色合法性PropertyHooks::isValidColor非法颜色清空回退主题自动配色批量入口readDatasetFrequencyMarkersFrame.cpp清空后逐条解析无效条目静默丢弃行为与AlarmBand契约一致在read(Dataset)中于readDatasetAlarmBands之后调用。3. Project EditorFrequencyMarkersEditor 对话框与提交管线T2、T63.1 启动信号与数据装配编辑器侧的 C 装配实现在 core/Ui/ProjectEditor/EditorForms.cpp。点击 DatasetView 的 Freq. Markers 功能区按钮后void EditorForms::openFrequencyMarkersEditorForSelection() { const double nyquist qMax(1, m_editor.m_selectedDataset.fftSamplingRate) * 0.5; QVariantList markers; markers.reserve(static_castint(m_editor.m_selectedDataset.fftMarkers.size())); for (const auto m : m_editor.m_selectedDataset.fftMarkers) { QVariantMap entry; entry.insert(QStringLiteral(freq), m.frequency); entry.insert(QStringLiteral(endFreq), m.endFrequency); entry.insert(QStringLiteral(label), m.label); entry.insert(QStringLiteral(color), m.color); entry.insert(QStringLiteral(warningDb), std::isfinite(m.warningDb) ? QVariant(m.warningDb) : QVariant()); entry.insert(QStringLiteral(alarmDb), std::isfinite(m.alarmDb) ? QVariant(m.alarmDb) : QVariant()); markers.append(entry); } Q_EMIT m_editor.openFrequencyMarkersEditor( m_editor.m_selectedDataset.groupId, m_editor.m_selectedDataset.datasetId, nyquist, markers); }关键点可编辑范围 0…Nyquist其中Nyquist fftSamplingRate * 0.5qMax(1, …)兜底采样率 ≤ 0 的异常情况未设置的阈值NaN在 QVariantList 里以无效 QVariant表达QML 侧据此显示空输入信号openFrequencyMarkersEditor(groupId, datasetId, nyquist, markers)声明于 core/Ui/ProjectEditor/ProjectEditor.h。3.2 提交槽与 commitAlarmBands 完全同构提交实现在 core/Ui/ProjectEditor/EditorCommit.cpp与读路径共享同一套校验逻辑非正/越界频率丢弃、反向频带降级、倒置阈值交换然后m_editor.m_selectedDataset.fftMarkers.push_back(std::move(marker)); ... auto pm m_model; pm.updateDataset(m_editor.m_selectedDataset.groupId, m_editor.m_selectedDataset.datasetId, m_editor.m_selectedDataset, false); m_editor.m_forms.buildDatasetModel(m_editor.m_selectedDataset);即pm.updateDataset(..., false)buildDatasetModel——与commitAlarmBands完全相同的形状不触碰任何 ProjectModel 构造函数可达的代码规避启动期风险。仪表盘的实时拾取走既有的 modified → autosave →syncRuntime()路径与报警频带一致无需重启即可看到更新。3.3 QML 对话框T6对话框是 app/qml/ProjectEditor/Dialogs/FrequencyMarkersEditor.qml从 AlarmBandsEditor 的语法克隆而来基于Widgets.SmartDialog预置卡片静态预置项超出 Nyquist 的条目自动跳过源码中可见三组Mains Hum (50 Hz harmonics)50 Hz Mains#ffb300、150 Hz 3x#f4511eMains Hum (60 Hz harmonics)60 Hz 3x 谐波1/3 倍频程频带组如 178–355 Hz 250 Hz#fdd835、355–708 Hz 500 Hz#7cb342、11200–22400 Hz 16 kHz#d81b60标记表格Start Hz、End Hz留空 点标记、Label、Color 色块 共享 ColorDialog 右键重置、Warn dB、Alarm dB、上移/下移/删除内联校验编辑时 Hz 钳制到 0…Nyquist收集时warn alarm归一化空白/非法数字视为未设置实时预览条0 Hz 到 Nyquist 的刻度条上频带渲染为半透明区域、点标记渲染为刻度线底部 Cancel / ApplyApply 调用Cpp_JSON_ProjectEditor.commitFrequencyMarkers(root.collectMarkers())见该文件第 792 行。DatasetView 侧app/qml/ProjectEditor/Views/DatasetView.qml用懒加载 Loader ConnectionsonOpenFrequencyMarkersEditor挂接对话框Behavior 分区新增 Freq. Markers 功能区按钮仅当所选数据集启用了DatasetFFT || DatasetWaterfall时可用。新 QML 文件已在 app/CMakeLists.txt 注册紧邻 AlarmBandsEditor 的条目。4. FFT 控件标记渲染与逐 tick 监视T4、T54.1 模型侧bin 窗口解析与零分配监视C 模型core/Ui/UI/Widgets/FFTPlot.cpp在构造时通过loadMarkers()复制数据集标记m_markerRt运行时数组 一次性构建的m_markerConfigQVariantList随后rebuildMarkerBins()把每个标记解析为闭包的 FFT bin 窗口FFTPlot.cppvoid Widgets::FFTPlot::rebuildMarkerBins() { constexpr double pointHalfWindow 2.0; ... const int spectrumSize m_size / 2; const double freqStep static_castdouble(m_samplingRate) / qMax(1, m_size); const double lastBin qMax(0, spectrumSize - 1); for (auto rt : m_markerRt) { double lo 0.0; double hi 0.0; if (rt.freqHi rt.freqLo) { lo std::floor(rt.freqLo / freqStep); hi std::ceil(rt.freqHi / freqStep); } else { const double center std::round(rt.freqLo / freqStep); lo center - pointHalfWindow; hi center pointHalfWindow; } rt.binLo static_castint(qBound(0.0, lo, lastBin)); rt.binHi static_castint(qBound(static_castdouble(rt.binLo), hi, lastBin)); } }要点点标记取 ±2 bins 邻域——轻微失谐的谱线仍能登记峰值spec Decisions 中明确的计划期常量bin 宽度随 FFT 尺寸变化因此每次rebuildFftPlanFFT 大小改变时都要重新解析否则标记会静默瞄偏架构笔记中特别强调此点bin 数学在转换为 int 之前于 double 域钳制qBound避免不可表示 double 强转 int 的未定义行为qt-cpp-review 确认修复项。逐 tick 监视在updateData()的computeBinSpectrum之后调用updateMarkerValues()FFTPlot.cppbool Widgets::FFTPlot::updateMarkerValues(const int spectrumSize) { ... bool changed false; for (auto rt : m_markerRt) { const int hi qMin(rt.binHi, spectrumSize - 1); float peak kSpectrumFloorDb; for (int i qMin(rt.binLo, hi); i hi; i) peak std::max(peak, m_binDb[static_caststd::size_t(i)]); const int previous_state rt.state; const int previous_label qRound(rt.peakDb * 10.0f); rt.peakDb peak; if (std::isfinite(rt.alarmDb) peak rt.alarmDb) rt.state 2; else if (std::isfinite(rt.warningDb) peak rt.warningDb) rt.state 1; else rt.state 0; changed | rt.state ! previous_state || qRound(rt.peakDb * 10.0f) ! previous_label; } return changed; }设计约束与验证重点tasks T4 Verify零稳态分配marker/bin/state 向量在构造和rebuildFftPlan时预分配per-tick 循环只比较浮点markersQVariantList 只构建一次CONSTANT 式配置只评估显示 dB峰值来自后弹道post-ballistics处理的显示频谱m_binDb——所见即所判WYSIWYG与--benchmark-hotpath门禁无关NaN 感知比较std::isfinite守卫后再比较未设阈值永不上报状态状态语义0 normal / 1 warning / 2 alarmFFTPlot.cpp越界索引返回频谱底噪 dB 或 0 状态信号只在状态或读数0.1 dB 分辨率取整比较真正变化时触发——避免每个 tick 都发信号qt-cpp-review 发现并修复的问题 F15。4.2 QML 侧双层叠加渲染T5渲染全部在 app/qml/Widgets/Dashboard/FFTPlot.qml 中完成采用双层叠加下层频带/谱线parent: plot.curveLayer且z: -1FFTPlot.qml所以标记渲染在频谱曲线笔画之下永不遮挡曲线频带 带水平渐变的半透明矩形边缘 0.04 → 核心 0.22 不透明度 1 px 边缘描边DAW-EQ 风格点标记 2 px 竖线 8 px 柔光矩形背景。上层标签芯片独立于曲线之上的胶囊标签层z: 1000文本格式为label · −42.1 dB实时读数告警时通过Cpp_ThemeManager.alarmColorForSeverity重新着色warning 用 severity 2、alarm 用 severity 3alarm 状态叠加透明度闪烁SequentialAnimation见 FFTPlot.qml 附近。芯片是可点击的点击 瞬时聚光root.selectedMarker高亮当前、淡化其他且芯片的 MouseArea 透传滚轮保证缩放仍可用。芯片横向重叠时采用简单的贪心行分配JS 遍历 marker x 位置少量条目开销可忽略。Hz → x 映射通过plot.xVisibleMin/xVisibleRangelogX ? log10(freq) : freq——与 PlotCurve/光标完全相同的变换因此缩放/平移自动跟随无需额外同步。工具栏开关R5FFTPlot 顶部工具栏的DashboardToolButton切换labels.svg图标设置以showFrequencyMarkers默认开启持久化到 widgetSettingsFFTPlot.qml。芯片文本只使用带编号的.arg()占位符规避%n翻译陷阱common-mistakes 约定。5. Waterfall 控件同一标记的双视图一致性T7Waterfall专业版QQuickPaintedItem在 core/Ui/UI/Widgets/Waterfall.cpp 与 app/qml/Widgets/Dashboard/Waterfall.qml 中实现同样的标记体系构造时复制标记markers.reserve(dataset.fftMarkers.size())见 Waterfall.cpp逐行峰值/状态updateData()从新写入的m_smoothed行计算每标记峰值m_overlay.updateMarkerStates(...)仅当markersVisible()时执行Waterfall.cpp逐 paint 绘制paint()在缓存的轴层合成之后绘制频带填充/谱线/标签芯片Waterfall.cpp——因为升级色调按行变化放进缓存的轴层会过期每次 paint 额外画几条线相对图像 blit 成本可忽略Hz→x 映射单一来源抽取本地辅助函数visibleFreqWindow()同时被drawXAxis、悬停光标和标记通道使用——从机制上杜绝两条映射漂移plan.md 风险清单明确项标记监视始终在线性 bin 空间进行显示轴线性/对数绝不改变测量对象markersVisible Q_PROPERTY默认 trueWaterfall.cppQML 工具栏开关以同名showFrequencyMarkers持久化Waterfall.qml芯片同样可点击聚光drawMarkerChip逐 paint 记录命中矩形m_chipHitRectsmousePressEvent在启动拖拽平移之前先命中测试悬停显示手型光标Waterfall.cpp。验收要点AC6同一组标记在 Waterfall 的缩放/平移下出现在正确频率上无标记时drawXAxis输出逐字节不变tasks T7 Verify 回读项。6. 远程 API原子读写与数据集更新键T36.1 命令注册与语义API 表面完全镜像报警频带对原子命令project.dataset.getFFTMarkers/project.dataset.setFFTMarkers注册在 core/Api/API/Handlers/ProjectDatasetFieldCommands.cpp。setFFTMarkers的语义是原子替换整个 fftMarkers 数组无效条目静默丢弃并计入result.droppedInvalidProjectDatasetFieldCommands.cppresult.count返回存储后的条数数据集更新键fftMarkers分支加入applyDatasetNumericFields数组逐条经DataModel::read(FrequencyMarker)解析无效条目丢弃键被消费以保证未知字段告警的诚实性免费特性无BUILD_COMMERCIAL门禁——schema 与 FFT 控件为 GPL 免费部分仅 Waterfall 渲染器本身已在控件层面按 Pro 门控遵循既有按键白名单 专用命令模式新键不破坏老客户端旧构建读取时忽略未知键。命令元数据也同步到了脚本侧app/rcc/api/SerialStudio.js、app/rcc/api/SerialStudio.lua、app/rcc/api/api-schema.json以及 gRPC 类型化 protodoc/grpc/serialstudio-typed.protoSDK 由scripts/sanitize-commit.py的 generate-sdk 步骤再生。6.2 API 用法示例基于测试与注册语义以下调用形态参考集成测试 tests/integration/test_fft_markers.py 与命令注册描述写入标记列表原子替换{ command: project.dataset.setFFTMarkers, data: { groupId: 1, datasetId: 2, markers: [ {freq: 800, endFreq: 850, label: Gear mesh, color: #ff5722, warningDb: -40, alarmDb: -25}, {freq: 50, label: Mains, warningDb: -45} ] } }响应中count为实际存储条数droppedInvalid为被丢弃的非法条目数。读取标记列表{ command: project.dataset.getFFTMarkers, data: {groupId: 1, datasetId: 2} }通过通用数据集更新键在project.dataset.update的字段中携带fftMarkers数组语义与setFFTMarkers一致字段级语义细节见 core/Api/API/Handlers/ProjectUpdateCommands.cpp 的说明文本。7. 集成测试与验收T8、AC1/AC2新增的 tests/integration/test_fft_markers.py 覆盖以下场景遵循tests/utils/api_client.py模式与 tests/README.md 约定测试覆盖点test_set_get_round_tripAC2setFFTMarkers原样存储、getFFTMarkers原样回读无droppedInvalidtest_empty_array_clears空数组清空标记列表test_invalid_entries_dropped混合垃圾载荷droppedInvalid 4负频率、坏类型等test_reversed_band_becomes_point反向频带降级为点标记test_reversed_thresholds_swapped倒置 warn/alarm 阈值被交换test_dataset_update_keyproject.dataset.update的fftMarkers键往返test_markers_survive_save/test_markers_survive_reloadAC1保存/重载后标记保持test_no_markers_means_no_key无标记项目序列化不含fftMarkers键字节级兼容运行方式维护者执行python -m py_compile tests/integration/test_fft_markers.py pytest tests/integration/test_fft_markers.py -v # 需应用 API 服务已启动其余验收项为维护者运行时检查tasks.md 的 maintainer AC3–AC7AC3 用音频输入实测 800 Hz 点标记与频带在两种轴模式下的渲染/缩放/深浅主题AC4 验证阈值越过/回升时的升级/降级AC5 对照 AlarmBands 对话框核对观感、越界校验与 Cancel/Apply 语义AC6 核对 Waterfall 同频渲染AC7 确认--benchmark-hotpath门禁无改动。热路径帧摄取/解析零接触已由无 ingest-path difftasks.md Definition of Done与架构笔记共同确认。8. 约束、权衡与设计决策规格要点8.1 非目标Non-Goals明确边界无全局报警系统集成标记报警是控件局部的视觉表现不接入应用级报警监视器、MQTT 或导出FFT 只在控件可见时计算无自动峰值检测/跟踪标记固定在用户配置的频率上不找峰、不追 RPM、不移动标记无谐波/边带游标1x/2x/3x 族用户可自行添加各次谐波族生成属未来工作无逐标记历史/日志读数仅实时无新依赖、无新文件格式标记搭乘既有项目 JSON schema。8.2 关键决策记录Decisions标记存数据集 JSON 而非 widgetSettings工程配置属于项目且须可被 API 寻址而非按控件的装饰性状态阈值取显示 dBFFT 控件原生 Y 单位与用户在图上读到的值一致不做线性单位阈值点标记监视窗口 ±2 bins 固定邻域轻微失谐仍可登记Waterfall 只做渲染 廉价读数不强制升级视觉其 C 绘制的轴层让逐 tick 标签工作成本高昂超出行/带/标签的一致性属于打磨而非契约。8.3 风险与缓解字节级序列化回归0014/0017 纪律FFTMarkers仅非空时写出、可选子键省略由 pytest 往返 未触碰项目 diff 覆盖%n/.arg()翻译陷阱芯片文本仅用编号占位符缩放下 QML 叠加漂移映射与 PlotCurve/光标一致xVisibleMin/xVisibleRange并裁剪到图层防越界出血Waterfall Hz 映射漂移drawXAxis与标记通道共享同一本地辅助函数多选提交commitFrequencyMarkers与commitAlarmBands一样仅作用于m_selectedDataset按钮启用条件对齐行为一致Apply 后仪表盘陈旧沿用 alarm bands 的 updateDataset → autosave →syncRuntime()路径AC5 验证实时拾取。9. 落地路径与速查按 tasks.md 的依赖顺序T1 无依赖T2/T3/T7 依赖 T1T4 依赖 T1T5 依赖 T4T6 依赖 T2T8 依赖 T3T9 依赖 T1–T8完整实现共 9 个任务、横跨约 14 个文件全部已完成。速查索引数据模型core/Core/DataModel/Frame.h、core/Core/DataModel/Frame.cpp、core/Core/DataModel/FrameKeys.h编辑器 Ccore/Ui/ProjectEditor/EditorForms.cpp、core/Ui/ProjectEditor/EditorCommit.cpp、core/Ui/ProjectEditor/ProjectEditor.h编辑器 QMLapp/qml/ProjectEditor/Dialogs/FrequencyMarkersEditor.qml、app/qml/ProjectEditor/Views/DatasetView.qmlFFT 控件core/Ui/UI/Widgets/FFTPlot.cpp、app/qml/Widgets/Dashboard/FFTPlot.qmlWaterfall 控件core/Ui/UI/Widgets/Waterfall.cpp、app/qml/Widgets/Dashboard/Waterfall.qmlAPIcore/Api/API/Handlers/ProjectDatasetFieldCommands.cpp、core/Api/API/Handlers/ProjectUpdateCommands.cpp测试tests/integration/test_fft_markers.py架构笔记doc/claude/architecture/dashboard.md规范文档doc/claude/specs/0019-fft-freq-markers/spec.md、plan.md、tasks.md典型使用流程一句话总结在 Project Editor 中选中 FFT/Waterfall 数据集 → 点击 Freq. Markers 打开对话框 → 从预置50/60 Hz 电源谐波、倍频程带或手工添加标记频率/频带 标签 颜色 可选 dB 阈值→ Apply 提交commitFrequencyMarkers重建向量并更新数据集模型→ 仪表盘 FFT 与 Waterfall 实时渲染标记并逐 tick 监视峰值越过 warning/alarm 阈值即视觉升级整个列表可随时通过project.dataset.get/setFFTMarkers或fftMarkers更新键经远程 API 读写并随项目文件持久化——未加标记的旧项目保存后字节级不变。【免费下载链接】Serial-StudioOpen-source telemetry dashboard. Supports UART, BLE, MQTT, Modbus, CAN Bus and more.项目地址: https://gitcode.com/GitHub_Trending/se/Serial-Studio创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
RELATED — 相关阅读

相关资讯

LATEST — 最新资讯

最新发布

TODAY — 本日精选

新闻

WEEKLY — 本周精选

新闻

MONTHLY — 本月精选

新闻