geopro/docs/superpowers/specs/2026-06-29-radar-bscan-deve...

465 lines
27 KiB
Markdown
Raw 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.

# 雷达 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` 面向函数曲线,不适合 rasterVTK 过重且离屏渲染延迟高。QImage 直接操作像素,处理管线输出 `std::vector<float>` → 映射色阶 → `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<GprAnomaly>` 驻内存,支持增删改+截图关联;后端 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::GprSurveyntraces×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-scanfloat 矩阵,与输入同维度)
▼ 参数变更时只重算脏节点下游(缓存各节点输出)
[app/panels/radar/ BScanProfileView]
- 原始/处理后数据各一份(或处理后实时生成)
- ColorMapperfloat 值域 → ColorScale → RGBA 像素
- QPainter画像素矩阵 + 网格线 + 异常框 + 十字准星
- 交互:滚轮缩放/平移变换矩阵、框选QRectF 世界坐标、悬停mouseMove 转采样/道号)
▼ 点击剖面位置
[A-Scan 子视图] AScanWaveformViewQwtPlotCurve 实时更新)
▼ 右侧面板
[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<float> run(const std::vector<float>& raw, const GprTraceMeta& meta);
// 获取指定节点的输出(用于调试/中间结果展示)
const std::vector<float>& nodeOutput(const QString& name) const;
private:
std::vector<std::unique_ptr<IGprProcNode>> nodes_;
std::unordered_map<QString, std::vector<float>> cache_;
std::unordered_set<QString> 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
**核心组件**
```
BScanWorkbenchADS::CDockWidget 外壳)
├── BScanProfileViewQWidget自绘主体
│ ├── 顶部工具栏:测线选择/通道选择/色阶/对比度/XY比例/显示异常/各处理参数按钮
│ ├── BScanCanvasQWidget自绘核心
│ │ ├── 原始数据缓存std::vector<float> raw_
│ │ ├── 处理后数据缓存std::vector<float> proc_或实时从管线取
│ │ ├── ColorMapperfloat min/max → ColorScale → QRgb
│ │ ├── ViewTransform世界坐标 ↔ 屏幕坐标:平移/缩放矩阵)
│ │ ├── 交互状态机Idle / Panning / Zooming / MarqueeSelect / Hover
│ │ └── 异常标注列表std::vector<BScanAnomaly>,世界坐标存储)
│ └── AScanWaveformViewQWidget内嵌右侧
│ └── 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<float>& data, const GprTraceMeta& meta);
void processedReady(const std::vector<float>& data, const GprTraceMeta& meta);
void anomalyListChanged(const std::vector<GprAnomaly>& 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 BB-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后端对接M2API 就绪后)
| 顺序 | 文件 | 说明 |
|---|---|---|
| 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前端解析填表。
- **推荐 AM1+ BM2 补充后端管理字段)**。
4. **多通道数据模型**
- 阵列雷达16通道在界面上是"一次全载 16 通道到内存"还是"按需切换通道懒加载"
- 16 通道 × 3778 道 × 516 采样 × 4字节(float) ≈ **120MB**,全载可接受。
- 但处理管线是"每个通道独立跑"还是"跨通道联合处理"?(背景去除的 MeanAverage 是跨道,但这里用户描述是按通道的"同通道道数量"平均)
- **建议 M1 先全载,切换通道时即时换数据,处理管线按单通道跑**。
### 🟡 中优先级(影响 UI 细节,不阻塞架构)
5. **异常标注的数据模型确认**
- 异常的世界坐标系:用 **道号+采样点**(原始索引)还是 **里程+深度**(物理坐标)?
- 用户说"上传点坐标,对应测线名称,距离和深度等"——建议**双存**:前端用道号/采样点(抗重采样不变性),显示和上传时转里程/深度。
- 异常类型枚举:用户提到"空洞/疏松/脱空/管线"——是否还有其他?需要完整列表。
- 截图要求:"切片截图和剖面截图"——M1 阶段用 `QWidget::grab()` 生成 `QPixmap` 存本地临时文件M2 上传时作为 multipart/form-data 附件?
6. **色阶方案**
- 用户提到"灰度图、彩虹色阶、冷暖色阶、振幅色阶"——这些是否已有定义?
- 复用现有 `core::ColorScale`(阶梯色阶)+ 预置几套默认 stopsGray/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-FFT516点很小实测不足再引入 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 顺序进入开发。_