libpappsomspp
Library for mass spectrometry
Loading...
Searching...
No Matches
savgolfilter.cpp File Reference
#include <qmath.h>
#include <QVector>
#include <QDebug>
#include "pappsomspp/core/exception/exceptionnotrecognized.h"
#include "savgolfilter.h"

Go to the source code of this file.

Namespaces

namespace  pappso
 tries to keep as much as possible monoisotopes, removing any possible C13 peaks and changes multicharge peaks to monocharge
 

Macros

#define SWAP(a, b)
 

Macro Definition Documentation

◆ SWAP

#define SWAP (   a,
 
)
Value:
tempr = (a); \
(a) = (b); \
(b) = tempr

Definition at line 49 of file savgolfilter.cpp.

54{
55 // m_params will have defaults that work well with mass spectra.
56}
57
58FilterSavitzkyGolay::FilterSavitzkyGolay(int nL, int nR, int m, int lD, bool convolveWithNr)
59{
60 m_params.nL = nL;
61 m_params.nR = nR;
62 m_params.m = m;
63 m_params.lD = lD;
64 m_params.convolveWithNr = convolveWithNr;
65}
66
67
68FilterSavitzkyGolay::FilterSavitzkyGolay(const SavGolParams sav_gol_params)
69{
70 m_params.nL = sav_gol_params.nL;
71 m_params.nR = sav_gol_params.nR;
72 m_params.m = sav_gol_params.m;
73 m_params.lD = sav_gol_params.lD;
74 m_params.convolveWithNr = sav_gol_params.convolveWithNr;
75}
76
77
78FilterSavitzkyGolay::FilterSavitzkyGolay(const QString &parameters)
79{
80 buildFilterFromString(parameters);
81}
82
83
84FilterSavitzkyGolay::FilterSavitzkyGolay(const FilterSavitzkyGolay &other)
85{
86 // This function only copies the parameters, not the data.
87
88 m_params.nL = other.m_params.nL;
89 m_params.nR = other.m_params.nR;
90 m_params.m = other.m_params.m;
91 m_params.lD = other.m_params.lD;
92 m_params.convolveWithNr = other.m_params.convolveWithNr;
93}
94
95
96FilterSavitzkyGolay::~FilterSavitzkyGolay()
97{
98}
99
100FilterSavitzkyGolay &
101FilterSavitzkyGolay::operator=(const FilterSavitzkyGolay &other)
102{
103 if(&other == this)
104 return *this;
105
106 // This function only copies the parameters, not the data.
107
108 m_params.nL = other.m_params.nL;
109 m_params.nR = other.m_params.nR;
110 m_params.m = other.m_params.m;
111 m_params.lD = other.m_params.lD;
112 m_params.convolveWithNr = other.m_params.convolveWithNr;
113
114 return *this;
115}
116
117
118void
119FilterSavitzkyGolay::buildFilterFromString(const QString &parameters)
120{
121 // Typical string: "Savitzky-Golay|15;15;4;0;false"
122 if(parameters.startsWith(QString("%1|").arg(name())))
123 {
124 QStringList params = parameters.split("|").back().split(";");
125
126 m_params.nL = params.at(0).toInt();
127 m_params.nR = params.at(1).toInt();
128 m_params.m = params.at(2).toInt();
129 m_params.lD = params.at(3).toInt();
130 m_params.convolveWithNr = (params.at(4) == "true" ? true : false);
131 }
132 else
133 {
135 QString("Building of FilterSavitzkyGolay from string '%1' failed").arg(parameters));
136 }
137}
138
139
140Trace &
141FilterSavitzkyGolay::filter(Trace &data_points) const
142{
143 // Initialize data:
144
145 // We want the filter to stay constant so we create a local copy of the data.
146
147 int data_point_count = data_points.size();
148
149 pappso_double *x_data_p = dvector(1, data_point_count);
150 pappso_double *y_initial_data_p = dvector(1, data_point_count);
151 pappso_double *y_filtered_data_p = nullptr;
152
153 if(m_params.convolveWithNr)
154 y_filtered_data_p = dvector(1, 2 * data_point_count);
155 else
156 y_filtered_data_p = dvector(1, data_point_count);
157
158 for(int iter = 0; iter < data_point_count; ++iter)
159 {
160 x_data_p[iter] = data_points.at(iter).x;
161 y_initial_data_p[iter] = data_points.at(iter).y;
162 }
163
164 // Now run the filter.
165
166 runFilter(y_initial_data_p, y_filtered_data_p, data_point_count);
167
168 // Put back the modified y values into the trace.
169 auto iter_yf = y_filtered_data_p;
170 for(auto &data_point : data_points)
171 {
172 data_point.y = *iter_yf;
173 iter_yf++;
174 }
175
176 return data_points;
177}
178
179
180SavGolParams
181FilterSavitzkyGolay::getParameters() const
182{
183 return SavGolParams(m_params.nL, m_params.nR, m_params.m, m_params.lD, m_params.convolveWithNr);
184}
185
186
187//! Return a string with the textual representation of the configuration data.
188QString
189FilterSavitzkyGolay::toString() const
190{
191 return QString("%1|%2").arg(name()).arg(m_params.toString());
192}
193
194
195QString
196FilterSavitzkyGolay::name() const
197{
198 return "Savitzky-Golay";
199}
200
201
202int *
203FilterSavitzkyGolay::ivector(long nl, long nh) const
204{
205 int *v;
206 v = (int *)malloc((size_t)((nh - nl + 2) * sizeof(int)));
207 if(!v)
208 {
209 qFatal("Error: Allocation failure.");
210 }
211 return v - nl + 1;
212}
213
214
216FilterSavitzkyGolay::dvector(long nl, long nh) const
217{
218 pappso_double *v;
219 long k;
220 v = (pappso_double *)malloc((size_t)((nh - nl + 2) * sizeof(pappso_double)));
221 if(!v)
222 {
223 qFatal("Error: Allocation failure.");
224 }
225 for(k = nl; k <= nh; k++)
226 v[k] = 0.0;
227 return v - nl + 1;
228}
229
230
232FilterSavitzkyGolay::dmatrix(long nrl, long nrh, long ncl, long nch) const
233{
234 long i, nrow = nrh - nrl + 1, ncol = nch - ncl + 1;
235 pappso_double **m;
236 m = (pappso_double **)malloc((size_t)((nrow + 1) * sizeof(pappso_double *)));
237 if(!m)
238 {
239 qFatal("Error: Allocation failure.");
240 }
241 m += 1;
242 m -= nrl;
243 m[nrl] = (pappso_double *)malloc((size_t)((nrow * ncol + 1) * sizeof(pappso_double)));
244 if(!m[nrl])
245 {
246 qFatal("Error: Allocation failure.");
247 }
248 m[nrl] += 1;
249 m[nrl] -= ncl;
250 for(i = nrl + 1; i <= nrh; i++)
251 m[i] = m[i - 1] + ncol;
252 return m;
253}
254
255
256void
257FilterSavitzkyGolay::free_ivector(int *v, long nl, [[maybe_unused]] long nh) const
258{
259 free((char *)(v + nl - 1));
260}
261
262
263void
264FilterSavitzkyGolay::free_dvector(pappso_double *v, long nl, [[maybe_unused]] long nh) const
265{
266 free((char *)(v + nl - 1));
267}
268
269
270void
271FilterSavitzkyGolay::free_dmatrix(
272 pappso_double **m, long nrl, [[maybe_unused]] long nrh, long ncl, [[maybe_unused]] long nch) const
273{
274 free((char *)(m[nrl] + ncl - 1));
275 free((char *)(m + nrl - 1));
276}
277
278
279void
280FilterSavitzkyGolay::lubksb(pappso_double **a, int n, int *indx, pappso_double b[]) const
281{
282 int i, ii = 0, ip, j;
284
285 for(i = 1; i <= n; i++)
286 {
287 ip = indx[i];
288 sum = b[ip];
289 b[ip] = b[i];
290 if(ii)
291 for(j = ii; j <= i - 1; j++)
292 sum -= a[i][j] * b[j];
293 else if(sum)
294 ii = i;
295 b[i] = sum;
296 }
297 for(i = n; i >= 1; i--)
298 {
299 sum = b[i];
300 for(j = i + 1; j <= n; j++)
301 sum -= a[i][j] * b[j];
302 b[i] = sum / a[i][i];
303 }
304}
305
306
307void
308FilterSavitzkyGolay::ludcmp(pappso_double **a, int n, int *indx, pappso_double *d) const
309{
310 int i, imax = 0, j, k;
311 pappso_double big, dum, sum, temp;
312 pappso_double *vv;
313
314 vv = dvector(1, n);
315 *d = 1.0;
316 for(i = 1; i <= n; i++)
317 {
318 big = 0.0;
319 for(j = 1; j <= n; j++)
320 if((temp = fabs(a[i][j])) > big)
321 big = temp;
322 if(big == 0.0)
323 {
324 qFatal("Error: Singular matrix found in routine ludcmp().");
325 }
326 vv[i] = 1.0 / big;
327 }
328 for(j = 1; j <= n; j++)
329 {
330 for(i = 1; i < j; i++)
331 {
332 sum = a[i][j];
333 for(k = 1; k < i; k++)
334 sum -= a[i][k] * a[k][j];
335 a[i][j] = sum;
336 }
337 big = 0.0;
338 for(i = j; i <= n; i++)
339 {
340 sum = a[i][j];
341 for(k = 1; k < j; k++)
342 sum -= a[i][k] * a[k][j];
343 a[i][j] = sum;
344 if((dum = vv[i] * fabs(sum)) >= big)
345 {
346 big = dum;
347 imax = i;
348 }
349 }
350 if(j != imax)
351 {
352 for(k = 1; k <= n; k++)
353 {
354 dum = a[imax][k];
355 a[imax][k] = a[j][k];
356 a[j][k] = dum;
357 }
358 *d = -(*d);
359 vv[imax] = vv[j];
360 }
361 indx[j] = imax;
362 if(a[j][j] == 0.0)
363 a[j][j] = std::numeric_limits<pappso_double>::epsilon();
364 if(j != n)
365 {
366 dum = 1.0 / (a[j][j]);
367 for(i = j + 1; i <= n; i++)
368 a[i][j] *= dum;
369 }
370 }
371 free_dvector(vv, 1, n);
372}
373
374
375void
376FilterSavitzkyGolay::four1(pappso_double data[], unsigned long nn, int isign)
377{
378 unsigned long n, mmax, m, j, istep, i;
379 pappso_double wtemp, wr, wpr, wpi, wi, theta;
380 pappso_double tempr, tempi;
381
382 n = nn << 1;
383 j = 1;
384 for(i = 1; i < n; i += 2)
385 {
386 if(j > i)
387 {
388 SWAP(data[j], data[i]);
389 SWAP(data[j + 1], data[i + 1]);
390 }
391 m = n >> 1;
392 while(m >= 2 && j > m)
393 {
394 j -= m;
395 m >>= 1;
396 }
397 j += m;
398 }
399 mmax = 2;
400 while(n > mmax)
401 {
402 istep = mmax << 1;
403 theta = isign * (6.28318530717959 / mmax);
404 wtemp = sin(0.5 * theta);
405 wpr = -2.0 * wtemp * wtemp;
406 wpi = sin(theta);
407 wr = 1.0;
408 wi = 0.0;
409 for(m = 1; m < mmax; m += 2)
410 {
411 for(i = m; i <= n; i += istep)
412 {
413 j = i + mmax;
414 tempr = wr * data[j] - wi * data[j + 1];
415 tempi = wr * data[j + 1] + wi * data[j];
416 data[j] = data[i] - tempr;
417 data[j + 1] = data[i + 1] - tempi;
418 data[i] += tempr;
419 data[i + 1] += tempi;
420 }
421 wr = (wtemp = wr) * wpr - wi * wpi + wr;
422 wi = wi * wpr + wtemp * wpi + wi;
423 }
424 mmax = istep;
425 }
426}
427
428
429void
430FilterSavitzkyGolay::twofft(pappso_double data1[],
431 pappso_double data2[],
432 pappso_double fft1[],
433 pappso_double fft2[],
434 unsigned long n)
435{
436 unsigned long nn3, nn2, jj, j;
437 pappso_double rep, rem, aip, aim;
438
439 nn3 = 1 + (nn2 = 2 + n + n);
440 for(j = 1, jj = 2; j <= n; j++, jj += 2)
441 {
442 fft1[jj - 1] = data1[j];
443 fft1[jj] = data2[j];
444 }
445 four1(fft1, n, 1);
446 fft2[1] = fft1[2];
447 fft1[2] = fft2[2] = 0.0;
448 for(j = 3; j <= n + 1; j += 2)
449 {
450 rep = 0.5 * (fft1[j] + fft1[nn2 - j]);
451 rem = 0.5 * (fft1[j] - fft1[nn2 - j]);
452 aip = 0.5 * (fft1[j + 1] + fft1[nn3 - j]);
453 aim = 0.5 * (fft1[j + 1] - fft1[nn3 - j]);
454 fft1[j] = rep;
455 fft1[j + 1] = aim;
456 fft1[nn2 - j] = rep;
457 fft1[nn3 - j] = -aim;
458 fft2[j] = aip;
459 fft2[j + 1] = -rem;
460 fft2[nn2 - j] = aip;
461 fft2[nn3 - j] = rem;
462 }
463}
464
465
466void
467FilterSavitzkyGolay::realft(pappso_double data[], unsigned long n, int isign)
468{
469 unsigned long i, i1, i2, i3, i4, np3;
470 pappso_double c1 = 0.5, c2, h1r, h1i, h2r, h2i;
471 pappso_double wr, wi, wpr, wpi, wtemp, theta;
472
473 theta = 3.141592653589793 / (pappso_double)(n >> 1);
474 if(isign == 1)
475 {
476 c2 = -0.5;
477 four1(data, n >> 1, 1);
478 }
479 else
480 {
481 c2 = 0.5;
482 theta = -theta;
483 }
484 wtemp = sin(0.5 * theta);
485 wpr = -2.0 * wtemp * wtemp;
486 wpi = sin(theta);
487 wr = 1.0 + wpr;
488 wi = wpi;
489 np3 = n + 3;
490 for(i = 2; i <= (n >> 2); i++)
491 {
492 i4 = 1 + (i3 = np3 - (i2 = 1 + (i1 = i + i - 1)));
493 h1r = c1 * (data[i1] + data[i3]);
494 h1i = c1 * (data[i2] - data[i4]);
495 h2r = -c2 * (data[i2] + data[i4]);
496 h2i = c2 * (data[i1] - data[i3]);
497 data[i1] = h1r + wr * h2r - wi * h2i;
498 data[i2] = h1i + wr * h2i + wi * h2r;
499 data[i3] = h1r - wr * h2r + wi * h2i;
500 data[i4] = -h1i + wr * h2i + wi * h2r;
501 wr = (wtemp = wr) * wpr - wi * wpi + wr;
502 wi = wi * wpr + wtemp * wpi + wi;
503 }
504 if(isign == 1)
505 {
506 data[1] = (h1r = data[1]) + data[2];
507 data[2] = h1r - data[2];
508 }
509 else
510 {
511 data[1] = c1 * ((h1r = data[1]) + data[2]);
512 data[2] = c1 * (h1r - data[2]);
513 four1(data, n >> 1, -1);
514 }
515}
516
517
518char
519FilterSavitzkyGolay::convlv(pappso_double data[],
520 unsigned long n,
521 pappso_double respns[],
522 unsigned long m,
523 int isign,
524 pappso_double ans[])
525{
526 unsigned long i, no2;
527 pappso_double dum, mag2, *fft;
528
529 fft = dvector(1, n << 1);
530 for(i = 1; i <= (m - 1) / 2; i++)
531 respns[n + 1 - i] = respns[m + 1 - i];
532 for(i = (m + 3) / 2; i <= n - (m - 1) / 2; i++)
533 respns[i] = 0.0;
534 twofft(data, respns, fft, ans, n);
535 no2 = n >> 1;
536 for(i = 2; i <= n + 2; i += 2)
537 {
538 if(isign == 1)
539 {
540 ans[i - 1] = (fft[i - 1] * (dum = ans[i - 1]) - fft[i] * ans[i]) / no2;
541 ans[i] = (fft[i] * dum + fft[i - 1] * ans[i]) / no2;
542 }
543 else if(isign == -1)
544 {
545 if((mag2 = ans[i - 1] * ans[i - 1] + ans[i] * ans[i]) == 0.0)
546 {
547 qDebug("Attempt of deconvolving at zero response in convlv().");
548 return (1);
549 }
550 ans[i - 1] = (fft[i - 1] * (dum = ans[i - 1]) + fft[i] * ans[i]) / mag2 / no2;
551 ans[i] = (fft[i] * dum - fft[i - 1] * ans[i]) / mag2 / no2;
552 }
553 else
554 {
555 qDebug("No meaning for isign in convlv().");
556 return (1);
557 }
558 }
559 ans[2] = ans[n + 1];
560 realft(ans, n, -1);
561 free_dvector(fft, 1, n << 1);
562 return (0);
563}
564
565
566char
567FilterSavitzkyGolay::sgcoeff(pappso_double c[], int np, int nl, int nr, int ld, int m) const
568{
569 int imj, ipj, j, k, kk, mm, *indx;
570 pappso_double d, fac, sum, **a, *b;
571
572 if(np < nl + nr + 1 || nl < 0 || nr < 0 || ld > m || nl + nr < m)
573 {
574 qDebug("Inconsistent arguments detected in routine sgcoeff.");
575 return (1);
576 }
577 indx = ivector(1, m + 1);
578 a = dmatrix(1, m + 1, 1, m + 1);
579 b = dvector(1, m + 1);
580 for(ipj = 0; ipj <= (m << 1); ipj++)
581 {
582 sum = (ipj ? 0.0 : 1.0);
583 for(k = 1; k <= nr; k++)
584 sum += pow((pappso_double)k, (pappso_double)ipj);
585 for(k = 1; k <= nl; k++)
586 sum += pow((pappso_double)-k, (pappso_double)ipj);
587 mm = (ipj < 2 * m - ipj ? ipj : 2 * m - ipj);
588 for(imj = -mm; imj <= mm; imj += 2)
589 a[1 + (ipj + imj) / 2][1 + (ipj - imj) / 2] = sum;
590 }
591 ludcmp(a, m + 1, indx, &d);
592 for(j = 1; j <= m + 1; j++)
593 b[j] = 0.0;
594 b[ld + 1] = 1.0;
595 lubksb(a, m + 1, indx, b);
596 for(kk = 1; kk <= np; kk++)
597 c[kk] = 0.0;
598 for(k = -nl; k <= nr; k++)
599 {
600 sum = b[1];
601 fac = 1.0;
602 for(mm = 1; mm <= m; mm++)
603 sum += b[mm + 1] * (fac *= k);
604 kk = ((np - k) % np) + 1;
605 c[kk] = sum;
606 }
607 free_dvector(b, 1, m + 1);
608 free_dmatrix(a, 1, m + 1, 1, m + 1);
609 free_ivector(indx, 1, m + 1);
610 return (0);
611}
612
613
614//! Perform the Savitzky-Golay filtering process.
615/*
616 The results are in the \c y_filtered_data_p C array of pappso_double
617 values.
618 */
619char
620FilterSavitzkyGolay::runFilter(double *y_data_p,
621 double *y_filtered_data_p,
622 int data_point_count) const
623{
624 int np = m_params.nL + 1 + m_params.nR;
626 char retval;
627
628#if CONVOLVE_WITH_NR_CONVLV
629 c = dvector(1, data_point_count);
630 retval = sgcoeff(c, np, m_nL, m_nR, m_lD, m_m);
631 if(retval == 0)
632 convlv(y_data_p, data_point_count, c, np, 1, y_filtered_data_p);
633 free_dvector(c, 1, data_point_count);
634#else
635 int j;
636 long int k;
637 c = dvector(1, m_params.nL + m_params.nR + 1);
638 retval = sgcoeff(c, np, m_params.nL, m_params.nR, m_params.lD, m_params.m);
639 if(retval == 0)
640 {
641 qDebug() << __FILE__ << __LINE__ << __FUNCTION__ << "()"
642 << "retval is 0";
643
644 for(k = 1; k <= m_params.nL; k++)
645 {
646 for(y_filtered_data_p[k] = 0.0, j = -m_params.nL; j <= m_params.nR; j++)
647 {
648 if(k + j >= 1)
649 {
650 y_filtered_data_p[k] +=
651 c[(j >= 0 ? j + 1 : m_params.nR + m_params.nL + 2 + j)] * y_data_p[k + j];
652 }
653 }
654 }
655 for(k = m_params.nL + 1; k <= data_point_count - m_params.nR; k++)
656 {
657 for(y_filtered_data_p[k] = 0.0, j = -m_params.nL; j <= m_params.nR; j++)
658 {
659 y_filtered_data_p[k] +=
660 c[(j >= 0 ? j + 1 : m_params.nR + m_params.nL + 2 + j)] * y_data_p[k + j];
661 }
662 }
663 for(k = data_point_count - m_params.nR + 1; k <= data_point_count; k++)
664 {
665 for(y_filtered_data_p[k] = 0.0, j = -m_params.nL; j <= m_params.nR; j++)
666 {
667 if(k + j <= data_point_count)
668 {
669 y_filtered_data_p[k] +=
670 c[(j >= 0 ? j + 1 : m_params.nR + m_params.nL + 2 + j)] * y_data_p[k + j];
671 }
672 }
673 }
674 }
675
676 free_dvector(c, 1, m_params.nR + m_params.nL + 1);
677#endif
678
679 return (retval);
680}
681
682
683} // namespace pappso
excetion to use when an item type is not recognized
@ a
peak detected using a single direct MS2 observation
double pappso_double
A type definition for doubles.
Definition types.h:60
#define SWAP(a, b)