27 KiB
雷达 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<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::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/多线程友好。
核心抽象:
// 处理节点接口
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)
核心组件:
BScanWorkbench(ADS::CDockWidget 外壳)
├── BScanProfileView(QWidget,自绘主体)
│ ├── 顶部工具栏:测线选择/通道选择/色阶/对比度/XY比例/显示异常/各处理参数按钮
│ ├── BScanCanvas(QWidget,自绘核心)
│ │ ├── 原始数据缓存(std::vector<float> raw_)
│ │ ├── 处理后数据缓存(std::vector<float> proc_,或实时从管线取)
│ │ ├── ColorMapper(float min/max → ColorScale → QRgb)
│ │ ├── ViewTransform(世界坐标 ↔ 屏幕坐标:平移/缩放矩阵)
│ │ ├── 交互状态机(Idle / Panning / Zooming / MarqueeSelect / Hover)
│ │ └── 异常标注列表(std::vector<BScanAnomaly>,世界坐标存储)
│ └── AScanWaveformView(QWidget,内嵌右侧)
│ └── QwtPlot + QwtPlotCurve(单道波形实时刷新)
├── ObjectAttrPanel(右侧面板上)— 复用现有 KeyValueView
└── ObjectExceptionPanel(右侧面板下)— 复用现有异常列表逻辑
BScanCanvas 渲染管线:
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):
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/(新建目录)
// 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 旁新增:
// 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 及之后)
-
ddCode 命名
- 雷达数据集在后端的
ddCode是什么?例如dd_gpr_data、dd_radar_profile、dd_gpr_bscan? - 是否按"三维雷达"vs"二维雷达"分不同 ddCode?(
.rd3是三维阵列雷达,.iprb是二维单通道)
- 雷达数据集在后端的
-
数据集入口方式
- 用户双击数据集后,前端如何获取雷达文件的本地路径?
- 方案A:后端
/business/dataset/detail返回filePath字段,前端直接读本地文件。 - 方案B:后端提供
/business/dd/gpr/download接口,前端先下载到临时目录再读。 - 方案C:后端直接提供
/business/dd/gpr/profile返回二进制 B-scan 数据,前端不碰文件系统。 - 推荐 A 或 B(前端实时处理需要本地文件随机访问/seek)。
-
雷达采集参数的数据来源
- 用户的"对象属性"表格(Date/Time/Antenna/Frequency/Traces/Channels 等)来自哪里?
- 方案A:全从
.iprh/.rd3头文件解析(已有IprHeader部分字段,需扩充)。 - 方案B:后端
/business/dd/gpr/info返回结构化 JSON,前端解析填表。 - 推荐 A(M1)+ B(M2 补充后端管理字段)。
-
多通道数据模型
- 阵列雷达(16通道)在界面上是"一次全载 16 通道到内存"还是"按需切换通道懒加载"?
- 16 通道 × 3778 道 × 516 采样 × 4字节(float) ≈ 120MB,全载可接受。
- 但处理管线是"每个通道独立跑"还是"跨通道联合处理"?(背景去除的 MeanAverage 是跨道,但这里用户描述是按通道的"同通道道数量"平均)
- 建议 M1 先全载,切换通道时即时换数据,处理管线按单通道跑。
🟡 中优先级(影响 UI 细节,不阻塞架构)
-
异常标注的数据模型确认
- 异常的世界坐标系:用 道号+采样点(原始索引)还是 里程+深度(物理坐标)?
- 用户说"上传点坐标,对应测线名称,距离和深度等"——建议双存:前端用道号/采样点(抗重采样不变性),显示和上传时转里程/深度。
- 异常类型枚举:用户提到"空洞/疏松/脱空/管线"——是否还有其他?需要完整列表。
- 截图要求:"切片截图和剖面截图"——M1 阶段用
QWidget::grab()生成QPixmap存本地临时文件,M2 上传时作为 multipart/form-data 附件?
-
色阶方案
- 用户提到"灰度图、彩虹色阶、冷暖色阶、振幅色阶"——这些是否已有定义?
- 复用现有
core::ColorScale(阶梯色阶)+ 预置几套默认 stops(Gray/Rainbow/Seismic/Amplitude)。 - 是否支持与 2D/3D 视图色阶联动?(跨视图色阶真源
DatasetViewState)——建议 M1 先独立,M2 视需求接入联动。
-
处理参数默认值
- 用户给出了大量默认值(如
cutSamples=30、slidingWindowSamples=100等)。 - 这些默认值是"写死在前端代码"还是"后端
/business/dd/gpr/defaultParams返回"? - 建议 M1 写死,M2 后端可覆盖。
- 用户给出了大量默认值(如
-
Wiggle 模式细节
- Wiggle 画的波形线:正半周填充什么颜色?负半周填充什么颜色?线宽?背景色?
- 参考标准:地震勘探通常正半周填黑/红,负半周填白/蓝,背景白色。
-
打标(Marker)数据格式
- 用户说"第一列是道号,第二列是采样点,第三列是类型,第四列是可以插入的里程号"。
- 这个数据存在哪里?
.mrk文件?后端 API?需要样例。
-
rd3 格式文档/样例
.rd3是 MALA 三维探地雷达格式,需要头文件结构说明或样例文件,才能写解析器。- 是否与
.iprb布局类似(int16 道序存储)?
🟢 低优先级(可开发中迭代)
-
独立工作区 vs 底部页签
- 当前方案是独立 ADS Dock。如果产品坚持要走底部
DatasetDetailPanel页签,需要大幅压缩 UI(控制栏收进折叠面板,A-Scan 弹窗或取消)。请确认。
- 当前方案是独立 ADS Dock。如果产品坚持要走底部
-
性能基准
- "几分钟处理完公里级三维探地雷达数据"——具体数据量?
- 如: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 顺序进入开发。