libpappsomspp
Library for mass spectrometry
Loading...
Searching...
No Matches
signaltonoiseestimatormedian.cpp
Go to the documentation of this file.
1// Copyright 2026 Filippo Rusconi
2// Inspired by work by Lars Nilse in OpenMS
3
4
5/////////////////////// stdlib includes
6#include <limits>
7
8
9/////////////////////// Qt includes
10#include <QList>
11#include <QDebug>
12
13
14/////////////////////// pappsomspp includes
15
16
17/////////////////////// Local includes
20
21namespace pappso
22{
23SignalToNoiseEstimatorMedian::SignalToNoiseEstimatorMedian()
24{
25}
26
27SignalToNoiseEstimatorMedian::~SignalToNoiseEstimatorMedian()
28{
29}
30
31void
32SignalToNoiseEstimatorMedian::pick(const Trace &trace,
33 Trace &picked_peaks,
34 QList<PeakRange> &peak_ranges)
35{
36 picked_peaks.clear();
37
38 qsizetype trace_size = trace.size();
39
40 if(trace_size < 5)
41 return;
42
43 for(qsizetype iter = 2; iter < trace_size - 2; ++iter)
44 {
45 double center_data_point_mz = trace.at(iter).x,
46 center_data_point_int = trace.at(iter).y;
47 double left_data_point_mz = trace.at(iter - 1).x,
48 left_data_point_int = trace.at(iter - 1).y;
49 double right_data_point_mz = trace.at(iter + 1).x,
50 right_data_point_int = trace.at(iter + 1).y;
51
52 // do not interpolate when the left or right support is a zero-data-point
53 if(std::fabs(left_data_point_int) <
54 std::numeric_limits<double>::epsilon())
55 {
56 continue;
57 }
58 if(std::fabs(right_data_point_int) <
59 std::numeric_limits<double>::epsilon())
60 {
61 continue;
62 }
63
64 double act_snt = 0.0, act_snt_l1 = 0.0, act_snt_r1 = 0.0;
65
66 if(m_signalToNoise > 0.0)
67 {
68 // FIXME
69 }
70
71 // look for peak cores meeting MZ and intensity/SNT criteria
72 if((center_data_point_int > left_data_point_int) &&
73 (center_data_point_int > right_data_point_int) &&
74 (act_snt >= m_signalToNoise) && (act_snt_l1 >= m_signalToNoise) &&
75 (act_snt_r1 >= m_signalToNoise))
76 {
77 // special case: if a peak core is surrounded by more intense
78 // satellite peaks (indicates oscillation rather than
79 // real peaks) -> remove
80
81 double act_snt_l2 = 0.0, act_snt_r2 = 0.0;
82
83 if(m_signalToNoise > 0.0)
84 {
85 // FIXME
86 }
87
88 // checking signal-to-noise?
89 if((iter > 1) && (iter + 2 < static_cast<qsizetype>(trace.size())) &&
90 (left_data_point_int < trace.at(iter - 2).y) &&
91 (right_data_point_int < trace.at(iter + 2).y) &&
92 (act_snt_l2 >= m_signalToNoise) && (act_snt_r2 >= m_signalToNoise))
93 {
94 ++iter;
95 continue;
96 }
97
98 QMap<double, double> peak_raw_data;
99
100 peak_raw_data[center_data_point_mz] = center_data_point_int;
101 peak_raw_data[left_data_point_mz] = left_data_point_int;
102 peak_raw_data[right_data_point_mz] = right_data_point_int;
103
104 // peak core found, now extend it
105 // to the left
106 qsizetype k = 2;
107
108 // no need to extend peak if previous intensity was zero
109 bool previous_zero_left(false);
110
111 // index of the left boundary for the spline interpolation
112 qsizetype left_boundary(iter - 1);
113
114 while((k <= iter) && // prevent underflow
115 (iter - k + 1 > 0) && !previous_zero_left &&
116 (trace.at(iter - k).y <= peak_raw_data.begin().value()))
117 {
118 double act_snt_lk = 0.0;
119
120 if(m_signalToNoise > 0.0)
121 {
122 // FIXME
123 }
124
125 if(act_snt_lk >= m_signalToNoise)
126 {
127 peak_raw_data[trace.at(iter - k).x] = trace.at(iter - k).y;
128 }
129
130 previous_zero_left = (trace.at(iter - k).y == 0);
131 left_boundary = iter - k;
132 ++k;
133 }
134
135 // to the right
136 k = 2;
137
138 // no need to extend peak if previous intensity was zero
139 bool previous_zero_right(false);
140
141 // index of the right boundary for the spline interpolation
142 qsizetype right_boundary(iter + 1);
143
144 while((iter + k < static_cast<qsizetype>(trace.size())) &&
145 !previous_zero_right &&
146 (trace.at(iter + k).y <= peak_raw_data.last()))
147 {
148 double act_snt_rk = 0.0;
149
150 if(m_signalToNoise > 0.0)
151 {
152 // FIXME
153 }
154
155 if(act_snt_rk >= m_signalToNoise)
156 {
157 peak_raw_data[trace.at(iter + k).x] = trace.at(iter + k).y;
158 }
159
160 previous_zero_right = (trace.at(iter + k).y == 0);
161 right_boundary = iter + k;
162 ++k;
163 }
164
165 // skip if the minimal number of 3 points for fitting is not reached
166 if(peak_raw_data.size() < 3)
167 {
168 continue;
169 }
170
171 // qDebug() << "peak_raw_data size:" << peak_raw_data.size();
172
173 CubicSplineModel cubic_spline_model(peak_raw_data);
174 // qDebug() << "Stack-allocated:" << &cubic_spline_model;
175
176
177 // qDebug() << "The cubic spline model has "
178 // << cubic_spline_model.getKnots().size() << "knots.";
179
180 // calculate maximum by evaluating the spline's 1st derivative
181 // (bisection method)
182 double max_peak_mz = center_data_point_mz;
183 double max_peak_int = center_data_point_int;
184 double threshold = 1e-6;
185 spline_bisection(cubic_spline_model,
186 left_data_point_mz,
187 right_data_point_mz,
188 max_peak_mz,
189 max_peak_int,
190 threshold);
191
192 // save picked peak into output spectrum
193 DataPoint data_point;
194
195 PeakRange peak_range;
196
197 data_point.x = max_peak_mz;
198 data_point.y = max_peak_int;
199 peak_range.mz_start = trace.at(left_boundary).x;
200 peak_range.mz_stop = trace.at(right_boundary).x;
201
202 // qDebug() << "Now appending new data point:" <<
203 // data_point.toString();
204
205 picked_peaks.append(data_point);
206
207 peak_ranges.push_back(peak_range);
208
209 // jump over profile data points that have been considered already
210 iter += k - 1;
211 }
212 }
213}
214
215
216} // namespace pappso
A simple container of DataPoint instances.
Definition trace.h:152
tries to keep as much as possible monoisotopes, removing any possible C13 peaks and changes multichar...
Definition aa.cpp:39
void spline_bisection(const CubicSplineModel &spline_model, double const mz_at_left, double const mz_at_right, double &center_peak_mz, double &center_peak_intensity, double const threshold)
pappso_double x
Definition datapoint.h:24