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

656 lines
25 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
> 基于用户确认的 4 项关键决策修订:① 底部页签容器 ② dd_radar_2d/dd_radar_3d
> ③ 本地文件加载 ④ 单通道处理管线 + 用户提供算法代码。
> 基线分支:`radar`。上一版见同目录 `2026-06-29-radar-bscan-development-plan.md`。
---
## 1. 一句话架构
**雷达 B-Scan = 现有底部页签引擎(`DatasetDetailController` + `ChartStrategyRegistry`)内的一个全新 `ViewKind::BScanProfile` 视图**。`BScanProfileView` 内部自含紧凑工具条 + 左右分割B-Scan 自绘 canvas + A-Scan Qwt 曲线)+ 底部状态栏。数据从前端本地 `.iprb/.iprh` 文件读取,处理管线在 `core/gpr_proc` 纯 C++ 层跑,用户提供各算法实现填入管线节点。
---
## 2. 相比上一版的关键调整
| 项 | 上一版 | 修订版 |
|---|---|---|
| 容器 | 独立 ADS Dock | **底部 `DatasetDetailPanel` 页签**(走 `DatasetDetailController` |
| ddCode | 待定 | **`dd_radar_2d`**(单通道)/**`dd_radar_3d`**(多通道阵列) |
| 数据入口 | 后端 API 或本地文件 | **纯本地文件**`ApiDatasetRepository` 对 radar loaderKey 本地处理) |
| 采集参数 | 后端 API 或头文件 | **全从 `.iprh` 头文件解析**(复用/扩展现有 `IprHeader` |
| 处理算法 | 我实现 | **用户提供代码**,我只定义接口 + 管线框架 |
| 多通道 | 单通道独立跑 | **全载内存,顶部工具条通道切换,处理管线按当前选中单通道跑** |
---
## 3. 数据流(底部页签路线,端到端)
```
[DatasetListPanel 双击雷达数据集 item]
main.cpp:1501 读 kDsIdRole + kDsDdCodeRole="dd_radar_2d"(或 dd_radar_3d
main.cpp:1509 detailCtrl.openDataset(dsId, "dd_radar_2d", dsName, tmObjectId)
[DatasetDetailController::openDataset]
registry_.find("dd_radar_2d") → Radar2dStrategy
strategy->tabs() → [
{ "B-Scan剖面", ViewKind::BScanProfile, "radar.profile", lazy=false },
{ "采集参数", ViewKind::Table, "radar.info", lazy=false },
{ "异常列表", ViewKind::Table, "radar.anomalies", lazy=false }
]
emit datasetOpened(...) → DatasetDetailPanel 建页
loadTab(dsId, "dd_radar_2d", 0) → loadTabImpl("radar.profile")
[ApiDatasetRepository::loadAsync("radar.profile", dsId)]
// 不走网络!本地文件直接读取:
1. 根据 dsId 推断本地路径(约定见 §4
2. io::gpr::parseIprHeader(iprhPath) → 采集参数
3. io::gpr::readIprb(iprbPath, header) → 单通道 B-scan2d
或 assembleGprSurvey(channelPaths, ordPath) → 多通道 GprSurvey3d
4. 组装 GprProfilePayload{原始数据, 头信息, 通道列表}
5. return new LocalDetailLoad(payload) // 同步完成,立即 emit done
[DatasetDetailController::tabReady]
emit tabReady(dsId, 0, payload)
[DatasetDetailPage::setTabPayload]
views_[0]->setPayload(payload) // BScanProfileView 自解包
[BScanProfileView]
1. 解包 GprProfilePayload → 填充通道选择下拉
2. 默认显示通道 0 的原始数据
3. 若用户切换到 proc_data_1 标签:
→ 用当前 ProcessingParams 跑 GprProcessingPipeline
→ 管线输出 proc_data → ColorMapper → 重绘 canvas
4. 用户调整参数 → 只重算脏节点 → 即时刷新
```
**懒加载/分页**:三个页签均 `lazy=false`(开页即载),`paginated=false`B-Scan 全量,采集参数/异常列表量小一次性返回)。异常列表量若大,后续可改 `paginated=true` + `DataTableView`
---
## 4. 本地文件加载方案
### 4.1 约定式本地路径
雷达数据集在前端本地项目目录下的固定结构:
```
{projectDir}/datasets/{dsId}/
├── data.iprh # 头文件(采集参数)
├── data.iprb # B-scan 二进制dd_radar_2d单通道
├── data.ord # 通道偏移dd_radar_3d 时需要)
└── data.gps # GPS 轨迹(可选,地形校正/里程归一化用)
```
`dd_radar_3d` 多通道扩展(若一个数据集含多线或多文件组):
```
{projectDir}/datasets/{dsId}/
├── data.iprh
├── ch01.iprb
├── ch02.iprb
...
├── ch16.iprb
└── channel.ord
```
### 4.2 `ApiDatasetRepository` 雷达分支(零架构改动)
在现有 `ApiDatasetRepository::loadAsync` 中,对 `"radar.*"` loaderKey 直接本地处理,**不发网络请求**。
```cpp
// src/data/api/ApiDatasetRepository.cpp
DetailLoad* ApiDatasetRepository::loadAsync(const std::string& key,
const std::string& dsId, ...) {
if (key == "radar.profile") return makeRadarProfile(dsId);
if (key == "radar.info") return makeRadarInfo(dsId);
if (key == "radar.anomalies") return makeRadarAnomalies(dsId);
// ... 现有网络请求逻辑不变
}
```
`makeRadarProfile` 内部:
1. 根据 `dsId` 组装本地目录路径(`LocalProjectPathResolver::datasetDir(dsId)`
2.`.iprh``IprHeader`
3. `dd_radar_2d`:读单个 `.iprb``BScan` → 转 `float`
4. `dd_radar_3d`:读多通道 `.iprb` + `.ord``assembleGprSurvey``GprSurvey`
5. 返回 `LocalDetailLoad`(同步完成,`QTimer::singleShot(0, ...)` emit done
**优势**:零改动 `DatasetDetailController`、`IAsyncDatasetRepository` 接口、页签引擎。雷达数据只是 `ApiDatasetRepository` 内部的一个本地分支。
### 4.3 `IprHeader` 扩展(采集参数)
现有 `IprHeader` 只有 `samples/lastTrace/channels/timeWindowNs/soilVelocity/distanceInterval`。需要从 `.iprh` 中解析更多字段:
```cpp
// src/io/gpr/IprHeader.hpp 扩展
struct IprHeader {
// 已有字段
int samples = 0;
long lastTrace = 0;
int channels = 0;
double timeWindowNs = 0;
double soilVelocity = 0; // m/s
double distanceInterval = 0; // m
// 新增字段(从 .iprh 文本解析)
QString date; // 采集日期,如 "2022-03-10"
QString time; // 采集时刻,如 "10:46"
QString antennaModel; // 雷达硬件型号,如 "MALA MIRA"
double antennaFreqMHz = 0; // 天线中心频率 MHz
// ... 其他字段按需追加
};
```
**注意**`.iprh` 是文本头,字段名可能不固定。解析器用关键词模糊匹配(如 `"DATE"`、`"ANTENNA"`、`"FREQUENCY"`),缺失字段留空/0 不抛错。
---
## 5. 算法接口契约(用户代码接入点)
用户提供处理算法代码,我只负责**管线框架**(节点编排、参数传递、脏链追踪、缓存、线程调度)。
### 5.1 算法函数签名(用户需实现)
```cpp
// 所有算法统一签名:输入输出为行主序 float 矩阵 [trace][sample]
// meta 提供几何参数dx, dz, velocity 等)
// params 为各算法专属参数结构(见 §5.3
// 算法就地修改 outout 已由调用方分配,大小 = in.size()
using GprAlgoFunc = void(*)(const float* in, float* out,
const GprTraceMeta& meta,
const void* params);
```
**内存布局约定**(必须与用户提供代码一致):
- `in/out` 大小 = `meta.ntraces * meta.samples`
- 索引方式:`in[t * meta.samples + s]`,其中 `t=0..ntraces-1`(道号),`s=0..samples-1`(采样点)
- `t=0` 为测线起点,`s=0` 为地表(时间零点)
### 5.2 管线框架(我实现)
```cpp
// src/core/gpr_proc/GprProcessingPipeline.hpp
namespace geopro::core {
struct GprTraceMeta {
int ntraces = 0;
int samples = 0;
double dx = 0; // 道距 (m)
double dz = 0; // 深度采样间隔 (m 或 ns看 zIsTime)
double x0 = 0;
double z0 = 0;
bool zIsTime = true; // true=ns, false=m
double velocityMPerNs = 0.12;
};
// 节点描述(纯数据,无虚函数,零开销)
struct ProcNodeDesc {
QString name; // 唯一标识,如 "zero_time", "gain"
GprAlgoFunc algo; // 用户提供的算法函数指针
const void* params; // 指向参数结构体的指针(由调用方保证生命周期)
bool enabled = true; // 是否启用
};
class GprProcessingPipeline {
public:
// 注册节点按顺序。params 指针必须指向稳定内存(如 BScanProfileView 成员)。
void registerNode(const ProcNodeDesc& desc);
// 执行管线:从 raw 输入开始,按注册顺序逐节点处理,返回最终输出
// 内部缓存各节点输出,参数未变时直接返回缓存(脏链追踪)
std::vector<float> run(const std::vector<float>& raw,
const GprTraceMeta& meta);
// 标记某节点参数已变更(下次 run 时从该节点开始重算)
void markDirty(const QString& nodeName);
// 标记全部重算
void markAllDirty();
private:
std::vector<ProcNodeDesc> nodes_;
std::unordered_map<QString, std::vector<float>> cache_;
std::unordered_set<QString> dirty_;
};
} // namespace geopro::core
```
### 5.3 参数结构体(用户算法使用)
每个算法一个参数结构体,定义在 `core/model/gpr_proc/` 下。用户按这些结构体传参:
```cpp
// src/core/model/gpr_proc/GprAlgoParams.hpp
#pragma once
namespace geopro::core {
struct ZeroTimeParams {
bool autoDetect = true;
int cutSamples = 30;
int frontSearchWindow = 180;
double noiseSigmaMultiple = 3.0;
};
enum class ZeroDriftMode { DC, Sliding };
struct ZeroDriftParams {
ZeroDriftMode mode = ZeroDriftMode::Sliding;
int slidingWindowSamples = 100;
};
enum class BgMode { MeanAverage, SingularityFilter };
struct BackgroundParams {
BgMode mode = BgMode::MeanAverage;
int averageTraceCount = 301;
double singularityThreshold = 1.8;
};
struct GainParams {
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;
};
struct BandpassParams {
bool autoFreq = true;
double lowFreqHz = 1000;
double highFreqHz = 100;
double antennaFreqMHz = 200.0;
};
struct SmoothParams {
int smoothWindow = 2;
bool verticalSmooth = true;
bool horizontalSmooth = true;
};
enum class TraceBalanceMode { Global, Local };
struct TraceBalanceParams {
TraceBalanceMode mode = TraceBalanceMode::Local;
int horizontalWindowTraces = 31;
double targetRms = 0.0;
double maxGain = 4.0;
double epsilon = 1.0;
};
struct SampleBalanceParams {
int windowSamples = 120;
double targetRms = 0.0;
double maxGain = 6.0;
double epsilon = 1.0;
};
struct HilbertParams {
bool computeEnvelope = true;
};
struct MigrationParams {
int sumWidth = 64;
double velocityMPerNs = 0.12;
};
struct TopoParams {
bool useAverageElevation = true;
double baseElevation = 0.0;
};
struct ResampleParams {
bool resampleTraces = false;
bool resampleSamples = false;
int newTraces = 0;
int newSamples = 0;
};
struct PredictiveDeconParams {
int predLag = 1;
int filterLen = 10;
};
// 总参数BScanProfileView 持有一个实例,各处理对话框修改子字段
struct GprProcessingParams {
ZeroTimeParams zeroTime;
ZeroDriftParams zeroDrift;
BackgroundParams background;
GainParams gain;
BandpassParams bandpass;
SmoothParams smooth;
TraceBalanceParams traceBalance;
SampleBalanceParams sampleBalance;
HilbertParams hilbert;
MigrationParams migration;
TopoParams topo;
ResampleParams resample;
PredictiveDeconParams predictiveDecon;
};
} // namespace geopro::core
```
### 5.4 用户提供代码的接入方式
**方式 A推荐函数指针**
用户把算法实现为 C 风格函数,放在 `src/core/gpr_proc/algo/` 下:
```cpp
// src/core/gpr_proc/algo/gpr_zero_time.cpp用户文件
#include "gpr_proc/GprAlgoParams.hpp"
void gprZeroTime(const float* in, float* out,
const geopro::core::GprTraceMeta& meta,
const void* params) {
const auto& p = *static_cast<const geopro::core::ZeroTimeParams*>(params);
// ... 用户算法实现 ...
}
```
管线注册:
```cpp
pipeline.registerNode({"zero_time", gprZeroTime, &params_.zeroTime});
```
**方式 B类接口若用户偏好 OOP**
```cpp
class IGprAlgorithm {
public:
virtual ~IGprAlgorithm() = default;
virtual void process(const float* in, float* out,
const GprTraceMeta& meta) = 0;
virtual QString name() const = 0;
};
```
**请用户确认偏好 A 还是 B**。A 更轻量C 风格函数指针B 更利于封装状态。
---
## 6. UI 组件架构(适配底部页签空间)
底部页签高度有限(默认 ~250px用户可拖拽拉大。BScanProfileView 内部必须紧凑。
```
BScanProfileViewQWidget实现 IDetailView
├── 顶部工具条单行QHBoxLayout
│ ├── [通道选择] QComboBox # dd_radar_3d 显示2d 隐藏
│ ├── [rawdata|proc_data_1] QButtonGroup # 切换原始/处理后
│ ├── [色阶] QComboBox # Gray / Rainbow / Seismic / ColdWarm
│ ├── [对比度] QSlider(0-200%) + QLabel
│ ├── [比例] QLabel("1:2") + QToolButton(调)
│ ├── [显示异常] QCheckBox
│ ├── [Wiggle] QToolButton(toggle)
│ ├── [道编辑] QToolButton
│ ├── [反向] QToolButton
│ └── [处理参数⚙] QToolButton # 点击弹出 GprProcessingDialog
├── 主体QSplitter水平
│ ├── BScanCanvasQWidget自绘占 80%
│ │ ├── 原始/处理后 float 数据缓存
│ │ ├── ColorMapperfloat→ColorScale→QImage
│ │ ├── ViewTransform世界↔屏幕
│ │ ├── 交互:滚轮缩放/左键平移/左键框选异常/悬停读值/右键菜单
│ │ └── 异常标注列表(屏幕坐标实时投影)
│ └── AScanWaveformViewQWidget占 20%,最小 120px
│ └── QwtPlot + QwtPlotCurve单道波形
└── 底部状态栏QHBoxLayout
├── 道号: 1234 | 里程: 123.45m | 深度: 2.34m | 振幅: -1234.5
└── 当前通道: CH01 | 当前数据: rawdata
```
### 6.1 空间压缩策略
| 原设计 | 底部页签适配 |
|---|---|
| 顶部多行控制栏23+参数) | **单行快捷栏** + "⚙处理参数"按钮弹出对话框 |
| 大面积剖面图 | BScanCanvas 占满可用空间,默认纵向压缩;用户拉大底部 panel 后自动扩展 |
| 右侧 A-Scan | QSplitter 水平分割,可拖拽调宽;默认窄条(~120px |
| 鼠标悬停信息面板 | 收进**底部状态栏**(单行文本),不盖剖面 |
| 异常编辑弹窗 | 模态对话框(`BScanAnomalyDialog`),不常驻 |
| 参数配置区 | `GprProcessingDialog`(模态/非模态对话框),分组折叠面板 |
### 6.2 BScanCanvas 自绘核心
```cpp
class BScanCanvas : public QWidget {
// 数据
std::vector<float> rawData_; // 当前通道原始数据
std::vector<float> procData_; // 处理后数据(由管线输出)
GprTraceMeta meta_;
// 渲染
QImage image_; // 当前显示的 RGB 图像(缓存)
ColorMapper mapper_; // float→QRgb复用 core::ColorScale
// 视图变换
ViewTransform view_; // 世界坐标(m,m) ↔ 屏幕坐标(px)
// 交互状态
enum class Mode { Idle, Panning, Zooming, Marquee, Hover } mode_ = Mode::Idle;
QPoint lastMousePos_;
QRectF marqueeRect_; // 框选异常(世界坐标)
// 异常
std::vector<GprAnomaly> anomalies_;
// A-Scan 联动
int hoverTrace_ = -1; // 当前悬停道号
signals:
void traceSelected(int traceIdx, const float* traceData, int samples);
void anomalySelected(const GprAnomaly& anomaly);
void hoverInfoChanged(const QString& info);
};
```
**paintEvent 管线**
1. 若数据/参数/视口变化 → 重新生成 `QImage``ColorMapper` float→QRgb
2. `QPainter::drawImage` 贴图
3. 画网格线(里程/深度刻度,根据缩放动态抽稀)
4. 画异常框(红框 + 标签)
5. 画十字准星(鼠标悬停位置)
6. 画打标线(如果有 Marker 数据)
**缩放策略**
- 滚轮:以鼠标位置为中心,缩放 `view_.scaleX``view_.scaleY`(可独立锁比例)
- XY 比例按钮:固定 `scaleY / scaleX = ratio`(如 1:2 表示纵向放大 2 倍)
- 全景:一键重置 view 到 fit
**Wiggle 模式**
- 关闭:`drawImage` 正常 raster 图
- 开启:每道画波形线(`QPainter::drawPolyline`),正半周填充色(如黑/红),负半周填充另一色(白/蓝),背景白
### 6.3 A-Scan 联动
```cpp
// BScanCanvas 悬停/点击某道时 emit traceSelected
connect(canvas_, &BScanCanvas::traceSelected, this, [this](int t, const float* data, int n) {
aScanCurve_->setSamples(data, n); // QwtPlotCurve 更新
aScanPlot_->replot();
});
```
---
## 7. Payload、ViewKind 与策略
### 7.1 新增 ViewKind
```cpp
// src/controller/DatasetDetailTab.hpp
enum class ViewKind {
Scatter, FilledContour, Bar, LineProfile, PolylineMap, Table, WebMap,
BScanProfile // ← 新增
};
```
### 7.2 新增 Payload
```cpp
// src/core/model/detail/DetailPayloads.hpp
// 单通道剖面数据B-Scan 核心载荷)
struct GprChannelData {
QString channelName; // 如 "CH01"
std::vector<float> data; // traces × samples行主序
};
struct GprProfilePayload {
GprTraceMeta meta; // 几何参数
std::vector<GprChannelData> channels; // 多通道数据2d 时 size=1
int currentChannel = 0; // 默认显示通道
// 采集参数(从头文件解析,供采集参数页签展示)
QString date, time, antennaModel;
double antennaFreqMHz = 0;
// 色阶(默认 Gray
ColorScale defaultScale;
};
// 雷达异常标注前后端共用M1 先本地)
struct GprAnomaly {
QString id;
QString typeCode; // cavity / loose / void / pipe
QString typeName;
int traceStart = 0, traceEnd = 0;
int sampleStart = 0, sampleEnd = 0;
double distStartM = 0, distEndM = 0;
double depthStartM = 0, depthEndM = 0;
double widthM = 0, heightM = 0, lengthM = 0;
double burialDepthM = 0, clearanceM = 0;
double confidence = 0;
QString remark;
};
Q_DECLARE_METATYPE(geopro::core::GprProfilePayload)
```
### 7.3 策略
```cpp
// src/app/panels/chart/Radar2dStrategy.hpp
struct Radar2dStrategy : controller::IDatasetChartStrategy {
std::string ddCode() const override { return "dd_radar_2d"; }
std::vector<controller::TabSpec> tabs() const override {
return {
{QStringLiteral("B-Scan剖面"), controller::ViewKind::BScanProfile,
QStringLiteral("radar.profile"), false, false},
{QStringLiteral("采集参数"), controller::ViewKind::Table,
QStringLiteral("radar.info"), false, false},
{QStringLiteral("异常列表"), controller::ViewKind::Table,
QStringLiteral("radar.anomalies"), false, false},
};
}
};
// Radar3dStrategy.hpp同结构ddCode="dd_radar_3d"
```
### 7.4 工厂分支
```cpp
// src/app/panels/chart/DetailViewFactory.cpp
case controller::ViewKind::BScanProfile: {
auto* bscan = new BScanProfileView(parent);
// 注入所需仓储/回调(如需)
return std::unique_ptr<IDetailView>(bscan);
}
```
---
## 8. 文件清单与开发顺序
### Phase 1数据模型 + 管线框架(无算法,无 UI
| # | 文件 | 说明 |
|---|---|---|
| 1.1 | `src/core/model/gpr_proc/GprTraceMeta.hpp` | 几何元数据 |
| 1.2 | `src/core/model/gpr_proc/GprAlgoParams.hpp` | 全部算法参数结构体 |
| 1.3 | `src/core/model/gpr_proc/GprAnomaly.hpp` | 异常标注模型 |
| 1.4 | `src/core/model/detail/DetailPayloads.hpp` | 追加 `GprProfilePayload` + `Q_DECLARE_METATYPE` |
| 1.5 | `src/core/gpr_proc/GprProcessingPipeline.hpp/cpp` | 管线框架(注册/执行/缓存/脏链) |
| 1.6 | `src/io/gpr/IprHeader.hpp/cpp` | 扩展头文件字段date/time/antenna/freq |
| 1.7 | `tests/core/gpr_proc/test_pipeline.cpp` | 管线框架测试(用 stub 算法) |
### Phase 2页签引擎接入策略 + 本地加载 + 工厂)
| # | 文件 | 说明 |
|---|---|---|
| 2.1 | `src/controller/DatasetDetailTab.hpp` | 追加 `BScanProfile` ViewKind |
| 2.2 | `src/app/panels/chart/Radar2dStrategy.hpp` | 2d 策略 |
| 2.3 | `src/app/panels/chart/Radar3dStrategy.hpp` | 3d 策略 |
| 2.4 | `src/app/panels/chart/BScanProfileView.hpp/cpp` | 壳子(先空白 QWidget |
| 2.5 | `src/app/panels/chart/DetailViewFactory.cpp` | 追加 `BScanProfile` 分支 |
| 2.6 | `src/data/api/ApiDatasetRepository.hpp/cpp` | 追加 `makeRadarProfile/Info/Anomalies` |
| 2.7 | `src/app/main.cpp` | 注册 Radar2d/3d 策略 |
| 2.8 | `src/app/CMakeLists.txt` | 追加新 .cpp |
| 2.9 | `src/core/CMakeLists.txt` | 追加 gpr_proc 子目录 |
### Phase 3B-Scan 自绘核心
| # | 文件 | 说明 |
|---|---|---|
| 3.1 | `src/app/panels/radar/ViewTransform.hpp/cpp` | 世界↔屏幕坐标变换(纯几何,可单测) |
| 3.2 | `src/app/panels/radar/ColorMapper.hpp/cpp` | float→ColorScale→QRgb复用 core::ColorScale |
| 3.3 | `src/app/panels/radar/BScanCanvas.hpp/cpp` | 自绘核心paintEvent + 数据缓存) |
| 3.4 | `src/app/panels/radar/AScanWaveformView.hpp/cpp` | QwtPlot + QwtPlotCurve |
| 3.5 | `src/app/panels/radar/BScanToolbar.hpp/cpp` | 顶部单行工具条 |
| 3.6 | `tests/render/test_bscan_canvas.cpp` | 自绘单元测试(用 fixture 数据画参考图) |
### Phase 4交互 + 处理对话框
| # | 文件 | 说明 |
|---|---|---|
| 4.1 | `src/app/panels/radar/BScanCanvas.cpp` | 补交互:滚轮/平移/框选/悬停/右键菜单 |
| 4.2 | `src/app/panels/radar/GprProcessingDialog.hpp/cpp` | 处理参数对话框(分组折叠面板) |
| 4.3 | `src/app/panels/radar/BScanAnomalyDialog.hpp/cpp` | 异常编辑对话框 |
| 4.4 | `src/app/panels/radar/BScanProfileView.cpp` | 总装:工具条 + Canvas + A-Scan + 状态栏 + 管线集成 |
### Phase 5算法接入用户提供代码后
| # | 文件 | 说明 |
|---|---|---|
| 5.1 | `src/core/gpr_proc/algo/` | 用户算法文件目录(函数实现) |
| 5.2 | `src/app/panels/radar/BScanProfileView.cpp` | 管线注册各节点,绑定参数 |
| 5.3 | `tests/core/gpr_proc/test_algo_*.cpp` | 各算法单元测试 |
---
## 9. 仍需确认的事项
### 🔴 接入方式确认1 项,阻塞 Phase 5
**算法接入方式**:用户提供处理代码时,偏好哪种接口?
- **A推荐**C 风格函数指针 `void algo(const float* in, float* out, const GprTraceMeta& meta, const void* params)`,放在 `src/core/gpr_proc/algo/*.cpp`
- **B**C++ 抽象类 `IGprAlgorithm`,用户继承实现 `process()` 方法
### 🟡 中优先级(影响细节,不阻塞框架开发)
2. **`.iprh` 头文件完整字段列表**:现有头文件只有 samples/lastTrace/channels/timeWindow/soilVelocity/distanceInterval。用户的"采集参数"还需要 Date/Time/Antenna/Frequency。`.iprh` 文本中这些字段的**精确关键词**是什么?(如 `"DATE"`、`"TIME"`、`"ANTENNA"`、`"FREQUENCY"`?)缺失字段是否允许留空?
3. **色阶预置方案**:用户提到"灰度图、彩虹色阶、冷暖色阶、振幅色阶"。这些是否已有标准定义?我可以先预置几套 `core::ColorScale` stops
- Gray: 黑(0) → 白(max)
- Rainbow: 紫→蓝→青→绿→黄→红
- Seismic/ColdWarm: 蓝(负) → 白(0) → 红(正)
请确认或提供具体 RGB 断点。
4. **Wiggle 模式配色**:正半周填什么色?负半周填什么色?线色?背景色?(标准地震勘探:正=黑/红,负=白/蓝,背景白)
5. **异常类型完整枚举**:用户提到"空洞/疏松/脱空/管线",是否还有其他类型?需要完整列表用于下拉选择。
6. **本地文件路径约定**`{projectDir}/datasets/{dsId}/data.iprb` 这个约定是否 OK还是需要从项目配置中读某个字段来确定根目录
---
## 10. 下一步行动建议
1. **你确认算法接入方式A 函数指针 / B 抽象类)** → 我锁定管线接口
2. **我启动 Phase 1 + Phase 2**(数据模型 + 管线框架 + 页签引擎接入)
3. **Phase 1/2 完成后,我给你一个可运行的空白 B-Scan 页签**(能双击雷达数据集打开页签、加载本地文件、显示空白画布)
4. **你同步准备算法代码**(按我定义的接口签名实现)
5. **我接 Phase 3/4**(自绘核心 + 交互 + 对话框)
6. **你提供算法代码后Phase 5 接入**
是否按此顺序推进?还是先确认完所有事项再开始编码?