修改射线列表下拉框,修改核素分析为框选区域,修改核素区域框选完成以后弹出能量范围的核素
This commit is contained in:
parent
03daa073cc
commit
1c7784702f
|
|
@ -125,6 +125,8 @@ std::vector<FindPeaksBySvd::PeakInfo> FindPeaksBySvd::FindPeaks(const arma::mat
|
|||
peak_info.pos = spec_data_col0.at(pos);
|
||||
peak_info.fwhm = fwhm;
|
||||
peak_info.area = area;
|
||||
peak_info.leftBoundary = left_half;
|
||||
peak_info.rightBoundary = right_half;
|
||||
peak_info_vec.push_back(peak_info);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,6 +15,8 @@ public:
|
|||
double pos = 0.0f; // 峰所在位置
|
||||
double fwhm = 0.0f; // 峰的半高宽
|
||||
double area = 0.0f; // 峰的面积
|
||||
double leftBoundary = 0.0f;//左边界
|
||||
double rightBoundary = 0.0f;//右边界
|
||||
} PeakInfo;
|
||||
|
||||
std::vector<FindPeaksBySvd::PeakInfo> FindPeaks(const arma::mat& spec_data, int step_w = 7) throw(std::string);
|
||||
|
|
|
|||
|
|
@ -1286,3 +1286,9 @@ void MainWindow::on_action_stop_measure_triggered()
|
|||
_measure_client->stopMeasure(device_guid);
|
||||
m_AddressCountTimer->stop();
|
||||
}
|
||||
|
||||
void MainWindow::on_action_device_connect_cfg_triggered()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -100,6 +100,9 @@ private slots:
|
|||
void on_AddressCountTimer();
|
||||
|
||||
void on_EnergyCountTimer();
|
||||
//设备连接管理
|
||||
void on_action_device_connect_cfg_triggered();
|
||||
|
||||
private:
|
||||
QMutex _mutex_info_output;
|
||||
QPlainTextEdit* _plain_edit_info_output;
|
||||
|
|
|
|||
|
|
@ -10,6 +10,53 @@
|
|||
#include <QwtPlotCanvas>
|
||||
#include <QwtText>
|
||||
#include <QPen>
|
||||
|
||||
// 更新选择框显示
|
||||
static void updateSelectionMarkers(QwtPlot* plot,
|
||||
QwtPlotMarker*& leftMarker,
|
||||
QwtPlotMarker*& rightMarker,
|
||||
double x1, double x2)
|
||||
{
|
||||
double x_left = qMin(x1, x2);
|
||||
double x_right = qMax(x1, x2);
|
||||
|
||||
// 左边界
|
||||
if (!leftMarker) {
|
||||
leftMarker = new QwtPlotMarker();
|
||||
leftMarker->setLineStyle(QwtPlotMarker::VLine);
|
||||
leftMarker->setLinePen(QPen(Qt::blue, 1, Qt::DashLine));
|
||||
leftMarker->attach(plot);
|
||||
}
|
||||
leftMarker->setValue(x_left, 0);
|
||||
|
||||
// 右边界
|
||||
if (!rightMarker) {
|
||||
rightMarker = new QwtPlotMarker();
|
||||
rightMarker->setLineStyle(QwtPlotMarker::VLine);
|
||||
rightMarker->setLinePen(QPen(Qt::blue, 1, Qt::DashLine));
|
||||
rightMarker->attach(plot);
|
||||
}
|
||||
rightMarker->setValue(x_right, 0);
|
||||
|
||||
plot->replot();
|
||||
}
|
||||
|
||||
// 清除选择框
|
||||
static void clearSelectionMarkers(QwtPlotMarker*& leftMarker,
|
||||
QwtPlotMarker*& rightMarker)
|
||||
{
|
||||
if (leftMarker) {
|
||||
leftMarker->detach();
|
||||
delete leftMarker;
|
||||
leftMarker = nullptr;
|
||||
}
|
||||
if (rightMarker) {
|
||||
rightMarker->detach();
|
||||
delete rightMarker;
|
||||
rightMarker = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
NuclideAnalysisView::NuclideAnalysisView(QWidget* parent)
|
||||
: MeasureAnalysisView { parent }
|
||||
{
|
||||
|
|
@ -48,8 +95,75 @@ void NuclideAnalysisView::SetAnalyzeDataFilename(const QMap<QString, QVariant>&
|
|||
const QString& data_filename = data_files_set.first().toString();
|
||||
if (QFileInfo(data_filename).exists()) {
|
||||
loadDataFromFile(data_name, data_filename);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
bool NuclideAnalysisView::eventFilter(QObject *obj, QEvent *event)
|
||||
{
|
||||
if (obj == _plot->canvas()) {
|
||||
QMouseEvent* mouseEvent = static_cast<QMouseEvent*>(event);
|
||||
|
||||
// 鼠标按下:记录起始位置
|
||||
if (event->type() == QEvent::MouseButtonPress) {
|
||||
if (mouseEvent->button() == Qt::LeftButton) {
|
||||
m_bSelecting = true;
|
||||
m_selectStartX = _plot->invTransform(QwtPlot::xBottom, mouseEvent->pos().x());
|
||||
m_selectStartPos = mouseEvent->pos();
|
||||
updateSelectionMarkers(_plot, m_selectLeftMarker, m_selectRightMarker,
|
||||
m_selectStartX, m_selectStartX);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// 鼠标移动:更新选择框
|
||||
if (event->type() == QEvent::MouseMove) {
|
||||
if (m_bSelecting) {
|
||||
double xValue = _plot->invTransform(QwtPlot::xBottom, mouseEvent->pos().x());
|
||||
updateSelectionMarkers(_plot, m_selectLeftMarker, m_selectRightMarker,
|
||||
m_selectStartX, xValue);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// 鼠标释放
|
||||
if (event->type() == QEvent::MouseButtonRelease) {
|
||||
if (mouseEvent->button() == Qt::LeftButton && m_bSelecting) {
|
||||
m_bSelecting = false;
|
||||
|
||||
double xEnd = _plot->invTransform(QwtPlot::xBottom, mouseEvent->pos().x());
|
||||
int dx = mouseEvent->pos().x() - m_selectStartPos.x();
|
||||
int dy = mouseEvent->pos().y() - m_selectStartPos.y();
|
||||
double moveDistance = sqrt(dx*dx + dy*dy);
|
||||
|
||||
// 清除选择框
|
||||
clearSelectionMarkers(m_selectLeftMarker, m_selectRightMarker);
|
||||
_plot->replot();
|
||||
if (moveDistance > 10)
|
||||
{
|
||||
double x1 = qMin(m_selectStartX, xEnd);
|
||||
double x2 = qMax(m_selectStartX, xEnd);
|
||||
|
||||
if (x2 - x1 > 1.0 && HasSpectrumData())
|
||||
{
|
||||
PeakFitResultNuclide result = FitPeakByRange(x1, x2, m_spectrumX, m_spectrumY);
|
||||
if (result.success)
|
||||
{
|
||||
// 追加到累积结果
|
||||
m_allFitResults.append(result);
|
||||
DrawPeaks(m_allFitResults);
|
||||
queryNuclidesForPeak(result);
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return MeasureAnalysisView::eventFilter(obj, event);
|
||||
}
|
||||
|
||||
void NuclideAnalysisView::setupPlot()
|
||||
|
|
@ -73,6 +187,9 @@ void NuclideAnalysisView::setupPlot()
|
|||
_plot->SetAxisDragScale(QwtPlot::xBottom, true);
|
||||
// 启用鼠标追踪
|
||||
_plot->canvas()->setMouseTracking(true);
|
||||
// 给canvas安装事件过滤器,捕获鼠标点击
|
||||
_plot->canvas()->installEventFilter(this);
|
||||
|
||||
}
|
||||
|
||||
void NuclideAnalysisView::setupMenu()
|
||||
|
|
@ -92,6 +209,18 @@ void NuclideAnalysisView::setupMenu()
|
|||
connect(action_plot_config, &QAction::triggered, this, &NuclideAnalysisView::onActionPlotConfigure);
|
||||
this->_menu->addSeparator();
|
||||
|
||||
QAction* action_plot_select = this->_menu->addAction(QStringLiteral(u"在谱线上选择"));
|
||||
action_plot_select->setObjectName("plot_select");
|
||||
connect(action_plot_select, &QAction::triggered, this, &NuclideAnalysisView::onActionPlotSelect);
|
||||
this->_menu->addSeparator();
|
||||
|
||||
QAction* action_plot_clear = this->_menu->addAction(QStringLiteral(u"清除"));
|
||||
action_plot_clear->setObjectName("plot_clear");
|
||||
connect(action_plot_clear, &QAction::triggered, this, &NuclideAnalysisView::onActionPlotClear);
|
||||
this->_menu->addSeparator();
|
||||
|
||||
setupNuclideDialog();
|
||||
|
||||
}
|
||||
|
||||
void NuclideAnalysisView::loadDataFromFile(const QString& data_name, const QString& filename)
|
||||
|
|
@ -130,9 +259,516 @@ void NuclideAnalysisView::loadDataFromFile(const QString& data_name, const QStri
|
|||
curve->setPen(QPen(Qt::gray, 2)); // 原始数据统一灰色
|
||||
curve->setSamples(x, y);
|
||||
_plot->AddCurve(curve,false);
|
||||
m_spectrumX = x;
|
||||
m_spectrumY = y;
|
||||
// QVector<PeakFitResultNuclide> results =FitThreePeaksFromSpectrum(x,y);
|
||||
// DrawPeaks(results,y);
|
||||
}
|
||||
|
||||
void NuclideAnalysisView::onActionPlotConfigure()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void NuclideAnalysisView::onActionPlotSelect()
|
||||
{
|
||||
m_bSelectHS = true;
|
||||
}
|
||||
|
||||
void NuclideAnalysisView::onActionPlotClear()
|
||||
{
|
||||
if (!_plot) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < m_peakMarkers.size(); ++i) {
|
||||
if (m_peakMarkers[i]) {
|
||||
m_peakMarkers[i]->detach();
|
||||
delete m_peakMarkers[i];
|
||||
}
|
||||
}
|
||||
m_peakMarkers.clear();
|
||||
|
||||
if (_vLineMarker) {
|
||||
_vLineMarker->detach();
|
||||
delete _vLineMarker;
|
||||
_vLineMarker = nullptr;
|
||||
}
|
||||
|
||||
if (m_selectLeftMarker) {
|
||||
m_selectLeftMarker->detach();
|
||||
delete m_selectLeftMarker;
|
||||
m_selectLeftMarker = nullptr;
|
||||
}
|
||||
if (m_selectRightMarker) {
|
||||
m_selectRightMarker->detach();
|
||||
delete m_selectRightMarker;
|
||||
m_selectRightMarker = nullptr;
|
||||
}
|
||||
|
||||
m_allFitResults.clear();
|
||||
|
||||
m_bSelecting = false;
|
||||
m_bSelectHS = false;
|
||||
|
||||
_plot->replot();
|
||||
}
|
||||
|
||||
void NuclideAnalysisView::addVerticalLine(double xValue, const QColor& color)
|
||||
{
|
||||
if(!m_bSelectHS)
|
||||
return;
|
||||
m_bSelectHS = false;
|
||||
if (!_vLineMarker) {
|
||||
_vLineMarker = new QwtPlotMarker();
|
||||
_vLineMarker->setLineStyle(QwtPlotMarker::VLine);
|
||||
_vLineMarker->setLinePen(QPen(color, 1, Qt::SolidLine));
|
||||
_vLineMarker->attach(_plot);
|
||||
} else {
|
||||
_vLineMarker->setLinePen(QPen(color, 1, Qt::SolidLine));
|
||||
}
|
||||
_vLineMarker->setValue(xValue, 0);
|
||||
_plot->replot();
|
||||
}
|
||||
|
||||
// 用光子峰模型拟合并计算单个峰的半高宽
|
||||
PeakFitResultNuclide NuclideAnalysisView::FitPhotonPeakCalcFwhm(const QVector<double>& x, const QVector<double>& y)
|
||||
{
|
||||
PeakFitResultNuclide result;
|
||||
result.success = false;
|
||||
result.center = 0.0;
|
||||
result.amplitude = 0.0;
|
||||
result.sigma = 0.0;
|
||||
result.fwhm = 0.0;
|
||||
result.area = 0.0;
|
||||
result.baseline = 0.0;
|
||||
result.sigmoidH = 0.0;
|
||||
result.sigmoidW = 0.0;
|
||||
|
||||
// 参数校验
|
||||
if (x.size() < 6 || y.size() < 6 || x.size() != y.size()) {
|
||||
return result;
|
||||
}
|
||||
|
||||
int n = x.size();
|
||||
arma::vec arma_x(n);
|
||||
arma::vec arma_y(n);
|
||||
for (int i = 0; i < n; ++i) {
|
||||
arma_x(i) = x[i];
|
||||
arma_y(i) = y[i];
|
||||
}
|
||||
|
||||
arma::vec p0 = EstimatePhotonPeakModelInitialParams(arma_x, arma_y);
|
||||
|
||||
arma::vec p_fit = NolinearLeastSquaresCurveFit::Lsqcurvefit(PhotonPeakModel, arma_x, arma_y, p0);
|
||||
|
||||
if (p_fit.n_elem >= 6) {
|
||||
double A = p_fit(0); // 高斯振幅
|
||||
double delt = p_fit(1); // 高斯宽度(sigma)
|
||||
double H = p_fit(2); // Sigmoid 高度
|
||||
double W = p_fit(3); // Sigmoid 宽度
|
||||
double C = p_fit(4); // 常数本底
|
||||
double P = p_fit(5); // 峰位
|
||||
|
||||
double fwhm = delt * 2.355;
|
||||
|
||||
double area = A * delt * 2.5066; // sqrt(2π) ≈ 2.5066
|
||||
|
||||
result.success = true;
|
||||
result.center = P;
|
||||
result.amplitude = A;
|
||||
result.sigma = delt;
|
||||
result.fwhm = fwhm;
|
||||
result.area = area;
|
||||
result.baseline = C;
|
||||
result.sigmoidH = H;
|
||||
result.sigmoidW = W;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// 根据x范围截取数据并拟合计FWHM
|
||||
PeakFitResultNuclide NuclideAnalysisView::FitPeakByRange(double x_start, double x_end,
|
||||
const QVector<double>& x, const QVector<double>& y)
|
||||
{
|
||||
PeakFitResultNuclide result;
|
||||
result.success = false;
|
||||
|
||||
if (x.size() != y.size() || x_start >= x_end) {
|
||||
return result;
|
||||
}
|
||||
|
||||
// 截取指定范围内的数据
|
||||
QVector<double> x_peak, y_peak;
|
||||
for (int i = 0; i < x.size(); ++i) {
|
||||
if (x[i] >= x_start && x[i] <= x_end) {
|
||||
x_peak.append(x[i]);
|
||||
y_peak.append(y[i]);
|
||||
}
|
||||
}
|
||||
|
||||
// 调用拟合函数
|
||||
result = FitPhotonPeakCalcFwhm(x_peak, y_peak);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// 从整张谱中自动寻找三个最强的峰并计算FWHM(SVD寻峰)
|
||||
QVector<PeakFitResultNuclide> NuclideAnalysisView::FitThreePeaksFromSpectrum(
|
||||
const QVector<double>& x,
|
||||
const QVector<double>& y,
|
||||
double height_threshold,
|
||||
int min_distance)
|
||||
{
|
||||
QVector<PeakFitResultNuclide> results;
|
||||
|
||||
if (x.size() < 10 || y.size() < 10 || x.size() != y.size()) {
|
||||
return results;
|
||||
}
|
||||
|
||||
// 将 QVector 转换为 arma::mat(FindPeaksBySvd 需要矩阵格式)
|
||||
int n = x.size();
|
||||
arma::mat spec_data(n, 2);
|
||||
for (int i = 0; i < n; ++i) {
|
||||
spec_data(i, 0) = x[i]; // 第一列:x(能量/道址)
|
||||
spec_data(i, 1) = y[i]; // 第二列:y(计数)
|
||||
}
|
||||
|
||||
FindPeaksBySvd peakFinder;
|
||||
std::vector<FindPeaksBySvd::PeakInfo> peaks;
|
||||
try {
|
||||
peaks = peakFinder.FindPeaks(spec_data, 40); // step_w = 7
|
||||
} catch (const std::string& e) {
|
||||
return results;
|
||||
}
|
||||
|
||||
if (peaks.empty()) {
|
||||
return results;
|
||||
}
|
||||
|
||||
// std::sort(peaks.begin(), peaks.end(),
|
||||
// [](const FindPeaksBySvd::PeakInfo& a, const FindPeaksBySvd::PeakInfo& b) {
|
||||
// return a.height > b.height;
|
||||
// });
|
||||
|
||||
// if (peaks.size() > 3) {
|
||||
// peaks.resize(3);
|
||||
// }
|
||||
|
||||
std::sort(peaks.begin(), peaks.end(),
|
||||
[](const FindPeaksBySvd::PeakInfo& a, const FindPeaksBySvd::PeakInfo& b) {
|
||||
return a.pos < b.pos;
|
||||
});
|
||||
|
||||
for (size_t i = 0; i < peaks.size(); ++i) {
|
||||
const FindPeaksBySvd::PeakInfo& peak = peaks[i];
|
||||
|
||||
double peak_pos = peak.pos;
|
||||
double window = peak.width;
|
||||
if (window < 10.0) window = 10.0;
|
||||
|
||||
double x_start = peak_pos - window;
|
||||
double x_end = peak_pos + window;
|
||||
|
||||
if (x_start < x.first()) x_start = x.first();
|
||||
if (x_end > x.last()) x_end = x.last();
|
||||
|
||||
QVector<double> x_peak, y_peak;
|
||||
for (int j = 0; j < x.size(); ++j) {
|
||||
if (x[j] >= x_start && x[j] <= x_end) {
|
||||
x_peak.append(x[j]);
|
||||
y_peak.append(y[j]);
|
||||
}
|
||||
}
|
||||
|
||||
PeakFitResultNuclide result = FitPhotonPeakCalcFwhm(x_peak, y_peak);
|
||||
results.append(result);
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
// 绘制检测到的峰(矩形表示峰位和峰宽)
|
||||
void NuclideAnalysisView::DrawPeaks(const QVector<PeakFitResultNuclide>& peaks)
|
||||
{
|
||||
if (!_plot || peaks.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 先清除之前的标记
|
||||
ClearPeakMarkers();
|
||||
|
||||
// 获取Y轴最大值,用于放置标签
|
||||
double y_max = 0;
|
||||
if (!m_spectrumY.isEmpty()) {
|
||||
y_max = *std::max_element(m_spectrumY.begin(), m_spectrumY.end());
|
||||
}
|
||||
|
||||
// 为每个峰创建矩形标记
|
||||
for (int i = 0; i < peaks.size(); ++i) {
|
||||
if (!peaks[i].success) {
|
||||
continue;
|
||||
}
|
||||
|
||||
double center = peaks[i].center;
|
||||
double fwhm = peaks[i].fwhm;
|
||||
double amplitude = peaks[i].amplitude;
|
||||
double baseline = peaks[i].baseline;
|
||||
|
||||
QColor color = Qt::red;
|
||||
|
||||
|
||||
// 半透明填充色
|
||||
QColor fillColor = color;
|
||||
fillColor.setAlpha(5); // 透明度 0-255
|
||||
|
||||
// 1. 矩形(表示峰位和峰宽)
|
||||
// 矩形范围:x = [center - fwhm/2, center + fwhm/2]
|
||||
// y = [baseline, baseline + amplitude]
|
||||
double x_left = center - fwhm / 2.0;
|
||||
double x_right = center + fwhm / 2.0;
|
||||
double y_bottom = 0.0;
|
||||
double y_top = y_max;
|
||||
|
||||
// 用 QwtPlotCurve 绘制闭合矩形
|
||||
QPolygonF rectPoints;
|
||||
rectPoints << QPointF(x_left, y_bottom);
|
||||
rectPoints << QPointF(x_right, y_bottom);
|
||||
rectPoints << QPointF(x_right, y_top);
|
||||
rectPoints << QPointF(x_left, y_top);
|
||||
rectPoints << QPointF(x_left, y_bottom); // 闭合
|
||||
|
||||
QwtPlotCurve* rectCurve = new QwtPlotCurve();
|
||||
rectCurve->setSamples(rectPoints);
|
||||
rectCurve->setPen(QPen(color, 2, Qt::SolidLine));
|
||||
rectCurve->setBrush(QBrush(fillColor));
|
||||
rectCurve->attach(_plot);
|
||||
m_peakMarkers.append(static_cast<QwtPlotItem*>(rectCurve));
|
||||
|
||||
// 2. 峰位竖线(中心线,虚线)
|
||||
QwtPlotMarker* centerLine = new QwtPlotMarker();
|
||||
centerLine->setLineStyle(QwtPlotMarker::VLine);
|
||||
centerLine->setLinePen(QPen(color, 1, Qt::DotLine));
|
||||
centerLine->setValue(center, 0);
|
||||
centerLine->attach(_plot);
|
||||
m_peakMarkers.append(centerLine);
|
||||
|
||||
// 3. 文本标签
|
||||
QString labelText = QString("位置: %1 keV\nFWHM: %2 keV\n峰面积: %3")
|
||||
.arg(center, 0, 'f', 1)
|
||||
.arg(fwhm, 0, 'f', 2)
|
||||
.arg(peaks[i].area, 0, 'f', 0);
|
||||
|
||||
QwtPlotMarker* textLabel = new QwtPlotMarker();
|
||||
textLabel->setLabel(QwtText(labelText));
|
||||
textLabel->setLabelAlignment(Qt::AlignLeft | Qt::AlignTop);
|
||||
textLabel->setLabelOrientation(Qt::Horizontal);
|
||||
|
||||
double label_y = y_top + y_max * 0.02;
|
||||
if (label_y > y_max * 0.9) {
|
||||
label_y = y_top - y_max * 0.15;
|
||||
textLabel->setLabelAlignment(Qt::AlignLeft | Qt::AlignTop);
|
||||
}
|
||||
|
||||
textLabel->setValue(x_left, label_y);
|
||||
textLabel->attach(_plot);
|
||||
m_peakMarkers.append(textLabel);
|
||||
}
|
||||
|
||||
_plot->replot();
|
||||
}
|
||||
|
||||
// 清除所有峰标记
|
||||
void NuclideAnalysisView::ClearPeakMarkers()
|
||||
{
|
||||
if (!_plot) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 分离并删除所有峰标记
|
||||
for (int i = 0; i < m_peakMarkers.size(); ++i) {
|
||||
if (m_peakMarkers[i]) {
|
||||
m_peakMarkers[i]->detach();
|
||||
delete m_peakMarkers[i];
|
||||
}
|
||||
}
|
||||
m_peakMarkers.clear();
|
||||
|
||||
_plot->replot();
|
||||
}
|
||||
|
||||
bool NuclideAnalysisView::HasSpectrumData() const
|
||||
{
|
||||
return !m_spectrumX.isEmpty();
|
||||
}
|
||||
|
||||
std::vector<FindPeaksBySvd::PeakInfo> NuclideAnalysisView::GetSvdPeaks(const QVector<double> &x, const QVector<double> &y, int step_w)
|
||||
{
|
||||
std::vector<FindPeaksBySvd::PeakInfo> peaks;
|
||||
|
||||
if (x.size() < 10 || y.size() < 10 || x.size() != y.size()) {
|
||||
return peaks;
|
||||
}
|
||||
|
||||
int n = x.size();
|
||||
arma::mat spec_data(n, 2);
|
||||
for (int i = 0; i < n; ++i) {
|
||||
spec_data(i, 0) = x[i];
|
||||
spec_data(i, 1) = y[i];
|
||||
}
|
||||
|
||||
FindPeaksBySvd peakFinder;
|
||||
try {
|
||||
peaks = peakFinder.FindPeaks(spec_data, step_w);
|
||||
} catch (const std::string& e) {
|
||||
|
||||
}
|
||||
|
||||
return peaks;
|
||||
}
|
||||
|
||||
void NuclideAnalysisView::setupNuclideDialog()
|
||||
{
|
||||
m_nuclideDialog = new QDialog(this);
|
||||
m_nuclideDialog->setWindowTitle(QStringLiteral(u"匹配核素射线"));
|
||||
m_nuclideDialog->resize(520, 400);
|
||||
|
||||
QVBoxLayout* layout = new QVBoxLayout(m_nuclideDialog);
|
||||
layout->setContentsMargins(6, 6, 6, 6);
|
||||
|
||||
m_nuclideTable = new QTableWidget(m_nuclideDialog);
|
||||
QStringList headers = { "核素", "射线类型", "能量(keV)","操作" };
|
||||
m_nuclideTable->setColumnCount(headers.size());
|
||||
m_nuclideTable->setHorizontalHeaderLabels(headers);
|
||||
// m_nuclideTable->horizontalHeader()->setStretchLastSection(true);
|
||||
m_nuclideTable->setSelectionBehavior(QAbstractItemView::SelectRows);
|
||||
m_nuclideTable->setEditTriggers(QAbstractItemView::NoEditTriggers);
|
||||
m_nuclideTable->setAlternatingRowColors(true);
|
||||
layout->addWidget(m_nuclideTable);
|
||||
}
|
||||
|
||||
void NuclideAnalysisView::queryNuclidesForPeak(const PeakFitResultNuclide& peak)
|
||||
{
|
||||
if (!peak.success) return;
|
||||
|
||||
if (!openNuclideDatabase()) {
|
||||
m_nuclideTable->setRowCount(1);
|
||||
m_nuclideTable->setItem(0, 0, new QTableWidgetItem(QStringLiteral(u"数据库未打开")));
|
||||
m_nuclideDialog->show();
|
||||
return;
|
||||
}
|
||||
|
||||
double eMin = peak.center - peak.fwhm;
|
||||
double eMax = peak.center + peak.fwhm;
|
||||
double center = peak.center;
|
||||
|
||||
auto& db = SqliteManager::instance();
|
||||
|
||||
QString sql = "SELECT ni.NUCLIDE_NAME, nri.RAY_TYPE, nri.RAY_MEV, "
|
||||
"nri.RAY_BRANCH_RATIO, nri.RAY_MEV_UNCERTAINTY "
|
||||
"FROM nuclideRayInfo nri "
|
||||
"INNER JOIN nuclideLib ni ON nri.NUCLIDE_ID = ni.ID;";
|
||||
|
||||
auto allRows = db.selectRows(sql);
|
||||
|
||||
struct MatchItem {
|
||||
QString nuclideName;
|
||||
QString rayType;
|
||||
QString energyDisplay;
|
||||
double sortValue;
|
||||
};
|
||||
QVector<MatchItem> matches;
|
||||
|
||||
for (const auto& row : allRows) {
|
||||
QString mevStr = row["RAY_MEV"].toString().trimmed();
|
||||
if (mevStr.isEmpty()) continue;
|
||||
|
||||
QStringList energyList = mevStr.split(',');
|
||||
for (const QString& eStr : energyList) {
|
||||
QString trimmed = eStr.trimmed();
|
||||
if (trimmed.isEmpty()) continue;
|
||||
|
||||
bool matched = false;
|
||||
QString displayStr;
|
||||
double sortVal = 0.0;
|
||||
|
||||
if (trimmed.contains('-')) {
|
||||
// 范围形式:如 "11.386-12.032"
|
||||
QStringList rangeParts = trimmed.split('-');
|
||||
if (rangeParts.size() == 2) {
|
||||
bool ok1 = false, ok2 = false;
|
||||
double low = rangeParts[0].trimmed().toDouble(&ok1);
|
||||
double high = rangeParts[1].trimmed().toDouble(&ok2);
|
||||
if (ok1 && ok2 && low <= high) {
|
||||
// 范围与搜索窗口有重叠就算匹配
|
||||
if (high >= eMin && low <= eMax) {
|
||||
matched = true;
|
||||
displayStr = trimmed;
|
||||
// 取范围内最接近峰位的值用于排序
|
||||
if (center < low) sortVal = low;
|
||||
else if (center > high) sortVal = high;
|
||||
else sortVal = center;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 单值形式
|
||||
bool ok = false;
|
||||
double energy = trimmed.toDouble(&ok);
|
||||
if (ok && energy >= eMin && energy <= eMax) {
|
||||
matched = true;
|
||||
displayStr = trimmed;
|
||||
sortVal = energy;
|
||||
}
|
||||
}
|
||||
|
||||
if (matched) {
|
||||
MatchItem item;
|
||||
item.nuclideName = row["NUCLIDE_NAME"].toString();
|
||||
item.rayType = row["RAY_TYPE"].toString();
|
||||
item.energyDisplay = displayStr;
|
||||
item.sortValue = sortVal;
|
||||
matches.append(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 按与峰位的接近程度排序
|
||||
std::sort(matches.begin(), matches.end(),
|
||||
[center](const MatchItem& a, const MatchItem& b) {
|
||||
return qAbs(a.sortValue - center) < qAbs(b.sortValue - center);
|
||||
});
|
||||
|
||||
m_nuclideTable->setRowCount(matches.size());
|
||||
for (int i = 0; i < matches.size(); ++i) {
|
||||
const auto& m = matches[i];
|
||||
m_nuclideTable->setItem(i, 0, new QTableWidgetItem(m.nuclideName));
|
||||
m_nuclideTable->setItem(i, 1, new QTableWidgetItem(m.rayType));
|
||||
m_nuclideTable->setItem(i, 2, new QTableWidgetItem(m.energyDisplay));
|
||||
}
|
||||
|
||||
if (matches.isEmpty()) {
|
||||
m_nuclideTable->setRowCount(1);
|
||||
m_nuclideTable->setItem(0, 0, new QTableWidgetItem(QStringLiteral(u"未找到匹配核素")));
|
||||
}
|
||||
|
||||
m_nuclideDialog->show();
|
||||
m_nuclideDialog->raise();
|
||||
m_nuclideDialog->activateWindow();
|
||||
}
|
||||
|
||||
bool NuclideAnalysisView::openNuclideDatabase()
|
||||
{
|
||||
auto& db = SqliteManager::instance();
|
||||
if (db.isOpen()) {
|
||||
return true;
|
||||
}
|
||||
QString dbPath = QCoreApplication::applicationDirPath() + "/nuclideLib.db";
|
||||
|
||||
if (dbPath.isEmpty()) {
|
||||
qWarning() << "核素数据库路径未设置";
|
||||
return false;
|
||||
}
|
||||
return db.openDatabase(dbPath, "nuclide_conn");
|
||||
}
|
||||
|
|
@ -4,24 +4,86 @@
|
|||
#include <QWidget>
|
||||
#include <MeasureAnalysisView.h>
|
||||
#include <QwtPlotCurve>
|
||||
#include <QwtPlotMarker>
|
||||
#include <QMenu>
|
||||
#include <QwtPlotShapeItem>
|
||||
#include <QPainterPath>
|
||||
#include <QwtSymbol>
|
||||
#include <QDialog>
|
||||
#include <QTableWidget>
|
||||
#include <QVBoxLayout>
|
||||
#include "sqlitemanager.h"
|
||||
|
||||
|
||||
#include "DataCalcProcess/AdaptiveSimpsonIntegrate.h"
|
||||
#include "DataCalcProcess/FindPeaksBySvd.h"
|
||||
#include "DataCalcProcess/MathModelDefine.h"
|
||||
#include "DataCalcProcess/NolinearLeastSquaresCurveFit.h"
|
||||
#include "DataCalcProcess/Poly2FindPeaks.h"
|
||||
|
||||
class CustomQwtPlot;
|
||||
|
||||
|
||||
struct PeakFitResultNuclide {
|
||||
bool success; // 拟合是否成功
|
||||
double center; // 峰中心能量 (keV)
|
||||
double amplitude; // 高斯振幅
|
||||
double sigma; // 高斯宽度
|
||||
double fwhm; // 半高宽 = sigma * 2.355
|
||||
double area; // 峰面积(积分)
|
||||
double baseline; // 常数项 C
|
||||
double sigmoidH; // Sigmoid 项高度 H
|
||||
double sigmoidW; // Sigmoid 项宽度 W
|
||||
};
|
||||
|
||||
class NuclideAnalysisView : public MeasureAnalysisView
|
||||
{
|
||||
|
||||
Q_OBJECT
|
||||
public:
|
||||
NuclideAnalysisView(QWidget *parent = nullptr);
|
||||
virtual ~NuclideAnalysisView();
|
||||
virtual void InitViewWorkspace(const QString& project_name) override final;
|
||||
virtual void SetAnalyzeDataFilename(const QMap<QString, QVariant>& data_files_set);
|
||||
void addVerticalLine(double xValue, const QColor& color = Qt::red);
|
||||
// 用光子峰模型拟合并计算单个峰的半高宽
|
||||
PeakFitResultNuclide FitPhotonPeakCalcFwhm(const QVector<double>& x, const QVector<double>& y);
|
||||
|
||||
// 根据x范围截取数据并拟合计FWHM
|
||||
PeakFitResultNuclide FitPeakByRange(double x_start, double x_end,
|
||||
const QVector<double>& x, const QVector<double>& y);
|
||||
|
||||
// 从整张谱中自动寻找三个最强的峰并计算FWHM
|
||||
QVector<PeakFitResultNuclide> FitThreePeaksFromSpectrum(
|
||||
const QVector<double>& x,
|
||||
const QVector<double>& y,
|
||||
double height_threshold = 50.0,
|
||||
int min_distance = 20);
|
||||
void DrawPeaks(const QVector<PeakFitResultNuclide>& peaks);
|
||||
void ClearPeakMarkers();
|
||||
|
||||
bool HasSpectrumData() const;
|
||||
|
||||
std::vector<FindPeaksBySvd::PeakInfo> GetSvdPeaks(const QVector<double>& x,const QVector<double>& y,int step_w);
|
||||
protected:
|
||||
virtual bool eventFilter(QObject* obj, QEvent* event) override;
|
||||
private:
|
||||
void setupPlot();
|
||||
void setupMenu();
|
||||
void loadDataFromFile(const QString &data_name, const QString& filename);
|
||||
|
||||
void setupNuclideDialog();
|
||||
void queryNuclidesForPeak(const PeakFitResultNuclide& peak);
|
||||
bool openNuclideDatabase();
|
||||
|
||||
private slots:
|
||||
void onActionPlotConfigure();
|
||||
|
||||
//在谱线上选择
|
||||
void onActionPlotSelect();
|
||||
|
||||
void onActionPlotClear();
|
||||
|
||||
private:
|
||||
CustomQwtPlot* _plot = nullptr;
|
||||
QString _workspace;
|
||||
|
|
@ -29,5 +91,23 @@ private:
|
|||
|
||||
int _x_max = 0;
|
||||
int _y_max = 0;
|
||||
|
||||
bool m_bSelectHS = false;
|
||||
QwtPlotMarker* _vLineMarker = nullptr; // 竖线标记
|
||||
QList<QwtPlotItem*> m_peakMarkers; // 峰标记列表
|
||||
|
||||
QVector<double> m_spectrumX; // 谱数据X(能量/道址)
|
||||
QVector<double> m_spectrumY; // 谱
|
||||
|
||||
QVector<PeakFitResultNuclide> m_allFitResults;
|
||||
|
||||
bool m_bSelecting = false; // 是否正在拖动选择
|
||||
double m_selectStartX = 0.0; // 选择起始X坐标
|
||||
QPoint m_selectStartPos; // 选择起始像素位置(判断点击还是拖动)
|
||||
QwtPlotMarker* m_selectLeftMarker = nullptr; // 选择左边界线
|
||||
QwtPlotMarker* m_selectRightMarker = nullptr; // 选择右边界线
|
||||
|
||||
QDialog* m_nuclideDialog = nullptr;
|
||||
QTableWidget* m_nuclideTable = nullptr;
|
||||
};
|
||||
#endif // NUCLIDEANALYSISVIEW_H
|
||||
|
|
@ -30,7 +30,7 @@ void NuclideRayDialog::initUI()
|
|||
|
||||
// 射线类型下拉框(常用核素射线类型)
|
||||
m_cmbRayType = new QComboBox(this);
|
||||
m_cmbRayType->addItems({QStringLiteral(u"γ"), QStringLiteral(u"β⁻"), QStringLiteral(u"β⁺"), QStringLiteral(u"α"), QStringLiteral(u"X射线"), QStringLiteral(u"中子"), QStringLiteral(u"电子俘获")});
|
||||
m_cmbRayType->addItems({QStringLiteral(u"γ"), QStringLiteral(u"β⁻"), QStringLiteral(u"β⁺"), QStringLiteral(u"α"), QStringLiteral(u"X射线")});
|
||||
formLayout->addRow(QStringLiteral(u"射线类型:"), m_cmbRayType);
|
||||
|
||||
m_leRayMev = new QLineEdit(this);
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user