OPAL (Object Oriented Parallel Accelerator Library)  2.2.0
OPAL
savgol.cpp
Go to the documentation of this file.
1 /* savgol.C
2  Savitzky-Golay Smoothing Filters
3 
4  NUMERICAL RECIPES IN C: THE ART OF SCIENTIFIC COMPUTING (ISBN 0-521-43108-5)
5 
6  Revision history
7  Date Description Programmer
8  ------------ -------------------------------------------- ------------
9  23/11/2006 Copied from "numerical recipies" Rene Bakker
10 
11  Last Revision:
12  $Id: savgol.C 29 2007-04-14 17:03:18Z l_bakker $
13 */
14 
15 #include <stdlib.h>
16 #include <stdio.h>
17 #include <math.h>
18 #include <string.h>
19 
22 
23 /* internal functions
24  ====================================================================== */
25 
26 static double *vector(long n)
27 /* allocate a double vector with subscript range v[0..n] */
28 {
29  double *v;
30 
31  v = (double *)malloc((size_t)((n + 1) * sizeof(double)));
32  if(!v) {
33  //writeBetError(errModeAll,errFatal,
34  // "savgol.C allocation failure in vector()");
35  writeBetError("savgol.C allocation failure in vector()");
36  }
37  return v;
38 }
39 
40 int *ivector(long n)
41 /* allocate an int vector with subscript range v[nl..n] */
42 {
43  int *v;
44 
45  v = (int *)malloc((size_t)((n + 1) * sizeof(int)));
46  if(!v) {
47  //writeBetError(errModeAll,errFatal,
48  // "savgol.C allocation failure in ivector()");
49  writeBetError("savgol.C allocation failure in ivector()");
50  }
51  return v;
52 }
53 
54 static double **matrix(long nr, long nc)
55 /* allocate a double matrix with subscript range m[0..nr][0..nc] */
56 {
57  long
58  i,
59  nrow = nr + 1,
60  ncol = nc + 1;
61  double
62  **m;
63 
64  /* allocate pointers to rows */
65  m = (double **) malloc((size_t)((nrow + 1) * sizeof(double *)));
66  if(!m) {
67  //writeBetError(errModeAll,errFatal,
68  // "savgol.C allocation failure 1 in matrix()");
69  writeBetError("savgol.C allocation failure 1 in matrix()");
70  }
71  /* allocate rows and set pointers to them */
72  m[0] = (double *) malloc((size_t)((nrow * ncol + 1) * sizeof(double)));
73  if(!m[0]) {
74  //writeBetError(errModeAll,errFatal,
75  // "savgol.C allocation failure 2 in matrix()");
76  writeBetError("savgol.C allocation failure 2 in matrix()");
77  }
78  for(i = 1; i <= nr; i++) m[i] = m[i-1] + ncol;
79 
80  /* return pointer to array of pointers to rows */
81  return m;
82 }
83 
84 static void free_matrix(double **a) {
85  if(a) {
86  if(a[0]) free(a[0]);
87  free(a);
88  }
89 }
90 
91 static int minarg1, minarg2;
92 #define FMIN(a,b) (minarg1=(a),minarg2=(b),(minarg1) < (minarg2) ?\
93  (minarg1) : (minarg2))
94 
95 /* lubksb()
96  Solves the set of n linear equations A·X = B.
97  Here a[1..n][1..n] is input, not as the matrix A but rather as its
98  LU decomposition, determined by the routine ludcmp. indx[1..n] is input
99  as the permutation vector returned by ludcmp. b[1..n] is input as the
100  right-hand side vector B, and returns with the solution vector X. a, n,
101  and indx are not modified by this routine and can be left in place for
102  successive calls with different right-hand sides b. This routine takes
103  into account the possibility that b will begin with many zero elements,
104  so it is efficient for use in matrix inversion.
105 */
106 static void lubksb(double **a, int n, int *indx, double b[]) {
107  int i, ii = 0, ip, j;
108  double sum;
109 
110  for(i = 1; i <= n; i++) {
111  ip = indx[i];
112  sum = b[ip];
113  b[ip] = b[i];
114  if(ii)
115  for(j = ii; j <= i - 1; j++) sum -= a[i][j] * b[j];
116  else if(sum) ii = i;
117  b[i] = sum;
118  }
119  for(i = n; i >= 1; i--) {
120  sum = b[i];
121  for(j = i + 1; j <= n; j++) sum -= a[i][j] * b[j];
122  b[i] = sum / a[i][i];
123  }
124 }
125 
126 #define TINY 1.0e-20;
127 
128 /* ludcmp()
129  Given a matrix a[1..n][1..n], this routine replaces it by the LU
130  decomposition of a rowwise permutation of itself. a and n are input.
131  a is output, arranged as in equation (2.3.14) above;
132  indx[1..n] is an output vector that records the row permutation effected
133  by the partial pivoting; d is output as ±1 depending on whether the number
134  of row interchanges was even or odd, respectively. This routine is used
135  in combination with lubksb to solve linear equations or invert a matrix.
136 */
137 static void ludcmp(double **a, int n, int *indx, double *d) {
138  int i, imax = -1, j, k;
139  double big, dum, sum, temp;
140  double *vv;
141 
142  vv = vector(n);
143  *d = 1.0;
144  for(i = 1; i <= n; i++) {
145  big = 0.0;
146  for(j = 1; j <= n; j++)
147  if((temp = fabs(a[i][j])) > big) big = temp;
148  if(big == 0.0) {
149  //writeBetError(errModeAll,errFatal,
150  // "Singular matrix in routine ludcmp (savgol)");
151  writeBetError("Singular matrix in routine ludcmp (savgol)");
152  }
153 
154  vv[i] = 1.0 / big;
155  }
156  for(j = 1; j <= n; j++) {
157  for(i = 1; i < j; i++) {
158  sum = a[i][j];
159  for(k = 1; k < i; k++) sum -= a[i][k] * a[k][j];
160  a[i][j] = sum;
161  }
162  big = 0.0;
163  for(i = j; i <= n; i++) {
164  sum = a[i][j];
165  for(k = 1; k < j; k++)
166  sum -= a[i][k] * a[k][j];
167  a[i][j] = sum;
168  if((dum = vv[i] * fabs(sum)) >= big) {
169  big = dum;
170  imax = i;
171  }
172  }
173  if(j != imax) {
174  for(k = 1; k <= n; k++) {
175  dum = a[imax][k];
176  a[imax][k] = a[j][k];
177  a[j][k] = dum;
178  }
179  *d = -(*d);
180  vv[imax] = vv[j];
181  }
182  indx[j] = imax;
183  if(a[j][j] == 0.0) a[j][j] = TINY;
184  if(j != n) {
185  dum = 1.0 / (a[j][j]);
186  for(i = j + 1; i <= n; i++) a[i][j] *= dum;
187  }
188  }
189  free(vv);
190 }
191 #undef TINY
192 
193 /* savgol()
194  Returns in c[1..np], in wrap-around order (N.B.!)
195  consistent with the argument respons in
196  routine convlv, a set of Savitzky-Golay filter coefficients.
197  nl is the number of leftward (past) data points used, while
198  nr is the number of rightward (future) data points, making
199  the total number of data points used nl+nr+1.
200  ld is the order of the derivative desired
201  (e.g., ld = 0 for smoothed function).
202  m is the order of the smoothing polynomial,
203  also equal to the highest conserved moment;
204  usual values are m = 2 or m = 4.
205 */
206 void savgol(double c[], int np, int nl, int nr, int ld, int m) {
207  int imj, ipj, j, k, kk, mm, *indx;
208  double d, fac, sum, **a, *b;
209 
210  if(np < nl + nr + 1 || nl < 0 || nr < 0 || ld > m || nl + nr < m) {
211  //writeBetError(errModeAll,errFatal,"bad args in savgol");
212  writeBetError("bad args in savgol");
213  }
214 
215  indx = ivector(m + 1);
216  a = matrix(m + 1, m + 1);
217  b = vector(m + 1);
218  for(ipj = 0; ipj <= (m << 1); ipj++) {
219  sum = (ipj ? 0.0 : 1.0);
220  for(k = 1; k <= nr; k++) sum += pow((double)k, ipj);
221  for(k = 1; k <= nl; k++) sum += pow((double) - k, ipj);
222  mm = FMIN(ipj, 2 * m - ipj);
223  for(imj = -mm; imj <= mm; imj += 2) a[1+(ipj+imj)/2][1+(ipj-imj)/2] = sum;
224  }
225  ludcmp(a, m + 1, indx, &d);
226  for(j = 1; j <= m + 1; j++) b[j] = 0.0;
227  b[ld+1] = 1.0;
228  lubksb(a, m + 1, indx, b);
229  for(kk = 1; kk <= np; kk++) c[kk] = 0.0;
230  for(k = -nl; k <= nr; k++) {
231  sum = b[1];
232  fac = 1.0;
233  for(mm = 1; mm <= m; mm++) sum += b[mm+1] * (fac *= k);
234  kk = ((np - k) % np) + 1;
235  c[kk] = sum;
236  }
237  free(b);
238  free_matrix(a);
239  free(indx);
240 }
241 
242 #undef FMIN
243 
244 #define SWAP(a,b) tempr=(a);(a)=(b);(b)=tempr
245 
246 /* four1()
247  Replaces data[1..2*nn] by its discrete Fourier transform, if isign
248  is input as 1; or replaces data[1..2*nn] by nn times its inverse
249  discrete Fourier transform, if isign is input as -1. data is a
250  complex array of length nn or, equivalently, a real array of length
251  2*nn. nn MUST be an integer power of 2 (this is not checked for!).
252 */
253 void four1(double data[], int nn, int isign) {
254  int n, mmax, m, j, istep, i;
255  double wtemp, wr, wpr, wpi, wi, theta;
256  double tempr, tempi;
257 
258  n = nn << 1;
259  j = 1;
260  for(i = 1; i < n; i += 2) {
261  if(j > i) {
262  SWAP(data[j], data[i]);
263  SWAP(data[j+1], data[i+1]);
264  }
265  m = n >> 1;
266  while(m >= 2 && j > m) {
267  j -= m;
268  m >>= 1;
269  }
270  j += m;
271  }
272  mmax = 2;
273  while(n > mmax) {
274  istep = mmax << 1;
275  theta = isign * (6.28318530717959 / mmax);
276  wtemp = sin(0.5 * theta);
277  wpr = -2.0 * wtemp * wtemp;
278  wpi = sin(theta);
279  wr = 1.0;
280  wi = 0.0;
281  for(m = 1; m < mmax; m += 2) {
282  for(i = m; i <= n; i += istep) {
283  j = i + mmax;
284  tempr = wr * data[j] - wi * data[j+1];
285  tempi = wr * data[j+1] + wi * data[j];
286  data[j] = data[i] - tempr;
287  data[j+1] = data[i+1] - tempi;
288  data[i] += tempr;
289  data[i+1] += tempi;
290  }
291  wr = (wtemp = wr) * wpr - wi * wpi + wr;
292  wi = wi * wpr + wtemp * wpi + wi;
293  }
294  mmax = istep;
295  }
296 }
297 #undef SWAP
298 
299 /* realft()
300  Calculates the Fourier transform of a set of n real-valued data points.
301  Replaces this data (which is stored in array data[1..n]) by the positive
302  frequency half of its complex Fourier transform. The real-valued first
303  and last components of the complex transform are returned as elements
304  data[1] and data[2], respectively. n must be a power of 2. This routine
305  also calculates the inverse transform of a complex data array if it is
306  the transform of real data. (Result in this case must be multiplied
307  by 2/n.)static
308 */
309 static void realft(double data[], int n, int isign) {
310  void four1(double data[], int nn, int isign);
311  int i, i1, i2, i3, i4, np3;
312  double c1 = 0.5, c2, h1r, h1i, h2r, h2i;
313  double wr, wi, wpr, wpi, wtemp, theta;
314 
315  theta = 3.141592653589793 / (double)(n >> 1);
316  if(isign == 1) {
317  c2 = -0.5;
318  four1(data, n >> 1, 1);
319  } else {
320  c2 = 0.5;
321  theta = -theta;
322  }
323  wtemp = sin(0.5 * theta);
324  wpr = -2.0 * wtemp * wtemp;
325  wpi = sin(theta);
326  wr = 1.0 + wpr;
327  wi = wpi;
328  np3 = n + 3;
329  for(i = 2; i <= (n >> 2); i++) {
330  i4 = 1 + (i3 = np3 - (i2 = 1 + (i1 = i + i - 1)));
331  h1r = c1 * (data[i1] + data[i3]);
332  h1i = c1 * (data[i2] - data[i4]);
333  h2r = -c2 * (data[i2] + data[i4]);
334  h2i = c2 * (data[i1] - data[i3]);
335  data[i1] = h1r + wr * h2r - wi * h2i;
336  data[i2] = h1i + wr * h2i + wi * h2r;
337  data[i3] = h1r - wr * h2r + wi * h2i;
338  data[i4] = -h1i + wr * h2i + wi * h2r;
339  wr = (wtemp = wr) * wpr - wi * wpi + wr;
340  wi = wi * wpr + wtemp * wpi + wi;
341  }
342  if(isign == 1) {
343  data[1] = (h1r = data[1]) + data[2];
344  data[2] = h1r - data[2];
345  } else {
346  data[1] = c1 * ((h1r = data[1]) + data[2]);
347  data[2] = c1 * (h1r - data[2]);
348  four1(data, n >> 1, -1);
349  }
350 }
351 
352 /* twofft()
353  Given two real input arrays data1[1..n] and data2[1..n],
354  this routine calls four1 and returns two complex output arrays,
355  fft1[1..2n] and fft2[1..2n], each of complex length n (i.e.,
356  real length 2*n), which contain the discrete Fourier transforms
357  of the respective data arrays. n MUST be an integer power of 2
358 */
359 static void twofft(double data1[], double data2[], double fft1[], double fft2[],
360  int n) {
361  void four1(double data[], int nn, int isign);
362  int nn3, nn2, jj, j;
363  double rep, rem, aip, aim;
364 
365  nn3 = 1 + (nn2 = 2 + n + n);
366  for(j = 1, jj = 2; j <= n; j++, jj += 2) {
367  fft1[jj-1] = data1[j];
368  fft1[jj] = data2[j];
369  }
370  four1(fft1, n, 1);
371  fft2[1] = fft1[2];
372  fft1[2] = fft2[2] = 0.0;
373  for(j = 3; j <= n + 1; j += 2) {
374  rep = 0.5 * (fft1[j] + fft1[nn2-j]);
375  rem = 0.5 * (fft1[j] - fft1[nn2-j]);
376  aip = 0.5 * (fft1[j+1] + fft1[nn3-j]);
377  aim = 0.5 * (fft1[j+1] - fft1[nn3-j]);
378  fft1[j] = rep;
379  fft1[j+1] = aim;
380  fft1[nn2-j] = rep;
381  fft1[nn3-j] = -aim;
382  fft2[j] = aip;
383  fft2[j+1] = -rem;
384  fft2[nn2-j] = aip;
385  fft2[nn3-j] = rem;
386  }
387 }
388 
389 // static double sqrarg;
390 #define SQR(a) (a == 0.0 ? 0.0 : a*a)//sqrarg*sqrarg)
391 
392 /* convlv()
393  Convolves or deconvolves a real data set data[1..n] (including any
394  user-supplied zero padding) with a response function respns[1..n].
395  The response function must be stored in wrap-around order in the
396  first m elements of respns, where m is an odd integer <=n. Wrap-
397  around order means that the first half of the array respns contains
398  the impulse response function at positive times, while the second
399  half of the array contains the impulse response function at negative
400  times, counting down from the highest element respns[m]. On input
401  isign is +1 for convolution, -1 for deconvolution. The answer is
402  returned in the first n components of ans. However, ans must be
403  supplied in the calling program with dimensions [1..2*n], for
404  consistency with twofft. n MUST be an integer power of two.
405 */
406 void convlv(double data[], int n, double respns[], int m,
407  int isign, double ans[]) {
408  int i, no2;
409  double dum, mag2, *fft;
410 
411  fft = vector(n << 1);
412 
413  for(i = 1; i <= (m - 1) / 2; i++)
414  respns[n+1-i] = respns[m+1-i];
415  for(i = (m + 3) / 2; i <= n - (m - 1) / 2; i++)
416  respns[i] = 0.0;
417  twofft(data, respns, fft, ans, n);
418  no2 = n >> 1;
419  for(i = 2; i <= n + 2; i += 2) {
420  if(isign == 1) {
421  ans[i-1] = (fft[i-1] * (dum = ans[i-1]) - fft[i] * ans[i]) / no2;
422  ans[i] = (fft[i] * dum + fft[i-1] * ans[i]) / no2;
423  } else if(isign == -1) {
424  if((mag2 = SQR(ans[i-1]) + SQR(ans[i])) == 0.0) {
425  // writeBetError(errModeAll,errFatal,
426  // "savgol.C Deconvolving at response zero in convlv");
427  writeBetError("savgol.C Deconvoloving at repsonse zero in convlv");
428  }
429 
430  ans[i-1] = (fft[i-1] * (dum = ans[i-1]) + fft[i] * ans[i]) / mag2 / no2;
431  ans[i] = (fft[i] * dum - fft[i-1] * ans[i]) / mag2 / no2;
432  } else writeBetError("No meaning for isign in convlv");
433 
434  //writeBetError(errModeAll,errFatal,
435  // "No meaning for isign in convlv");
436  }
437  ans[2] = ans[n+1];
438  realft(ans, n, -1);
439 
440  free(fft);
441 }
442 
443 #undef SQR
444 
445 
446 /* sgSmooth()
447  Smoothes c[0..n-1] with a Savitzky-Golay filter.
448  nl is the number of leftward (past) data points used, while
449  nr is the number of rightward (future) data points, making
450  the total number of data points used nl+nr+1.
451  ld is the order of the derivative desired
452  (e.g., ld = 0 for smoothed function).
453  m is the order of the smoothing polynomial,
454  also equal to the highest conserved moment;
455  usual values are m = 2 or m = 4.
456 */
457 void sgSmooth(double *c, int n, int nl, int nr, int ld, int m) {
458  double
459  *cIn, *cOut,
460  *cf;
461  int
462  isign,
463  nn, np, i;
464 
465  // make dimension 2^m with m integer
466  nn = (int) pow(2.0, (int)(log(1.0 * n) / log(2.0)) + 1);
467 
468 
469  // memory allocation
470  cf = vector(nn);
471  cIn = vector(nn);
472  cOut = vector(nn * 2);
473 
474  // fill data array
475  cIn[0] = 0.0;
476  memcpy(&cIn[1], c, sizeof(double)*n);
477  for(i = n + 1; i <= nn; i++) cIn[i] = 0.0;
478 
479  // create filter coefficients
480  np = nl + nr + 1;
481  if((np % 2) == 0) ++np;
482  savgol(cf, np, nl, nr, ld, m);
483 
484  // filter
485  isign = 1;
486  convlv(cIn, nn, cf, np, isign, cOut);
487 
488  // move data back
489  memcpy(c, &cOut[1], sizeof(double)*n);
490 
491  free(cIn);
492  free(cOut);
493  free(cf);
494  // writeBetError(errModeMaster,errMessage,"SG n = %d, nn = %d, np = %d",n,nn,np);
495 }
void sgSmooth(double *c, int n, int nl, int nr, int ld, int m)
Definition: savgol.cpp:457
int * ivector(long n)
Definition: savgol.cpp:40
#define SQR(a)
Definition: savgol.cpp:390
void writeBetError(std::string err)
Definition: BetError.cpp:36
PETE_TUTree< FnFabs, typename T::PETE_Expr_t > fabs(const PETE_Expr< T > &l)
Definition: PETE.h:815
Tps< T > sin(const Tps< T > &x)
Sine.
Definition: TpsMath.h:111
const int nr
Definition: ClassicRandom.h:24
T::PETE_Expr_t::PETE_Return_t sum(const PETE_Expr< T > &expr)
Definition: PETE.h:1213
Tps< T > log(const Tps< T > &x)
Natural logarithm.
Definition: TpsMath.h:182
void savgol(double c[], int np, int nl, int nr, int ld, int m)
Definition: savgol.cpp:206
void convlv(double data[], int n, double respns[], int m, int isign, double ans[])
Definition: savgol.cpp:406
constexpr double c
The velocity of light in m/s.
Definition: Physics.h:52
Tps< T > pow(const Tps< T > &x, int y)
Integer power.
Definition: TpsMath.h:76
#define SWAP(a, b)
Definition: savgol.cpp:244
#define FMIN(a, b)
Definition: savgol.cpp:92
#define TINY
Definition: savgol.cpp:126
void four1(double data[], int nn, int isign)
Definition: savgol.cpp:253