# 雷达 B-Scan 详情页开发方案 — 2026-06-29 > 状态:方案阶段,待确认事项见第7节。确认后即可按第6节顺序开发。 > 基线分支:`radar`(基于 `fix/3d-volume-blanking-mask`)。 --- ## 1. 一句话架构 **B-Scan 详情页 = 独立 ADS Dock 工作区(非底部页签)**,内部自含: - 顶部控制栏(23+ 处理参数 + 色阶/对比度/比例/模式切换) - 主体 B-Scan 剖面图(灰度/彩色/Wiggle 可切换,支持框选异常、滚轮缩放、悬停读值) - 右侧内嵌 A-Scan 单道波形(点击剖面任意位置联动) - 右侧面板对象属性(采集参数只读表)+ 对象异常(异常列表+操作) 数据流:**本地文件/后端 → `io/gpr` 解析 → `core/gpr_proc` 信号处理管线 → `BScanProfileView` 自渲染(QImage + QPainter,非 Qwt/VTK)→ 参数调整即时重绘**。 --- ## 2. 关键决策与理由 | 决策 | 选择 | 理由 | |---|---|---| | 容器 | **独立 ADS CDockWidget**(类似 `MapViewPanel`) | 底部 `DatasetDetailPanel` 空间(~200-300px高)无法承载密集控制栏+大面积剖面交互+A-Scan波形。独立 dock 可最大化利用屏幕,且雷达分析是专业深度工作流,值得独占工作区。 | | B-Scan 渲染 | **QImage + QPainter 自绘**(非 Qwt/VTK) | B-Scan 是二维 raster 图像(traces × samples 的像素矩阵),非曲线/等值线。Qwt 的 `QwtPlot` 面向函数曲线,不适合 raster;VTK 过重且离屏渲染延迟高。QImage 直接操作像素,处理管线输出 `std::vector` → 映射色阶 → `setPixel`/`scanLine`,足够快且可控。 | | A-Scan 渲染 | **QwtPlot 曲线**(复用现有 Qwt 基础设施) | A-Scan 是单道振幅-深度折线,Qwt 的 `QwtPlotCurve` 完全匹配,且项目已链 Qwt。 | | 处理管线 | **前端 C++ 实时计算**(内存常驻 + 多线程) | 用户明确要求"几分钟处理完公里级数据"。全量数据(32MB/测线)驻内存,参数调整只重跑相关处理节点(脏链追踪),避免全量重算。重负载节点(滤波、偏移、反褶积)丢 `QtConcurrent::run` 后台线程,主线程保持 UI 响应。 | | 数据入口 | **先本地文件(.iprb/.iprh/.ord),后接后端 API** | API 和 ddCode 尚未设计。M1 先走本地文件入口验证处理管线和 UI;后端契约确定后,在 `data` 层补 `loadAsync` 分发即可,视图层不动。 | | 异常标注 | **前端本地状态(M1)+ 后端同步接口(M2)** | 前端用 `std::vector` 驻内存,支持增删改+截图关联;后端 API 就绪后,在 `data` 层补 `saveAnomalies/loadAnomalies`,控制器层补上传逻辑。 | --- ## 3. 数据流(端到端) ``` [用户双击雷达数据集] DatasetListPanel::itemDoubleClicked main.cpp ──► 判断 ddCode == "dd_gpr_data"(或雷达相关 ddCode) │ ▼ 走独立路由(不走 DatasetDetailController 的页签引擎) RadarWorkbenchController::openSurvey(dsId, ddCode, dsName, filePath) │ ▼ 如果本地文件已缓存,直接取;否则发请求/读本地文件 [io/gpr 层] readIprb / assembleGprSurvey / 未来 readRd3 │ 输出:core::GprSurvey(ntraces×samples×channels 的 double 值数组) ▼ [core/gpr_proc 层] GprProcessingPipeline 输入:原始 B-scan(某通道的 traces×samples)+ ProcessingParams 节点链:ZeroTime ──► ZeroDrift ──► BackgroundRemove ──► Gain ──► Bandpass ──► Smooth ──► TraceBalance ──► SampleBalance ──► Hilbert ──► Migration ──► TopoCorrect ──► Resample ──► PredictiveDecon 输出:processed B-scan(float 矩阵,与输入同维度) │ ▼ 参数变更时只重算脏节点下游(缓存各节点输出) [app/panels/radar/ BScanProfileView] - 原始/处理后数据各一份(或处理后实时生成) - ColorMapper:float 值域 → ColorScale → RGBA 像素 - QPainter:画像素矩阵 + 网格线 + 异常框 + 十字准星 - 交互:滚轮缩放/平移(变换矩阵)、框选(QRectF 世界坐标)、悬停(mouseMove 转采样/道号) │ ▼ 点击剖面位置 [A-Scan 子视图] AScanWaveformView(QwtPlotCurve 实时更新) │ ▼ 右侧面板 [ObjectAttrPanel] 采集参数只读表(走现有 KeyValueView) [ObjectExceptionPanel] 异常列表(复用现有异常列表面板逻辑) ``` **注意**:此路由**绕过** `DatasetDetailController` / `ChartStrategyRegistry` / `DetailViewFactory` 的页签引擎。雷达详情页是**独立专业工作区**,非通用页签容器。未来若需把雷达也纳入底部页签(如只展示属性表格),可再补一个 `Table` 页签走现有引擎。 --- ## 4. 分层设计 ### 4.1 `core/gpr_proc` — 信号处理管线(纯 C++17,零 Qt/VTK) **设计目标**:可独立单元测试、可脏链追踪、SIMD/多线程友好。 **核心抽象**: ```cpp // 处理节点接口 class IGprProcNode { public: virtual ~IGprProcNode() = default; // 输入输出:行主序 float 矩阵 [trace][sample],尺寸由 meta 描述 virtual void process(const float* in, float* out, const GprTraceMeta& meta) = 0; virtual QString name() const = 0; }; // 管线:持有节点链,支持脏链追踪与缓存 class GprProcessingPipeline { public: void setNodeEnabled(const QString& name, bool on); void setNodeParams(const QString& name, const QVariantMap& params); // 执行管线:从原始数据到处理后数据,只重算脏节点 std::vector run(const std::vector& raw, const GprTraceMeta& meta); // 获取指定节点的输出(用于调试/中间结果展示) const std::vector& nodeOutput(const QString& name) const; private: std::vector> nodes_; std::unordered_map> cache_; std::unordered_set dirty_; }; ``` **节点清单(按用户需求的 20+ 种处理)**: | # | 节点名 | 类名 | 复杂度 | 说明 | |---|---|---|---|---| | 1 | 时间零点校正 | `ZeroTimeCorrectionNode` | 低 | 自动/手动模式;自动:前 N 采样内噪声σ倍数阈值找起跳 | | 2 | 去除零漂 | `ZeroDriftRemovalNode` | 低 | DC(整道均值) / Sliding(滑动窗口均值) | | 3 | 背景去除 | `BackgroundRemovalNode` | 中 | MeanAverage(多道平均) / SingularityFilter(SVD) | | 4 | 增益 | `GainNode` | 中 | AGC / SphericalDiffusion / AbsorptionCompensation 等 | | 5 | 带通滤波 | `BandpassFilterNode` | 中 | FFT 实现(需 FFTW 或自实现 Cooley-Tukey);自动/手动频带 | | 6 | 剖面平滑 | `SmoothingNode` | 低 | 2D 均值/高斯滤波,可分别开关横向/纵向 | | 7 | 道间均衡 | `TraceBalanceNode` | 中 | Global(全剖面RMS) / Local(滑动窗口RMS) | | 8 | 道内增益 | `SampleBalanceNode` | 中 | TVG-like:深度方向滑动RMS均衡 | | 9 | 希尔伯特变换 | `HilbertTransformNode` | 中 | 包络/瞬时相位/瞬时频率(FFT-based 或时域 FIR) | | 10 | 偏移处理 | `MigrationNode` | **高** | Kirchhoff / F-K 偏移;需速度模型 | | 11 | 速度分析 | `VelocityAnalysisNode` | **高** | 双曲线拟合 → 速度谱;交互式拾取(后续迭代) | | 12 | 地形校正 | `TopoCorrectionNode` | 中 | 高程→时深转换,波形整体平移/拉伸 | | 13 | 里程归一化 | `DistanceNormalizationNode` | 低 | 打标处插桩号,固定道间距重采样 | | 14 | 数据重采样 | `ResamplingNode` | 低 | 横向/纵向独立,线性插值 | | 15 | 预测反褶积 | `PredictiveDeconNode` | **高** | 自相关 → Levinson-Durbin → 预测误差滤波 | | 16 | 道编辑 | `TraceEditNode` | 低 | 废道删除/置零(前端 UI 传入废道索引列表) | | 17 | 剖面反向 | `ReverseTraceNode` | 低 | 道序反转 | **M1 阶段实现优先级**: - **P0(必须)**:1, 2, 4(基础增益), 5(简化版), 6, 7, 8, 14, 16, 17 - **P1(重要)**:3, 4(球面扩散), 9, 12, 13 - **P2(后续迭代)**:10, 11, 15(计算密集,需更多测试数据验证) **性能策略**: - 节点输出缓存:`run()` 时比较参数 hash,未变则直接返回缓存。 - 并行化:各道独立处理(零漂、增益、均衡等)用 `tbb::parallel_for` 或 `std::execution::par`(C++17)。 - FFT:带通滤波、希尔伯特变换需 FFT。可用 `fftw3`(vcpkg 有)或自实现基2-FFT(数据量 516 采样点,很小,自实现也可接受)。推荐先自实现避免引入新依赖,性能不足再切 FFTW。 ### 4.2 `app/panels/radar/` — B-Scan 视图层(QtWidgets) **核心组件**: ``` BScanWorkbench(ADS::CDockWidget 外壳) ├── BScanProfileView(QWidget,自绘主体) │ ├── 顶部工具栏:测线选择/通道选择/色阶/对比度/XY比例/显示异常/各处理参数按钮 │ ├── BScanCanvas(QWidget,自绘核心) │ │ ├── 原始数据缓存(std::vector raw_) │ │ ├── 处理后数据缓存(std::vector proc_,或实时从管线取) │ │ ├── ColorMapper(float min/max → ColorScale → QRgb) │ │ ├── ViewTransform(世界坐标 ↔ 屏幕坐标:平移/缩放矩阵) │ │ ├── 交互状态机(Idle / Panning / Zooming / MarqueeSelect / Hover) │ │ └── 异常标注列表(std::vector,世界坐标存储) │ └── AScanWaveformView(QWidget,内嵌右侧) │ └── QwtPlot + QwtPlotCurve(单道波形实时刷新) ├── ObjectAttrPanel(右侧面板上)— 复用现有 KeyValueView └── ObjectExceptionPanel(右侧面板下)— 复用现有异常列表逻辑 ``` **BScanCanvas 渲染管线**: ```cpp void BScanCanvas::paintEvent(QPaintEvent*) { QPainter p(this); // 1. 背景 p.fillRect(rect(), Qt::black); // 2. 根据当前标签(rawdata/proc_data_1)选择数据源 const auto& data = (currentTab_ == Raw) ? raw_ : proc_; // 3. 可见区域裁剪:由 viewTransform_ 计算当前窗口对应的 [t0,t1)×[s0,s1) // 4. 逐像素/逐块映射:float → ColorScale → QRgb // - 若缩放比小(全景),聚合多采样取平均后上色(防混叠) // - 若缩放比大(局部),单采样直接上色 // 5. 画网格线(里程/深度刻度,根据缩放动态抽稀) // 6. 画异常框(世界坐标 → 屏幕坐标,红框+标签) // 7. 画十字准星(鼠标悬停位置) // 8. 画打标线(如果有打标数据) } ``` **交互设计细节**: | 交互 | 实现 | 坐标系 | |---|---|---| | 左键拖动 | 平移(修改 viewTransform 的 offset) | 屏幕 delta → 世界 delta | | 滚轮 | 以鼠标位置为中心缩放(修改 viewTransform 的 scaleX/scaleY) | 屏幕锚点 → 世界锚点保持不动 | | 左键框选 | 释放时生成 `BScanAnomaly`(世界坐标矩形) | 屏幕 rect → 世界 rect | | 鼠标悬停 | 状态栏显示:道号、里程、深度、振幅值;同时更新 A-Scan | 屏幕 pos → 道号 t + 采样 s | | 双击异常框 | 弹出异常编辑对话框(类型/深度/尺寸/备注) | — | | 右键菜单 | 切换 X 轴显示:道号 / 距离 / 里程;切换 Y 轴:时间 / 深度 | — | **Wiggle 模式**:点击按钮切换。 - Off:正常灰度/彩色 raster 图。 - On:每道画波形线(振幅→水平偏移,正右负左),填充正/负区域为不同颜色,背景透明/白色。参考地震勘探 wiggle trace 标准画法。 ### 4.3 `controller` — 雷达工作区控制器 新增 `RadarWorkbenchController`(独立于 `DatasetDetailController`): ```cpp class RadarWorkbenchController : public QObject { Q_OBJECT public: void openSurvey(const std::string& dsId, const std::string& ddCode, const QString& name, const QString& filePath); void switchChannel(int channelIndex); void setProcessingParams(const GprProcessingParams& params); void runProcessing(); // 触发管线,完成后 emit processedReady void saveAnomalies(); // M2:调后端 API 上传异常 signals: void surveyLoaded(const GprSurveyInfo& info); // 采集参数、通道列表 void rawDataReady(const std::vector& data, const GprTraceMeta& meta); void processedReady(const std::vector& data, const GprTraceMeta& meta); void anomalyListChanged(const std::vector& anomalies); void progress(int percent, const QString& stage); // 长时间处理进度 }; ``` --- ## 5. 数据模型新增 ### 5.1 `core/model/gpr_proc/`(新建目录) ```cpp // GprTraceMeta.hpp — 单通道剖面的元数据(纯 C++17) struct GprTraceMeta { int ntraces = 0; int samples = 0; double dx = 0; // 道距 (m) double dz = 0; // 采样间隔 (m 或 ns) double x0 = 0; double z0 = 0; bool zIsTime = false; // true=时间(ns), false=深度(m) double velocityMPerNs = 0.12; // 雷达波速,时深转换用 }; // GprProcessingParams.hpp — 全部处理参数的结构化定义 struct GprProcessingParams { // 零点校正 struct ZeroTime { bool autoDetect=true; int cutSamples=30; int frontSearchWindow=180; double noiseSigmaMultiple=3.0; } zeroTime; // 零漂 enum class ZeroDriftMode { DC, Sliding }; struct ZeroDrift { ZeroDriftMode mode=ZeroDriftMode::Sliding; int slidingWindowSamples=100; } zeroDrift; // 背景去除 enum class BgMode { MeanAverage, SingularityFilter }; struct Background { BgMode mode=BgMode::MeanAverage; int averageTraceCount=301; double singularityThreshold=1.8; } background; // 增益(简化:先实现 SphericalDiffusion) struct Gain { bool enableSpherical=true; bool enableAbsorption=true; double velocityMPerNs=0.12; double referenceDepthM=0.01; double exponent=1.5; double absorptionBeta=1.0; double maxGain=30.0; } gain; // 带通滤波 struct Bandpass { bool autoFreq=true; double lowFreqHz=1000; double highFreqHz=100; double antennaFreqMHz=200.0; } bandpass; // 平滑 struct Smooth { int smoothWindow=2; bool verticalSmooth=true; bool horizontalSmooth=true; } smooth; // 道间均衡 enum class TraceBalanceMode { Global, Local }; struct TraceBalance { TraceBalanceMode mode=TraceBalanceMode::Local; int horizontalWindowTraces=31; double targetRms=0.0; double maxGain=4.0; double epsilon=1.0; } traceBalance; // 道内增益 struct SampleBalance { int windowSamples=120; double targetRms=0.0; double maxGain=6.0; double epsilon=1.0; } sampleBalance; // 希尔伯特 struct Hilbert { bool computeEnvelope=true; } hilbert; // 偏移 struct Migration { int sumWidth=64; double velocityMPerNs=0.12; } migration; // 地形校正 struct Topo { bool useAverageElevation=true; double baseElevation=0.0; } topo; // 重采样 struct Resample { bool resampleTraces=false; bool resampleSamples=false; int newTraces=0; int newSamples=0; } resample; // 预测反褶积 struct PredictiveDecon { int predLag=1; int filterLen=10; } predictiveDecon; }; // GprAnomaly.hpp — 雷达异常标注(前后端共用模型) struct GprAnomaly { QString id; // 后端返回或前端临时 UUID QString surveyLineName; int traceStart = 0, traceEnd = 0; // 道号范围 int sampleStart = 0, sampleEnd = 0; // 采样范围 double distanceStartM = 0, distanceEndM = 0; // 里程范围(m) double depthStartM = 0, depthEndM = 0; // 深度范围(m) QString typeCode; // cavity / loose / void / pipe ... QString typeName; double widthM = 0, heightM = 0, lengthM = 0; // 异常尺寸 double burialDepthM = 0; // 埋深 double clearanceM = 0; // 净空 double confidence = 0; // 置信度 0-1 QString remark; QString sliceScreenshotPath; // 切片截图本地路径(M1 先本地文件) QString profileScreenshotPath; // 剖面截图本地路径 }; ``` ### 5.2 `io/gpr/` — rd3 解析器(未来扩展) 在现有 `IprbReader` / `IprHeader` / `GprSurveyAssembler` 旁新增: ```cpp // Rd3Reader.hpp — MALA .rd3 / .rd7 三维雷达格式解析 namespace geopro::io::gpr { struct Rd3Header { /* 天线频率、采样率、道数、通道数等 */ }; Rd3Header parseRd3Header(const std::string& headerText); BScan readRd3(const std::string& path, const Rd3Header& h); BScan readRd3Range(const std::string& path, const Rd3Header& h, std::int64_t t0, std::int64_t t1); } // namespace geopro::io::gpr ``` > **注意**:`.rd3` 格式细节需用户提供样例文件或格式文档。当前先以 `.iprb` 验证管线。 --- ## 6. 文件清单与开发顺序 ### Phase A:基础设施(无 UI,可独立测试) | 顺序 | 文件 | 说明 | |---|---|---| | A1 | `src/core/model/gpr_proc/GprTraceMeta.hpp` | 元数据模型 | | A2 | `src/core/model/gpr_proc/GprProcessingParams.hpp` | 参数结构体(对应用户需求的 20+ 参数) | | A3 | `src/core/model/gpr_proc/GprAnomaly.hpp` | 异常标注模型 | | A4 | `src/core/gpr_proc/IGprProcNode.hpp` | 节点接口 | | A5 | `src/core/gpr_proc/GprProcessingPipeline.hpp/cpp` | 管线编排+脏链追踪 | | A6 | `src/core/gpr_proc/ZeroTimeCorrectionNode.cpp` | 零点校正 | | A7 | `src/core/gpr_proc/ZeroDriftRemovalNode.cpp` | 去零漂 | | A8 | `src/core/gpr_proc/GainNode.cpp` | 基础增益(球面扩散+吸收补偿) | | A9 | `src/core/gpr_proc/BandpassFilterNode.cpp` | 带通滤波(自实现 FFT) | | A10 | `src/core/gpr_proc/SmoothingNode.cpp` | 剖面平滑 | | A11 | `src/core/gpr_proc/TraceBalanceNode.cpp` | 道间均衡 | | A12 | `src/core/gpr_proc/SampleBalanceNode.cpp` | 道内增益 | | A13 | `src/core/gpr_proc/ResamplingNode.cpp` | 数据重采样 | | A14 | `tests/core/gpr_proc/test_gpr_pipeline.cpp` | 管线集成测试(用 fixture 数据断言输出) | | A15 | `tests/core/gpr_proc/test_*.cpp` | 各节点单元测试 | ### Phase B:B-Scan 视图(UI 核心) | 顺序 | 文件 | 说明 | |---|---|---| | B1 | `src/app/panels/radar/BScanCanvas.hpp/cpp` | 自绘核心(QImage raster + 交互状态机) | | B2 | `src/app/panels/radar/ColorMapper.hpp/cpp` | float → ColorScale → QRgb(复用 core::ColorScale) | | B3 | `src/app/panels/radar/ViewTransform.hpp/cpp` | 世界↔屏幕坐标变换(纯几何,可单测) | | B4 | `src/app/panels/radar/AScanWaveformView.hpp/cpp` | A-Scan 波形(QwtPlotCurve) | | B5 | `src/app/panels/radar/BScanProfileView.hpp/cpp` | 总装:工具栏 + BScanCanvas + AScanWaveformView | | B6 | `src/app/panels/radar/BScanToolbar.hpp/cpp` | 顶部控制栏(参数按钮+滑块+下拉) | | B7 | `src/app/panels/radar/GprColorScaleDialog.hpp/cpp` | 色阶选择对话框(可复用 ColorScaleConfigDialog) | | B8 | `src/app/panels/radar/GprParamsDialog.hpp/cpp` | 参数配置对话框(各处理参数的表单) | | B9 | `src/app/panels/radar/BScanAnomalyDialog.hpp/cpp` | 异常编辑对话框(类型/深度/尺寸/备注) | | B10 | `src/app/panels/radar/BScanWorkbench.hpp/cpp` | ADS DockWidget 外壳 + 右侧面板布局 | ### Phase C:控制器与集成 | 顺序 | 文件 | 说明 | |---|---|---| | C1 | `src/controller/RadarWorkbenchController.hpp/cpp` | 控制器:数据加载/处理调度/异常管理 | | C2 | `src/app/main.cpp` | 修改:新增雷达工作区路由、ADS dock 注册、信号接线 | | C3 | `src/app/CMakeLists.txt` | 新增 Phase B 所有 .cpp 文件 | | C4 | `src/core/CMakeLists.txt` | 新增 Phase A 所有 .cpp 文件(gpr_proc 子目录) | | C5 | `tests/...` | 补 UI 单元测试(ViewTransform、ColorMapper)和控制器测试 | ### Phase D:后端对接(M2,API 就绪后) | 顺序 | 文件 | 说明 | |---|---|---| | D1 | `src/data/api/ApiDatasetRepository.cpp` | 新增 loaderKey 分发:`gpr.profile`、`gpr.anomalies` 等 | | D2 | `src/data/dto/GprDto.hpp/cpp` | 雷达采集参数、异常列表的 JSON DTO | | D3 | `src/controller/RadarWorkbenchController.cpp` | 补 `saveAnomalies` / `loadAnomalies` 后端调用 | --- ## 7. 待确认事项(阻塞开发或影响架构) ### 🔴 高优先级(阻塞 Phase C 及之后) 1. **ddCode 命名** - 雷达数据集在后端的 `ddCode` 是什么?例如 `dd_gpr_data`、`dd_radar_profile`、`dd_gpr_bscan`? - 是否按"三维雷达"vs"二维雷达"分不同 ddCode?(`.rd3` 是三维阵列雷达,`.iprb` 是二维单通道) 2. **数据集入口方式** - 用户双击数据集后,前端如何获取雷达文件的本地路径? - 方案A:后端 `/business/dataset/detail` 返回 `filePath` 字段,前端直接读本地文件。 - 方案B:后端提供 `/business/dd/gpr/download` 接口,前端先下载到临时目录再读。 - 方案C:后端直接提供 `/business/dd/gpr/profile` 返回二进制 B-scan 数据,前端不碰文件系统。 - **推荐 A 或 B**(前端实时处理需要本地文件随机访问/seek)。 3. **雷达采集参数的数据来源** - 用户的"对象属性"表格(Date/Time/Antenna/Frequency/Traces/Channels 等)来自哪里? - 方案A:全从 `.iprh` / `.rd3` 头文件解析(已有 `IprHeader` 部分字段,需扩充)。 - 方案B:后端 `/business/dd/gpr/info` 返回结构化 JSON,前端解析填表。 - **推荐 A(M1)+ B(M2 补充后端管理字段)**。 4. **多通道数据模型** - 阵列雷达(16通道)在界面上是"一次全载 16 通道到内存"还是"按需切换通道懒加载"? - 16 通道 × 3778 道 × 516 采样 × 4字节(float) ≈ **120MB**,全载可接受。 - 但处理管线是"每个通道独立跑"还是"跨通道联合处理"?(背景去除的 MeanAverage 是跨道,但这里用户描述是按通道的"同通道道数量"平均) - **建议 M1 先全载,切换通道时即时换数据,处理管线按单通道跑**。 ### 🟡 中优先级(影响 UI 细节,不阻塞架构) 5. **异常标注的数据模型确认** - 异常的世界坐标系:用 **道号+采样点**(原始索引)还是 **里程+深度**(物理坐标)? - 用户说"上传点坐标,对应测线名称,距离和深度等"——建议**双存**:前端用道号/采样点(抗重采样不变性),显示和上传时转里程/深度。 - 异常类型枚举:用户提到"空洞/疏松/脱空/管线"——是否还有其他?需要完整列表。 - 截图要求:"切片截图和剖面截图"——M1 阶段用 `QWidget::grab()` 生成 `QPixmap` 存本地临时文件,M2 上传时作为 multipart/form-data 附件? 6. **色阶方案** - 用户提到"灰度图、彩虹色阶、冷暖色阶、振幅色阶"——这些是否已有定义? - 复用现有 `core::ColorScale`(阶梯色阶)+ 预置几套默认 stops(Gray/Rainbow/Seismic/Amplitude)。 - 是否支持与 2D/3D 视图色阶联动?(跨视图色阶真源 `DatasetViewState`)——建议 **M1 先独立,M2 视需求接入联动**。 7. **处理参数默认值** - 用户给出了大量默认值(如 `cutSamples=30`、`slidingWindowSamples=100` 等)。 - 这些默认值是"写死在前端代码"还是"后端 `/business/dd/gpr/defaultParams` 返回"? - **建议 M1 写死,M2 后端可覆盖**。 8. **Wiggle 模式细节** - Wiggle 画的波形线:正半周填充什么颜色?负半周填充什么颜色?线宽?背景色? - 参考标准:地震勘探通常正半周填黑/红,负半周填白/蓝,背景白色。 9. **打标(Marker)数据格式** - 用户说"第一列是道号,第二列是采样点,第三列是类型,第四列是可以插入的里程号"。 - 这个数据存在哪里?`.mrk` 文件?后端 API?需要样例。 10. **rd3 格式文档/样例** - `.rd3` 是 MALA 三维探地雷达格式,需要头文件结构说明或样例文件,才能写解析器。 - 是否与 `.iprb` 布局类似(int16 道序存储)? ### 🟢 低优先级(可开发中迭代) 11. **独立工作区 vs 底部页签** - 当前方案是独立 ADS Dock。如果产品坚持要走底部 `DatasetDetailPanel` 页签,需要大幅压缩 UI(控制栏收进折叠面板,A-Scan 弹窗或取消)。**请确认**。 12. **性能基准** - "几分钟处理完公里级三维探地雷达数据"——具体数据量? - 如:10km 测线 × 20道/米 × 516 采样 × 16 通道 ≈ 3.2GB 原始数据(int16)。这个量级全内存+实时处理是否可行?是否需要逐段流式处理(Slab)? - **建议先以单测线(~2MB/通道)验证管线性能,再评估大数据量策略**。 --- ## 8. 与现有架构的衔接点 | 现有组件 | 衔接方式 | |---|---| | `io/gpr/IprbReader` | 直接复用 `readIprb` / `readIprbRange` 读原始 B-scan | | `io/gpr/IprHeader` | 解析采集参数(frequency、samples、traces、timeWindow、distanceInterval);需扩展字段(date、time、antenna 型号等,如果头文件里有) | | `io/gpr/GprSurveyAssembler` | 多通道时复用 `assembleGprSurvey` 装配 `core::GprSurvey` | | `core::ColorScale` | B-Scan 上色直接复用,支持全局透明度和 under/over/nan | | `core::GeoLocalFrame` / `GpsTrack` | 里程归一化、地形校正需要 GPS 轨迹 → 复用 `GpsTrack` 解析 `.gps` 文件 | | `app/panels/KeyValueView` | 对象属性面板直接复用 | | `app/panels/ObjectExceptionPanel` / `AnomalyTablePanel` | 异常列表 UI 复用,数据模型从 `core::Anomaly` 扩展为 `core::GprAnomaly` | | `ads::CDockWidget` | BScanWorkbench 继承或封装 ADS 停靠 | | `DatasetViewState` | M2 可选接入跨视图色阶联动 | | `net::ApiClient` / `ApiBatch` | M2 后端对接复用现有网络基础设施 | --- ## 9. 风险评估 | 风险 | 等级 | 缓解措施 | |---|---|---| | FFT 实现性能不足(带通/希尔伯特) | 中 | 先自实现基2-FFT(516点很小),实测不足再引入 FFTW | | 偏移/反褶积算法复杂,M1 难以正确实现 | 高 | M1 不实现(P2),先留接口占位;用简化版本或后端预处理过渡 | | 大数据量(公里级)内存/性能不达标 | 中 | 单测线验证通过后,评估是否需要:① 分块加载(Slab)② 处理管线也分块 ③ 降采样预览 + 全精度导出 | | rd3 格式无文档,解析器开发受阻 | 中 | 向用户/厂商索要格式说明或样例;M1 先用 iprb 验证全链路 | | UI 复杂度高,开发周期长 | 中 | 按 Phase A→B→C 分阶段交付,每阶段可独立验证;BScanCanvas 自绘虽然工作量大但可控 | | API 未设计,后端对接延期 | 低 | M1 全走本地文件,视图和控制器与后端解耦;API 就绪后只改 data 层 | --- _本文档为雷达 B-Scan 详情页的完整开发方案。待第 7 节高优先级事项确认后,按第 6 节 Phase A→B→C 顺序进入开发。_