diff --git a/src/backend/nsl/nsl_smooth.c b/src/backend/nsl/nsl_smooth.c index c13d4d42e..17435d43e 100644 --- a/src/backend/nsl/nsl_smooth.c +++ b/src/backend/nsl/nsl_smooth.c @@ -1,547 +1,548 @@ /*************************************************************************** File : nsl_smooth.c Project : LabPlot Description : NSL smooth functions -------------------------------------------------------------------- Copyright : (C) 2010 by Knut Franke (knut.franke@gmx.de) Copyright : (C) 2016 by Stefan Gerlach (stefan.gerlach@uni.kn) ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, * * Boston, MA 02110-1301 USA * * * ***************************************************************************/ #include "nsl_smooth.h" #include "nsl_common.h" #include "nsl_sf_kernel.h" #include "nsl_stats.h" #include #include #include #include /* gsl_sf_choose */ const char* nsl_smooth_type_name[] = { i18n("moving average (central)"), i18n("moving average (lagged)"), i18n("percentile"), i18n("Savitzky-Golay") }; const char* nsl_smooth_pad_mode_name[] = { i18n("none"), i18n("interpolating"), i18n("mirror"), i18n("nearest"), i18n("constant"), i18n("periodic") }; const char* nsl_smooth_weight_type_name[] = { i18n("uniform (rectangular)"), i18n("triangular"), i18n("binomial"), i18n("parabolic (Epanechnikov)"), i18n("quartic (biweight)"), i18n("triweight"), i18n("tricube"), i18n("cosine") }; double nsl_smooth_pad_constant_lvalue = 0.0, nsl_smooth_pad_constant_rvalue = 0.0; int nsl_smooth_moving_average(double *data, size_t n, size_t points, nsl_smooth_weight_type weight, nsl_smooth_pad_mode mode) { size_t i, j; double *result = (double *)malloc(n*sizeof(double)); for (i = 0; i < n; i++) result[i] = 0; for (i = 0; i < n; i++) { size_t np = points; size_t half = (points-1)/2; if (mode == nsl_smooth_pad_none) { /* reduce points */ half = GSL_MIN(GSL_MIN((points-1)/2, i), n-i-1); np = 2 * half + 1; } /* weight */ double sum = 0.0; double* w = (double*)malloc(np * sizeof(double)); switch (weight) { case nsl_smooth_weight_uniform: for (j = 0; j < np; j++) w[j] = 1./np; break; case nsl_smooth_weight_triangular: sum = gsl_pow_2((double)(np + 1)/2); for (j = 0; j < np; j++) w[j] = GSL_MIN(j + 1, np - j)/sum; break; case nsl_smooth_weight_binomial: sum = (np - 1)/2.; for (j = 0; j < np; j++) w[j] = gsl_sf_choose((unsigned int)(2 * sum), (unsigned int)((sum + fabs(j - sum))/pow(4., sum))); break; case nsl_smooth_weight_parabolic: for (j = 0; j < np; j++) { w[j] = nsl_sf_kernel_parabolic(2.*(j-(np-1)/2.)/(np+1)); sum += w[j]; } for (j = 0; j < np; j++) w[j] /= sum; break; case nsl_smooth_weight_quartic: for (j = 0; j < np; j++) { w[j]=nsl_sf_kernel_quartic(2.*(j-(np-1)/2.)/(np+1)); sum += w[j]; } for (j = 0; j < np; j++) w[j] /= sum; break; case nsl_smooth_weight_triweight: for (j = 0; j < np; j++) { w[j] = nsl_sf_kernel_triweight(2.*(j-(np-1)/2.)/(np+1)); sum += w[j]; } for (j = 0 ; j < np; j++) w[j] /= sum; break; case nsl_smooth_weight_tricube: for (j = 0; j < np; j++) { w[j] = nsl_sf_kernel_tricube(2.*(j-(np-1)/2.)/(np+1)); sum += w[j]; } for (j = 0 ; j < np; j++) w[j] /= sum; break; case nsl_smooth_weight_cosine: for (j = 0; j < np; j++) { w[j] = nsl_sf_kernel_cosine((j-(np-1)/2.)/((np+1)/2.)); sum += w[j]; } for (j = 0 ; j < np; j++) w[j] /= sum; break; } /*printf("(%d) w:",i); for(j=0;j (int)n - 1) result[i] += w[j]*nsl_smooth_pad_constant_rvalue; else result[i] += w[j]*data[index]; break; case nsl_smooth_pad_periodic: if (index < 0) - index = n+index; + index = index + (int)n; else if (index > (int)n - 1) - index = index-n; + index = index - (int)n; result[i] += w[j] * data[index]; break; } } /*puts("");*/ free(w); } for (i = 0; i < n; i++) data[i] = result[i]; free(result); return 0; } int nsl_smooth_moving_average_lagged(double *data, size_t n, size_t points, nsl_smooth_weight_type weight, nsl_smooth_pad_mode mode) { size_t i, j; double* result = (double *)malloc(n*sizeof(double)); for (i = 0; i < n; i++) result[i] = 0; for (i = 0; i < n; i++) { size_t np = points; size_t half = (points-1)/2; if (mode == nsl_smooth_pad_none) { /* reduce points */ np = GSL_MIN(points, i+1); half = np-1; } /* weight */ double sum = 0.0, *w = (double *)malloc(np*sizeof(double)); switch (weight) { case nsl_smooth_weight_uniform: for (j = 0; j < np; j++) w[j] = 1./np; break; case nsl_smooth_weight_triangular: sum = np*(double)(np+1)/2; for (j = 0; j < np; j++) w[j] = (j+1)/sum; break; case nsl_smooth_weight_binomial: for (j = 0; j < np; j++) { - w[j] = gsl_sf_choose(2*(np-1), j); + w[j] = gsl_sf_choose((unsigned int)(2*(np-1)), j); sum += w[j]; } for (j = 0 ; j < np; j++) w[j] /= sum; break; case nsl_smooth_weight_parabolic: for (j = 0; j < np; j++) { w[j] = nsl_sf_kernel_parabolic(1.-(1+j)/(double)np); sum += w[j]; } for (j = 0; j < np; j++) w[j] /= sum; break; case nsl_smooth_weight_quartic: for( j = 0; j < np; j++) { w[j] = nsl_sf_kernel_quartic(1.-(1+j)/(double)np); sum += w[j]; } for (j = 0; j < np; j++) w[j] /= sum; break; case nsl_smooth_weight_triweight: for (j = 0; j < np; j++) { w[j] = nsl_sf_kernel_triweight(1.-(1+j)/(double)np); sum += w[j]; } for (j = 0; j < np; j++) w[j] /= sum; break; case nsl_smooth_weight_tricube: for (j = 0; j < np; j++) { w[j] = nsl_sf_kernel_tricube(1.-(1+j)/(double)np); sum += w[j]; } for (j = 0; j < np; j++) w[j] /= sum; break; case nsl_smooth_weight_cosine: for (j = 0; j < np; j++) { w[j] = nsl_sf_kernel_cosine((np-1-j)/(double)np); sum += w[j]; } for (j = 0; j < np; j++) w[j] /= sum; break; } /*printf("(%d) w:",i); for(j=0;j (int)n-1) values[j] = nsl_smooth_pad_constant_rvalue; else values[j] = data[index]; break; case nsl_smooth_pad_periodic: if (index < 0) - index = n+index; + index = index + (int)n; else if (index > (int)n-1) - index = index-n; + index = index - (int)n; /*printf(" %d",index);*/ values[j] = data[index]; break; } } /*puts("");*/ /*using type 4 as default */ result[i] = nsl_stats_quantile(values, 1, np, percentile, nsl_stats_quantile_type4); free(values); } for (i=0; i n) { printf("Tried to smooth over more points (points=%d) than given as input (%d).", (int)points, (int)n); return -1; } if (order < 1 || (size_t)order > points-1) { printf("The polynomial order must be between 1 and %d (%d given).", (int)(points-1), order); return -2; } /* Savitzky-Golay coefficient matrix, y' = H y */ gsl_matrix *h = gsl_matrix_alloc(points, points); error = nsl_smooth_savgol_coeff(points, order, h); if (error) { printf("Internal error in Savitzky-Golay algorithm:\n%s", gsl_strerror(error)); gsl_matrix_free(h); return error; } double *result = (double *)malloc(n*sizeof(double)); for (i = 0; i < n; i++) result[i] = 0; /* left edge */ if(mode == nsl_smooth_pad_none) { for (i = 0; i < half; i++) { /*reduce points and order*/ - size_t rpoints = 2*i+1, rorder = GSL_MIN((size_t)order, rpoints-GSL_MIN(rpoints, 2)); + size_t rpoints = 2*i+1; + int rorder = GSL_MIN(order, (int)(rpoints-GSL_MIN(rpoints, 2))); gsl_matrix *rh = gsl_matrix_alloc(rpoints, rpoints); error = nsl_smooth_savgol_coeff(rpoints, rorder, rh); if (error) { printf("Internal error in Savitzky-Golay algorithm:\n%s",gsl_strerror(error)); gsl_matrix_free(rh); free(result); return error; } for (k = 0; k < rpoints; k++) result[i] += gsl_matrix_get(rh, i, k) * data[k]; } } else { for (i = 0; i < half; i++) { for (k = 0; k < points; k++) switch(mode) { case nsl_smooth_pad_interp: result[i] += gsl_matrix_get(h, i, k) * data[k]; break; case nsl_smooth_pad_mirror: result[i] += gsl_matrix_get(h, half, k) * data[abs((int)(k+i-half))]; break; case nsl_smooth_pad_nearest: result[i] += gsl_matrix_get(h, half, k) * data[i+k-GSL_MIN(half,i+k)]; break; case nsl_smooth_pad_constant: if (k