diff src/ltpda_dft/ltpda_dft.c @ 0:f0afece42f48

Import.
author Daniele Nicolodi <nicolodi@science.unitn.it>
date Wed, 23 Nov 2011 19:22:13 +0100
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ltpda_dft/ltpda_dft.c	Wed Nov 23 19:22:13 2011 +0100
@@ -0,0 +1,559 @@
+/*
+ * Mex file that implements the core DFT part of the LPSD algorithm.
+ *
+ * M Hewitson  15-01-08
+ *
+ * $Id: ltpda_dft.c,v 1.16 2009/08/18 10:53:27 hewitson Exp $
+ */
+
+
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <mex.h>
+
+#include "ltpda_dft.h"
+#include "version.h"
+#include "../c_sources/polyreg.c"
+
+#define DEBUG 0
+
+
+/*
+ * Rounding function
+ *
+ */
+int myround(double x) {
+  return ((int) (floor(x + 0.5)));
+}
+
+
+/*
+ * Matlab mex file to make a dft at a single frequency
+ *
+ * Inputs
+ *  - data
+ *  - seg length
+ *  - DFT coefficients
+ *  - overlap (%)
+ *  - order of detrending
+ *
+ * function [P, navs] = ltpda_dft(x, seglen, DFTcoeffs, olap, order);
+ *
+ */
+void  mexFunction(  int nlhs,       mxArray *plhs[],
+        int nrhs, const mxArray *prhs[]) {
+  /* Parse inputs */
+  if( (nrhs == 0) && (nlhs == 0) ) {
+    print_usage(VERSION);
+    return;
+  }
+  else if ( (nrhs == 5) && (nlhs == 3) ) /* let's go */ {
+    double    Pr, Vr;
+    double   *xdata, *Cr, *Ci;
+    double    olap;
+    long int  nData;
+    long int  nSegs;
+    long int  segLen;
+    int       order;
+    double   *ptr;
+    
+    if( !mxIsComplex(prhs[2]) )
+      mexErrMsgTxt("DFT coefficients must be complex.\n");
+    
+    /* Extract inputs */
+    xdata     = mxGetPr(prhs[0]);                 /* Pointer to data */
+    nData     = mxGetNumberOfElements(prhs[0]);   /* Number of data points */
+    segLen    = (int)mxGetScalar(prhs[1]);        /* Segment length */
+    Cr        = mxGetPr(prhs[2]);                 /* Real part of DFT coefficients */
+    Ci        = mxGetPi(prhs[2]);                 /* Imag part of DFT coefficients */
+    olap      = mxGetScalar(prhs[3]);             /* Overlap percentage */
+    order     = (int)mxGetScalar(prhs[4]);        /* Order of detrending */
+    
+    if (order > 10 || order < -1)
+      mexErrMsgTxt("Detrending order must be between -1 and 10");
+    
+    /*mexPrintf("Input data: %d samples\n", nData);*/
+    /*mexPrintf("Segment length: %d\n", segLen);*/
+    /*mexPrintf("Overlap: %f\n", olap);*/
+    
+    /* Compute DFT */
+    dft(&Pr, &Vr, &nSegs, xdata, nData, segLen, Cr, Ci, olap, order);
+    
+    /* Set output matrices */
+    plhs[0] = mxCreateDoubleMatrix(1, 1, mxCOMPLEX);
+    plhs[1] = mxCreateDoubleMatrix(1, 1, mxCOMPLEX);
+    plhs[2] = mxCreateDoubleMatrix(1, 1, mxCOMPLEX);
+    
+    /* Get pointers to output matrices */
+    ptr    = mxGetPr(plhs[0]);
+    ptr[0] = Pr;
+    ptr    = mxGetPr(plhs[1]);
+    ptr[0] = Vr;
+    ptr    = mxGetPr(plhs[2]);
+    ptr[0] = nSegs;
+    
+  }
+  else if ( (nrhs == 6) && (nlhs == 5) ) /* let's go */ {
+    double    Mr, Mi, XX, YY, M2;
+    double   *xdata, *ydata, *Cr, *Ci;
+    double    olap;
+    long int  nData, nxData, nyData;
+    long int  nSegs;
+    long int  segLen;
+    int       order;
+    double   *ptr;
+    
+    if( !mxIsComplex(prhs[3]) )
+      mexErrMsgTxt("DFT coefficients must be complex.\n");
+    
+    /* Extract inputs */
+    xdata     = mxGetPr(prhs[0]);                 /* Pointer to data */
+    ydata     = mxGetPr(prhs[1]);                 /* Pointer to data */
+    nxData    = mxGetNumberOfElements(prhs[0]);   /* Number of data points */
+    nyData    = mxGetNumberOfElements(prhs[0]);   /* Number of data points */
+    segLen    = (int)mxGetScalar(prhs[2]);        /* Segment length */
+    Cr        = mxGetPr(prhs[3]);                 /* Real part of DFT coefficients */
+    Ci        = mxGetPi(prhs[3]);                 /* Imag part of DFT coefficients */
+    olap      = mxGetScalar(prhs[4]);             /* Overlap percentage */
+    order     = (int)mxGetScalar(prhs[5]);        /* Order of detrending */
+    
+    if (order > 10 || order < -1)
+      mexErrMsgTxt("Detrending order must be between -1 and 10");
+    
+    if (nxData != nyData)
+      mexErrMsgTxt("The two input data vector should be the same length.");
+    
+    nData = nxData;
+    
+    /*mexPrintf("Input data: %d samples\n", nData);*/
+    /*mexPrintf("Segment length: %d\n", segLen);*/
+    /*mexPrintf("Overlap: %f\n", olap);*/
+    
+    /* Compute DFT */
+    xdft(&Mr, &Mi, &XX, &YY, &M2, &nSegs, xdata, ydata, nData, segLen, Cr, Ci, olap, order);
+    
+    /* Set output matrices */
+    plhs[0] = mxCreateDoubleMatrix(1, 1, mxCOMPLEX);
+    plhs[1] = mxCreateDoubleMatrix(1, 1, mxREAL);
+    plhs[2] = mxCreateDoubleMatrix(1, 1, mxREAL);
+    plhs[3] = mxCreateDoubleMatrix(1, 1, mxREAL);
+    plhs[4] = mxCreateDoubleMatrix(1, 1, mxREAL);
+    
+    /* Get pointers to output matrices */
+    ptr    = mxGetPr(plhs[0]);
+    ptr[0] = Mr;
+    ptr    = mxGetPi(plhs[0]);
+    ptr[0] = Mi;
+    ptr    = mxGetPr(plhs[1]);
+    ptr[0] = XX;
+    ptr    = mxGetPr(plhs[2]);
+    ptr[0] = YY;
+    ptr    = mxGetPr(plhs[3]);
+    ptr[0] = M2;
+    ptr    = mxGetPr(plhs[4]);
+    ptr[0] = nSegs;
+    
+    
+  }
+  else /* we have an error */ {
+    print_usage(VERSION);
+    mexErrMsgTxt("### incorrect usage");
+  }
+}
+
+/*
+ *  Output usage to MATLAB terminal
+ *
+ */
+void print_usage(char *version) {
+  mexPrintf("ltpda_dft version %s\n", version);
+  mexPrintf("  usage:    function [P, navs] = ltpda_dft(x, seglen, DFTcoeffs, olap, order); \n");
+}
+
+/*
+ * Short routine to compute the DFT at a single frequency
+ *
+ */
+void dft(double *Pr, double *Vr, long int *Navs,
+        double *xdata, long int nData, long int segLen, double *Cr, double *Ci, double olap, int order) {
+  long int  istart;
+  double    shift, start;
+  double    *px, *cr, *ci;
+  double    rxsum, ixsum;
+  double    Xr, Mr, M2, Qr;
+  double    p, *x, *a;
+  long int  jj, ii;
+    
+  
+  /* Compute the number of averages we want here */
+  double   ovfact = 1. / (1. - olap / 100.);
+  double   davg = ((double) ((nData - segLen)) * ovfact) / segLen + 1;
+  long int navg = myround(davg);
+    
+  /* Compute steps between segments */
+  if (navg == 1)
+    shift = 1;
+  else
+    shift = (double) (nData - segLen) / (double) (navg - 1);
+  
+  if (shift < 1)
+    shift = 1;
+  
+  /*   mexPrintf("Seglen: %d\t | Shift: %f\t | navs: %d\n", segLen, shift, navg);*/
+  
+  /* allocate vectors */
+  x = (double*)mxCalloc(segLen, sizeof(double));  /* detrending output */
+  a = (double*)mxCalloc(order+1, sizeof(double)); /* detrending coefficients */
+  
+  /* Loop over segments */
+  start = 0.0;
+  Xr = 0.0;
+  Qr = 0.0;
+  Mr = 0.0;
+  M2 = 0.0;
+  
+  for (ii = 0; ii < navg; ii++) {
+    /* compute start index */
+    istart = myround(start);
+    start += shift;
+    
+    /* pointer to start of this segment */
+    px = &(xdata[istart]);
+    
+    /* pointer to DFT coeffs */
+    cr = &(Cr[0]);
+    ci = &(Ci[0]);
+    
+    /* Detrend segment */
+    switch (order) {
+      case -1:
+        /* no detrending */
+        memcpy(x, px, segLen*sizeof(double));
+        break;
+      case 0:
+        /* mean removal */
+        polyreg0(px, segLen, x, a);
+        break;
+      case 1:
+        /* linear detrending */
+        polyreg1(px, segLen, x, a);
+        break;
+      case 2:
+        /* 2nd order detrending */
+        polyreg2(px, segLen, x, a);
+        break;
+      case 3:
+        /* 3rd order detrending */
+        polyreg3(px, segLen, x, a);
+        break;
+      case 4:
+        /* 4th order detrending */
+        polyreg4(px, segLen, x, a);
+        break;
+      case 5:
+        /* 5th order detrending */
+        polyreg5(px, segLen, x, a);
+        break;
+      case 6:
+        /* 6th order detrending */
+        polyreg6(px, segLen, x, a);
+        break;
+      case 7:
+        /* 7th order detrending */
+        polyreg7(px, segLen, x, a);
+        break;
+      case 8:
+        /* 8th order detrending */
+        polyreg8(px, segLen, x, a);
+        break;
+      case 9:
+        /* 9th order detrending */
+        polyreg9(px, segLen, x, a);
+        break;
+      case 10:
+        /* 10th order detrending */
+        polyreg10(px, segLen, x, a);
+        break;
+    }
+    
+    /* Go over all samples in this segment */
+    rxsum = ixsum = 0.0;
+    for (jj=0; jj<segLen; jj++) {
+      p      = x[jj];
+      rxsum += (*cr) * p; /* cos term */
+      ixsum += (*ci) * p; /* sin term */
+      
+      /* increment pointers */
+      cr++;
+      ci++;
+    }
+    /*mexPrintf("   xsum=(%g +i %g), ysum=(%g + i%g)\n", rxsum, ixsum, rysum, iysum);*/
+    
+    /* Average the cross-power
+     * Rsum += rxsum*rxsum + ixsum*ixsum;
+     */
+    
+    /* Welford's algorithm to update mean and variance */
+    if (ii == 0) {
+      Mr = (rxsum*rxsum + ixsum*ixsum);
+    } else {
+      Xr = (rxsum*rxsum + ixsum*ixsum);
+      Qr = Xr - Mr;
+      Mr += Qr/ii;
+      M2 += Qr * (Xr - Mr);
+    }
+    
+  }
+  
+  /* mexPrintf("     start: %f \t istart: %d | %d \n", start, istart, nData-istart);*/
+  /*mexPrintf(" Rsum=%g, MR=%g \n", Rsum,MR); */
+  
+  /* clean up */
+  mxFree(x);
+  mxFree(a);
+  
+  /* Outputs */
+  *Pr = Mr;
+  if(navg == 1){
+    *Vr = Mr*Mr;
+  } else {
+    *Vr = M2/(navg-1);
+  }
+  *Navs = navg;
+}
+
+/*
+ * Short routine to compute the cross-DFT at a single frequency
+ *
+ */
+void xdft(double *Pxyr, double *Pxyi, double *Pxx, double *Pyy, double *Vr, long int *Navs,
+        double *xdata, double *ydata, long int nData, long int segLen, double *Cr, double *Ci, double olap, int order) {
+  long int  istart;
+  double    shift, start;
+  double    *px, *py, *cr, *ci;
+  double    rxsum, ixsum, rysum, iysum;
+  double    XYr, XYi, QXYr, QXYi, QXYrn, QXYin;
+  double    MXYr, MXYi, MXY2;
+  double    XX, YY, QXX, QYY;
+  double    MXX, MYY, MXX2, MYY2;
+  double    p, *x, *y, *a;
+  double    ct, st;
+  long int  jj, ii;
+  
+  /* Compute the number of averages we want here */
+  double   ovfact = 1. / (1. - olap / 100.);
+  double   davg = ((double) ((nData - segLen)) * ovfact) / segLen + 1;
+  long int navg = myround( davg );
+  
+  /* Compute steps between segments */
+  if (navg == 1)
+    shift = 1;
+  else
+    shift = (double) (nData - segLen) / (double) (navg - 1);
+  
+  if (shift < 1)
+    shift = 1;
+  
+  /* mexPrintf("Seglen: %d\t | Shift: %f\t | navs: %d\n", segLen, shift, navg); */
+  
+  /* allocate vectors */
+  y = (double*)mxCalloc(segLen, sizeof(double));  /* detrending output */
+  x = (double*)mxCalloc(segLen, sizeof(double));  /* detrending output */
+  a = (double*)mxCalloc(order+1, sizeof(double)); /* detrending coefficients */
+  
+  /* Loop over segments */
+  start = 0.0;
+  MXYr  = 0.0;
+  MXYi  = 0.0;
+  MXY2   = 0.0;
+  MXX   = 0.0;
+  MYY   = 0.0;
+  MXX2 = 0.0;
+  MYY2 = 0.0;
+  
+  for (ii = 0; ii < navg; ii++) {
+    /* compute start index */
+    istart = myround(start);
+    start += shift;
+    
+    /* pointer to start of this segment */
+    px = &(xdata[istart]);
+    py = &(ydata[istart]);
+    
+    /* pointer to DFT coeffs */
+    cr = &(Cr[0]);
+    ci = &(Ci[0]);
+    
+    /* Detrend segment */
+    switch (order) {
+      case -1:
+        /* no detrending */
+        memcpy(x, px, segLen*sizeof(double));
+        memcpy(y, py, segLen*sizeof(double));
+        break;
+      case 0:
+        /* mean removal */
+        polyreg0(px, segLen, x, a);
+        polyreg0(py, segLen, y, a);
+        break;
+      case 1:
+        /* linear detrending */
+        polyreg1(px, segLen, x, a);
+        polyreg1(py, segLen, y, a);
+        break;
+      case 2:
+        /* 2nd order detrending */
+        polyreg2(px, segLen, x, a);
+        polyreg2(py, segLen, y, a);
+        break;
+      case 3:
+        /* 3rd order detrending */
+        polyreg3(px, segLen, x, a);
+        polyreg3(py, segLen, y, a);
+        break;
+      case 4:
+        /* 4th order detrending */
+        polyreg4(px, segLen, x, a);
+        polyreg4(py, segLen, y, a);
+        break;
+      case 5:
+        /* 5th order detrending */
+        polyreg5(px, segLen, x, a);
+        polyreg5(py, segLen, y, a);
+        break;
+      case 6:
+        /* 6th order detrending */
+        polyreg6(px, segLen, x, a);
+        polyreg6(py, segLen, y, a);
+        break;
+      case 7:
+        /* 7th order detrending */
+        polyreg7(px, segLen, x, a);
+        polyreg7(py, segLen, y, a);
+        break;
+      case 8:
+        /* 8th order detrending */
+        polyreg8(px, segLen, x, a);
+        polyreg8(py, segLen, y, a);
+        break;
+      case 9:
+        /* 9th order detrending */
+        polyreg9(px, segLen, x, a);
+        polyreg9(py, segLen, y, a);
+        break;
+      case 10:
+        /* 10th order detrending */
+        polyreg10(px, segLen, x, a);
+        polyreg10(py, segLen, y, a);
+        break;
+    }
+    
+    /* Go over all samples in this segment */
+    rxsum = ixsum = 0.0;
+    rysum = iysum = 0.0;
+    for (jj=0; jj<segLen; jj++) {
+      ct     = (*cr);
+      st     = (*ci);
+      p      = x[jj];
+      rxsum += ct * p; /* cos term */
+      ixsum += st * p; /* sin term */
+      p      = y[jj];
+      rysum += ct * p; /* cos term */
+      iysum += st * p; /* sin term */
+      
+      /* increment pointers */
+      cr++;
+      ci++;
+    }
+    /*mexPrintf("   xsum=(%g +i %g), ysum=(%g + i%g)\n", rxsum, ixsum, rysum, iysum);*/
+    
+    /* Average XX and YY power
+     * XXsum  += rxsum*rxsum + ixsum*ixsum;
+     * YYsum  += rysum*rysum + iysum*iysum;
+     * XYRsum += rysum*rxsum + iysum*ixsum;
+     * XYIsum += iysum*rxsum - rysum*ixsum; */
+    
+    /* Welford's algorithm to update mean and variance for cross-power */
+    if (ii == 0) {
+      /* for XY  */
+      MXYr = rysum*rxsum + iysum*ixsum;
+      MXYi = iysum*rxsum - rysum*ixsum;
+      /* for XX  */
+      MXX = rxsum*rxsum + ixsum*ixsum;
+      /* for YY  */
+      MYY = rysum*rysum + iysum*iysum;
+    } else {
+      /* for XY cross - power */
+      XYr = rysum*rxsum + iysum*ixsum;
+      XYi = iysum*rxsum - rysum*ixsum;
+      QXYr = XYr - MXYr;
+      QXYi = XYi - MXYi;
+      MXYr += QXYr/ii;
+      MXYi += QXYi/ii;
+      /* new Qs, using new mean */
+      QXYrn = XYr - MXYr;
+      QXYin = XYi - MXYi;
+      /* taking abs to get real variance */
+      MXY2 += sqrt(pow(QXYr * QXYrn - QXYi * QXYin, 2) + pow(QXYr * QXYin + QXYi * QXYrn, 2));
+      /* for XX  */
+      XX = rxsum*rxsum + ixsum*ixsum;
+      QXX = XX - MXX;
+      MXX += QXX/ii;
+      MXX2 += QXX*(XX - MXX);
+      /* for YY  */
+      YY = rysum*rysum + iysum*iysum;
+      QYY = YY - MYY;
+      MYY += QYY/ii;
+      MYY2 += QYY*(YY - MYY);      
+    }
+    
+  }
+  
+  /* mexPrintf("     start: %f \t istart: %d | %d \n", start, istart, nData-istart);*/
+  /*mexPrintf(" Rsum=%g, Isum=%g\n", Rsum, Isum);*/
+  
+  /* clean up */
+  mxFree(y);
+  mxFree(x);
+  mxFree(a);
+  
+  /* Outputs */
+  *Pxyr = MXYr;
+  *Pxyi = MXYi;
+  if(navg == 1){
+    *Vr = MXYr*MXYr; /* set to mean^2 here, but at the end returns Inf in the ao.dy field */
+  } else {
+    *Vr = MXY2/(navg-1);
+  }
+  *Pxx  = MXX;
+  *Pyy  = MYY;      /* we don't return variance for XX and YY  */
+  *Navs = navg;
+}
+
+/*
+ * Fast linear detrending routine
+ *
+ */
+void remove_linear_drift(double *segm, double *data, int nfft) {
+  int i;
+  long double sx, sy, stt, sty, a, b, xm;
+  
+  sx  = (long double) nfft *(long double) (nfft - 1) / 2.0L;
+  xm  = (long double) (nfft - 1) / 2.0L;
+  stt = ((long double) nfft * (long double) nfft * (long double) nfft - (long double)	 nfft) / 12.0L;
+  
+  sy=sty = 0;
+  for (i = 0; i < nfft; i++) {
+    sy += data[i];
+    sty += (i-xm) * data[i];
+  }
+  b = sty / stt;
+  a = (sy - sx * b) / nfft;
+  for (i = 0; i < nfft; i++) {
+    segm[i] = data[i] - (a + b * i);
+  }
+}
+
+