25 KiB
雷达 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-scan(2d)
或 assembleGprSurvey(channelPaths, ordPath) → 多通道 GprSurvey(3d)
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 直接本地处理,不发网络请求。
// 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 内部:
- 根据
dsId组装本地目录路径(LocalProjectPathResolver::datasetDir(dsId)) - 读
.iprh→IprHeader dd_radar_2d:读单个.iprb→BScan→ 转floatdd_radar_3d:读多通道.iprb+.ord→assembleGprSurvey→GprSurvey- 返回
LocalDetailLoad(同步完成,QTimer::singleShot(0, ...)emit done)
优势:零改动 DatasetDetailController、IAsyncDatasetRepository 接口、页签引擎。雷达数据只是 ApiDatasetRepository 内部的一个本地分支。
4.3 IprHeader 扩展(采集参数)
现有 IprHeader 只有 samples/lastTrace/channels/timeWindowNs/soilVelocity/distanceInterval。需要从 .iprh 中解析更多字段:
// 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 算法函数签名(用户需实现)
// 所有算法统一签名:输入输出为行主序 float 矩阵 [trace][sample]
// meta 提供几何参数(dx, dz, velocity 等)
// params 为各算法专属参数结构(见 §5.3)
// 算法就地修改 out(out 已由调用方分配,大小 = 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 管线框架(我实现)
// 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/ 下。用户按这些结构体传参:
// 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/ 下:
// 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);
// ... 用户算法实现 ...
}
管线注册:
pipeline.registerNode({"zero_time", gprZeroTime, ¶ms_.zeroTime});
方式 B:类接口(若用户偏好 OOP)
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 内部必须紧凑。
BScanProfileView(QWidget,实现 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,水平)
│ ├── BScanCanvas(QWidget,自绘,占 80%)
│ │ ├── 原始/处理后 float 数据缓存
│ │ ├── ColorMapper(float→ColorScale→QImage)
│ │ ├── ViewTransform(世界↔屏幕)
│ │ ├── 交互:滚轮缩放/左键平移/左键框选异常/悬停读值/右键菜单
│ │ └── 异常标注列表(屏幕坐标实时投影)
│ └── AScanWaveformView(QWidget,占 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 自绘核心
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 管线:
- 若数据/参数/视口变化 → 重新生成
QImage(ColorMapperfloat→QRgb) QPainter::drawImage贴图- 画网格线(里程/深度刻度,根据缩放动态抽稀)
- 画异常框(红框 + 标签)
- 画十字准星(鼠标悬停位置)
- 画打标线(如果有 Marker 数据)
缩放策略:
- 滚轮:以鼠标位置为中心,缩放
view_.scaleX和view_.scaleY(可独立锁比例) - XY 比例按钮:固定
scaleY / scaleX = ratio(如 1:2 表示纵向放大 2 倍) - 全景:一键重置 view 到 fit
Wiggle 模式:
- 关闭:
drawImage正常 raster 图 - 开启:每道画波形线(
QPainter::drawPolyline),正半周填充色(如黑/红),负半周填充另一色(白/蓝),背景白
6.3 A-Scan 联动
// 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
// src/controller/DatasetDetailTab.hpp
enum class ViewKind {
Scatter, FilledContour, Bar, LineProfile, PolylineMap, Table, WebMap,
BScanProfile // ← 新增
};
7.2 新增 Payload
// 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 策略
// 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 工厂分支
// 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 3:B-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()方法
🟡 中优先级(影响细节,不阻塞框架开发)
-
.iprh头文件完整字段列表:现有头文件只有 samples/lastTrace/channels/timeWindow/soilVelocity/distanceInterval。用户的"采集参数"还需要 Date/Time/Antenna/Frequency。.iprh文本中这些字段的精确关键词是什么?(如"DATE"、"TIME"、"ANTENNA"、"FREQUENCY"?)缺失字段是否允许留空? -
色阶预置方案:用户提到"灰度图、彩虹色阶、冷暖色阶、振幅色阶"。这些是否已有标准定义?我可以先预置几套
core::ColorScalestops:- Gray: 黑(0) → 白(max)
- Rainbow: 紫→蓝→青→绿→黄→红
- Seismic/ColdWarm: 蓝(负) → 白(0) → 红(正) 请确认或提供具体 RGB 断点。
-
Wiggle 模式配色:正半周填什么色?负半周填什么色?线色?背景色?(标准地震勘探:正=黑/红,负=白/蓝,背景白)
-
异常类型完整枚举:用户提到"空洞/疏松/脱空/管线",是否还有其他类型?需要完整列表用于下拉选择。
-
本地文件路径约定:
{projectDir}/datasets/{dsId}/data.iprb这个约定是否 OK?还是需要从项目配置中读某个字段来确定根目录?
10. 下一步行动建议
- 你确认算法接入方式(A 函数指针 / B 抽象类) → 我锁定管线接口
- 我启动 Phase 1 + Phase 2(数据模型 + 管线框架 + 页签引擎接入)
- Phase 1/2 完成后,我给你一个可运行的空白 B-Scan 页签(能双击雷达数据集打开页签、加载本地文件、显示空白画布)
- 你同步准备算法代码(按我定义的接口签名实现)
- 我接 Phase 3/4(自绘核心 + 交互 + 对话框)
- 你提供算法代码后,Phase 5 接入
是否按此顺序推进?还是先确认完所有事项再开始编码?