优化测量分析历史处理维护
This commit is contained in:
parent
7016f584b9
commit
f5c61d6940
146
doc/需求相关资料/软件开发交互分析功能需求更新版.docx
Normal file
146
doc/需求相关资料/软件开发交互分析功能需求更新版.docx
Normal file
|
|
@ -0,0 +1,146 @@
|
|||
1.交互分析功能需求:
|
||||
粒子数据文件索引及打开功能;
|
||||
可对能谱进行重新刻度,或刻度数据导入;
|
||||
常用功能:可进行谱放大缩小,自适应屏幕调整能谱大小,常规或对数能谱显示,显示能谱信息、刻度参数等常规信息;
|
||||
粒子时间差统计,给出粒子时间差的统计结果,能够根据指定的时间差对符合事件进行筛选(功能5);
|
||||
能谱显示:可显示能量计数谱、二维符合谱;在符合时间未给出的情况下不显示符合能谱,只显示能量计数谱;
|
||||
能量计数谱可显示原始能谱、符合谱、反符合能谱(可选择探测器,如在信息中没有探测器的信息则此选项为不可选);
|
||||
能量技术谱分析可根据选定能峰的信息给出可能的核素信息,通过选择核素可同步显示该核素其他能峰对应能量区域的能谱信息,如果有能峰存在给出该区域的能峰拟合信息;
|
||||
时间切片功能,按指定的时间间隔统计特征能峰(或指定区域,即ROI)的计数率,给出时间段内计数率随测量时间的变化;
|
||||
多条件粒子筛选、显示及统计功能:通过时间差、能量、符合次数筛选不同类型的符合事件及其粒子信息,指定筛选给出粒子的类型(电子、光子),统计事件次数,实现可视化;
|
||||
数据拟合:通过默认或输入的公式对指定(ROI区域)的能峰进行拟合,给出峰位、FWHM、峰面积、本底计数等信息,可在能谱上显示拟合能谱曲线及本底曲线(只在能量计数谱上显示);
|
||||
符合能谱数据统计:统计ROI区域的计数,针对二维符合能谱ROI区域可设为圆形或任意角度的长方形,统计区域中不同能量的计数(及其粒子信息)。
|
||||
二维符合谱的显示:模式1,横轴为初级入射粒子的能量keV,纵轴为次级入射粒子的能量keV之和;模式2:横轴为入射γ、X光子的能量keV,纵轴为电子的能量keV;
|
||||
2.目前使用的峰拟合函数:
|
||||
光子
|
||||
f_peak=f_peak=@(A,delt,H,W,C,P,x)A*exp(-(x-P).^2 ./(2*delt^2))+H./(1+exp((x-P)./W))+C;
|
||||
电子
|
||||
f_epeak=@(A,Bg,a,m.delt,a)Bg+A*(1+((x)./a).^2).^(-m).*exp(-delt.*atan((x)./a))
|
||||
|
||||
Matlab峰拟合脚本(γ能谱):
|
||||
clear;
|
||||
clc;
|
||||
close all;
|
||||
%% 参数设置
|
||||
fname = 'SampleA.TKA';
|
||||
count = load(fname); % * .TKA 是纯文本列向量
|
||||
% load('E:\UnCSSSP\Matspace\electron\si_sp.mat')
|
||||
% count=data1(:,2);
|
||||
% 能量刻度: En = a * ch + b
|
||||
par_encal = [3.509e-1, -1.965e-2]; % [a, b] 单位: keV/channel
|
||||
a = par_encal(1);
|
||||
b = par_encal(2);
|
||||
x_ch=1:length(count);
|
||||
x_en=a*x_ch+b;
|
||||
% 峰形刻度: FWHM(keV) = p_form(1)*sqrt(En) + p_form(2)
|
||||
p_form = [3.968e-2, 3.661e-1]; % [k, c]
|
||||
% 拟合峰能量(keV)输入峰能量
|
||||
E_peak =input('指定全能峰能量keV:');
|
||||
% 计算 FWHM 和 高斯标准差
|
||||
FWHM = p_form(1) * sqrt(E_peak) + p_form(2); % keV
|
||||
delt = FWHM / 2.355; % 高斯 σ(keV)
|
||||
% 转换为道址(用于提取数据)
|
||||
FWHM_ch = FWHM / a; % 道址宽度
|
||||
delt_ch = delt / a; % σ 道址
|
||||
p_chn = round((E_peak - b) / a); % 峰位道址
|
||||
% 提取数据范围:+-1.5 FWHM,共 p_w 个点
|
||||
p_w = 9;
|
||||
channels = (p_chn - round(6*FWHM_ch)) : (p_chn + round(5 * FWHM_ch));
|
||||
% if length(channels) < p_w
|
||||
% error('提取道址范围过小,请检查 FWHM 或 p_w');
|
||||
% end
|
||||
% channels = channels(1:p_w); % 截取 p_w 个点
|
||||
% 提取能量和计数
|
||||
en_p = a * channels + b; % 实际能量(keV)
|
||||
p_count = count(channels)'; % 注意:转置为列向量
|
||||
%% 定义拟合函数(所有参数显式列出)
|
||||
% f(x) = Gaussian + Sigmoid tail + constant
|
||||
f_peak = @(A, delt, H, W, C, P, x)A*exp(-(x-P).^2 ./(2*delt^2))+H./(1+exp((x-P)./W))+C;
|
||||
%本底函数
|
||||
f_BG=@(A, delt, H, W, C,P , x)H./(1+exp((x-P)./W))+C; %本底函数
|
||||
%峰函数
|
||||
f_pk_s=@(A, delt, H, W,P , x)A.*exp(-(x-P).^2/(2.*delt.^2)); %计算得出的峰函数
|
||||
% 初始猜测
|
||||
startA = max(p_count) - min(p_count); % 峰高
|
||||
startC = min(p_count); % 基线
|
||||
startH = 0.1 * startA; % 拖尾幅度(经验)
|
||||
startW = delt * 2; % 拖尾尺度(经验)
|
||||
startP = E_peak; % 峰位
|
||||
opts = fitoptions('Method', 'NonlinearLeastSquares');
|
||||
opts.StartPoint = [startA, delt, startH, startW, startC, startP]; % A, delt, H, W, C
|
||||
opts.Robust = 'LAR'; % 抗异常值
|
||||
opts.Display = 'off';
|
||||
opts.TolFun = 1e-10;
|
||||
opts.TolX = 1e-10;
|
||||
% 定义 fittype
|
||||
ft = fittype(f_peak,'independent','x','dependent', 'y','coefficients', {'A', 'delt', 'H', 'W', 'C','P'});
|
||||
%% 拟合
|
||||
[info_pfit, gof, output] = fit(en_p', p_count', ft, opts);
|
||||
% 提取结果
|
||||
A = info_pfit.A;
|
||||
delt_fitted = info_pfit.delt; % 可更新
|
||||
H = info_pfit.H;
|
||||
W = info_pfit.W;
|
||||
C = info_pfit.C;
|
||||
P = info_pfit.P;
|
||||
%% 输出结果
|
||||
fprintf(' 全能峰拟合完成(%g keV):\n', E_peak);
|
||||
fprintf(' A = %.2f\n', A);
|
||||
fprintf(' H = %.2f\n', H);
|
||||
fprintf(' W = %.2f keV\n', W);
|
||||
fprintf(' P = %.2f keV\n',P);
|
||||
fprintf(' C = %.2f (基线)\n', C);
|
||||
fprintf(' R^2 = %.6f\n', gof.rsquare);
|
||||
fprintf(' RMSE = %.4f\n', gof.rmse);
|
||||
%% 计算峰计数
|
||||
peak_only = @(x) f_pk_s(A, delt_fitted, H, W, P, x);
|
||||
background = @(x) f_BG(A, delt_fitted, H, W, C, P, x);
|
||||
full_peak = @(x) f_peak(A, delt_fitted, H, W, C, P, x);
|
||||
% 积分范围:+-3σ(能量空间)
|
||||
x_low = E_peak - 3 * delt_fitted;
|
||||
x_high = E_peak + 3 * delt_fitted;
|
||||
% 积分求解峰面积
|
||||
Area_total = integral(full_peak, x_low, x_high, 'ArrayValued', true)/a;
|
||||
BG_area = integral(background, x_low, x_high, 'ArrayValued', true)/a;
|
||||
Peak_area = integral(peak_only, x_low, x_high, 'ArrayValued', true)/a;
|
||||
fprintf('\n %.2f keV全能峰面积计算:\n',E_peak);
|
||||
fprintf(' 全峰面积 = %.2f \n', Area_total);
|
||||
fprintf(' 本底面积 = %.2f \n', BG_area);
|
||||
fprintf(' 净峰面积 = %.2f \n', Peak_area);
|
||||
%% 绘图
|
||||
monitorPositions = get(0, 'MonitorPositions');
|
||||
[~, numMonitors] = size(monitorPositions);
|
||||
if numMonitors < 2
|
||||
error('仅检测到一个显示器,无法在副屏显示。');
|
||||
end
|
||||
% 副屏位置
|
||||
secondaryMonitor = monitorPositions(2, :); % [left, bottom, width, height]
|
||||
fig_pos=secondaryMonitor;
|
||||
fig_pos(1) = fig_pos(1) + 8 ;
|
||||
fig_pos(3) = fig_pos(3) - 8 ;
|
||||
fig_pos(4) = fig_pos(4) - 85;
|
||||
figure;%figure('WindowState','fullscreen')
|
||||
set(gcf, 'Units', 'pixels', 'Position', fig_pos);
|
||||
subplot(1,3,[1,2]);
|
||||
plot(x_en,count);
|
||||
xlabel('能量 (keV)');
|
||||
ylabel('计数');
|
||||
grid on;
|
||||
subplot(1,3,3);
|
||||
plot(en_p, p_count, 'bo', 'DisplayName', '数据点');
|
||||
hold on;
|
||||
x_fine = linspace(min(en_p), max(en_p), 100);
|
||||
plot(x_fine, f_peak(A, delt_fitted, H, W, C, P, x_fine), 'r-', 'LineWidth', 2, 'DisplayName', '拟合曲线');
|
||||
plot(x_fine, f_pk_s(A, delt_fitted, H, W, P, x_fine), 'g--', 'LineWidth', 2, 'DisplayName', '单峰曲线')
|
||||
plot(x_fine, f_BG(A, delt_fitted, H, W, C, P, x_fine), 'c--', 'LineWidth', 1, 'DisplayName', '本底曲线')
|
||||
plot(x_fine, f_pk_s(A, delt_fitted, H, W, P, x_fine)+f_BG(A, delt_fitted, H, W, C, P, x_fine), 'b--', 'LineWidth', 1, 'DisplayName', '单峰曲线')
|
||||
xlabel('能量 (keV)');
|
||||
ylabel('计数');
|
||||
title(sprintf('%.1f keV 峰拟合', P));
|
||||
legend('Location', 'best');
|
||||
grid on;
|
||||
hold off;
|
||||
%% 添加文本框显示结果
|
||||
annotation('textbox', [0.898, 0.81, 0.1, 0.1], 'String',{sprintf('修正后峰位:%.3f', P),sprintf('修正后方差:%.3f keV', delt_fitted), ...
|
||||
sprintf('拟合优度:%.5f',gof.rsquare),sprintf('净峰面积:%.0f', Peak_area)}, 'BackgroundColor', 'white', 'EdgeColor', 'k', 'FontSize', 10);
|
||||
|
||||
|
|
@ -35,7 +35,7 @@ MeasureAnalysisHistoryForm::~MeasureAnalysisHistoryForm()
|
|||
delete ui;
|
||||
}
|
||||
|
||||
void MeasureAnalysisHistoryForm::loadHistoryInfo()
|
||||
void MeasureAnalysisHistoryForm::loadHistoryInfo(const QString &history_name)
|
||||
{
|
||||
auto row_count = ui->tablew_history->rowCount();
|
||||
for (int row = row_count - 1; row >= 0; --row) {
|
||||
|
|
@ -57,9 +57,11 @@ void MeasureAnalysisHistoryForm::loadHistoryInfo()
|
|||
const QString& project_filename = project_dir_path.filePath(msproject_files.first());
|
||||
MeasureAnalysisProjectModel project_model;
|
||||
project_model.LoadProjectModel(project_filename);
|
||||
const QString& project_name = project_model.GetProjectName();
|
||||
|
||||
int row = ui->tablew_history->rowCount();
|
||||
ui->tablew_history->insertRow(row);
|
||||
ui->tablew_history->setItem(row, 0, new QTableWidgetItem(project_model.GetProjectName()));
|
||||
ui->tablew_history->setItem(row, 0, new QTableWidgetItem(project_name));
|
||||
ui->tablew_history->item(row, 0)->setCheckState(Qt::Unchecked);
|
||||
ui->tablew_history->item(row, 0)->setData(Qt::UserRole, project_filename);
|
||||
const QString& is_measure_complete = project_model.GetIsMeasureComplete() ? QStringLiteral(u"是") : QStringLiteral(u"否");
|
||||
|
|
@ -73,6 +75,15 @@ void MeasureAnalysisHistoryForm::loadHistoryInfo()
|
|||
ui->tablew_history->setItem(row, 5, new QTableWidgetItem());
|
||||
ui->tablew_history->setItem(row, 6, new QTableWidgetItem());
|
||||
ui->tablew_history->setItem(row, 7, new QTableWidgetItem(project_model.GetDescriptionInfo()));
|
||||
|
||||
if (history_name == project_name) {
|
||||
for (int col = 0; col < ui->tablew_history->columnCount(); ++col) {
|
||||
QTableWidgetItem* other_item = ui->tablew_history->item(row, col);
|
||||
if (other_item) {
|
||||
other_item->setFlags(other_item->flags() & ~(Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsUserCheckable));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -126,12 +137,13 @@ void MeasureAnalysisHistoryForm::onReverseSelect()
|
|||
|
||||
void MeasureAnalysisHistoryForm::onRemoveSelectedProject()
|
||||
{
|
||||
QMap<int, QString> selected_project_list;
|
||||
QMap<int, QPair<QString, QString> > selected_project_list;
|
||||
auto row_count = ui->tablew_history->rowCount();
|
||||
for (int row = row_count - 1; row >= 0; --row) {
|
||||
if (Qt::Checked == ui->tablew_history->item(row, 0)->checkState()) {
|
||||
const QString& project_name = ui->tablew_history->item(row, 0)->text();
|
||||
const QString& project_filename = ui->tablew_history->item(row, 0)->data(Qt::UserRole).toString();
|
||||
selected_project_list[row] = project_filename;
|
||||
selected_project_list[row] = qMakePair(project_name, project_filename);
|
||||
}
|
||||
}
|
||||
if (selected_project_list.isEmpty()) {
|
||||
|
|
@ -142,12 +154,15 @@ void MeasureAnalysisHistoryForm::onRemoveSelectedProject()
|
|||
return;
|
||||
}
|
||||
for (int row : selected_project_list.keys()) {
|
||||
QFileInfo project_file_info(selected_project_list.value(row));
|
||||
const QString& project_name = selected_project_list.value(row).first;
|
||||
ProjectList::Instance()->RmProjectModel(project_name);
|
||||
const QString& project_filename = selected_project_list.value(row).second;
|
||||
QFileInfo project_file_info(project_filename);
|
||||
const QString& project_dir_path = project_file_info.absoluteDir().absolutePath();
|
||||
QDir(project_dir_path).removeRecursively();
|
||||
QDir(project_dir_path).rmdir(project_dir_path);
|
||||
ui->tablew_history->removeRow(row);
|
||||
LOG_INFO(QStringLiteral(u"测量分析项目\"%1\"已经删除.").arg(project_file_info.baseName()));
|
||||
LOG_INFO(QStringLiteral(u"测量分析项目\"%1\"已经删除.").arg(project_name));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -225,6 +240,5 @@ void MeasureAnalysisHistoryForm::onUpdateCloseHistoryProject(const QString &proj
|
|||
|
||||
void MeasureAnalysisHistoryForm::onUpdateNewHistoryProject(const QString &project_name)
|
||||
{
|
||||
Q_UNUSED(project_name);
|
||||
loadHistoryInfo();
|
||||
loadHistoryInfo(project_name);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ public:
|
|||
~MeasureAnalysisHistoryForm();
|
||||
|
||||
private:
|
||||
void loadHistoryInfo();
|
||||
void loadHistoryInfo(const QString& history_name = QString());
|
||||
private slots:
|
||||
void onQueryHistory();
|
||||
void onAllSelect();
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
#include <QResource>
|
||||
#include <QStyleFactory>
|
||||
#include "QsLogManage.h"
|
||||
#include <QThreadPool>
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
|
|
@ -61,5 +62,7 @@ int main(int argc, char *argv[])
|
|||
|
||||
MainWindow w;
|
||||
w.showMaximized();
|
||||
return app.exec();
|
||||
int ret = app.exec();
|
||||
QThreadPool::globalInstance()->waitForDone();
|
||||
return ret;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user