Mercurial > hg > ltpda
comparison 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 |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:f0afece42f48 |
---|---|
1 /* | |
2 * Mex file that implements the core DFT part of the LPSD algorithm. | |
3 * | |
4 * M Hewitson 15-01-08 | |
5 * | |
6 * $Id: ltpda_dft.c,v 1.16 2009/08/18 10:53:27 hewitson Exp $ | |
7 */ | |
8 | |
9 | |
10 #include <math.h> | |
11 #include <stdio.h> | |
12 #include <stdlib.h> | |
13 #include <string.h> | |
14 #include <mex.h> | |
15 | |
16 #include "ltpda_dft.h" | |
17 #include "version.h" | |
18 #include "../c_sources/polyreg.c" | |
19 | |
20 #define DEBUG 0 | |
21 | |
22 | |
23 /* | |
24 * Rounding function | |
25 * | |
26 */ | |
27 int myround(double x) { | |
28 return ((int) (floor(x + 0.5))); | |
29 } | |
30 | |
31 | |
32 /* | |
33 * Matlab mex file to make a dft at a single frequency | |
34 * | |
35 * Inputs | |
36 * - data | |
37 * - seg length | |
38 * - DFT coefficients | |
39 * - overlap (%) | |
40 * - order of detrending | |
41 * | |
42 * function [P, navs] = ltpda_dft(x, seglen, DFTcoeffs, olap, order); | |
43 * | |
44 */ | |
45 void mexFunction( int nlhs, mxArray *plhs[], | |
46 int nrhs, const mxArray *prhs[]) { | |
47 /* Parse inputs */ | |
48 if( (nrhs == 0) && (nlhs == 0) ) { | |
49 print_usage(VERSION); | |
50 return; | |
51 } | |
52 else if ( (nrhs == 5) && (nlhs == 3) ) /* let's go */ { | |
53 double Pr, Vr; | |
54 double *xdata, *Cr, *Ci; | |
55 double olap; | |
56 long int nData; | |
57 long int nSegs; | |
58 long int segLen; | |
59 int order; | |
60 double *ptr; | |
61 | |
62 if( !mxIsComplex(prhs[2]) ) | |
63 mexErrMsgTxt("DFT coefficients must be complex.\n"); | |
64 | |
65 /* Extract inputs */ | |
66 xdata = mxGetPr(prhs[0]); /* Pointer to data */ | |
67 nData = mxGetNumberOfElements(prhs[0]); /* Number of data points */ | |
68 segLen = (int)mxGetScalar(prhs[1]); /* Segment length */ | |
69 Cr = mxGetPr(prhs[2]); /* Real part of DFT coefficients */ | |
70 Ci = mxGetPi(prhs[2]); /* Imag part of DFT coefficients */ | |
71 olap = mxGetScalar(prhs[3]); /* Overlap percentage */ | |
72 order = (int)mxGetScalar(prhs[4]); /* Order of detrending */ | |
73 | |
74 if (order > 10 || order < -1) | |
75 mexErrMsgTxt("Detrending order must be between -1 and 10"); | |
76 | |
77 /*mexPrintf("Input data: %d samples\n", nData);*/ | |
78 /*mexPrintf("Segment length: %d\n", segLen);*/ | |
79 /*mexPrintf("Overlap: %f\n", olap);*/ | |
80 | |
81 /* Compute DFT */ | |
82 dft(&Pr, &Vr, &nSegs, xdata, nData, segLen, Cr, Ci, olap, order); | |
83 | |
84 /* Set output matrices */ | |
85 plhs[0] = mxCreateDoubleMatrix(1, 1, mxCOMPLEX); | |
86 plhs[1] = mxCreateDoubleMatrix(1, 1, mxCOMPLEX); | |
87 plhs[2] = mxCreateDoubleMatrix(1, 1, mxCOMPLEX); | |
88 | |
89 /* Get pointers to output matrices */ | |
90 ptr = mxGetPr(plhs[0]); | |
91 ptr[0] = Pr; | |
92 ptr = mxGetPr(plhs[1]); | |
93 ptr[0] = Vr; | |
94 ptr = mxGetPr(plhs[2]); | |
95 ptr[0] = nSegs; | |
96 | |
97 } | |
98 else if ( (nrhs == 6) && (nlhs == 5) ) /* let's go */ { | |
99 double Mr, Mi, XX, YY, M2; | |
100 double *xdata, *ydata, *Cr, *Ci; | |
101 double olap; | |
102 long int nData, nxData, nyData; | |
103 long int nSegs; | |
104 long int segLen; | |
105 int order; | |
106 double *ptr; | |
107 | |
108 if( !mxIsComplex(prhs[3]) ) | |
109 mexErrMsgTxt("DFT coefficients must be complex.\n"); | |
110 | |
111 /* Extract inputs */ | |
112 xdata = mxGetPr(prhs[0]); /* Pointer to data */ | |
113 ydata = mxGetPr(prhs[1]); /* Pointer to data */ | |
114 nxData = mxGetNumberOfElements(prhs[0]); /* Number of data points */ | |
115 nyData = mxGetNumberOfElements(prhs[0]); /* Number of data points */ | |
116 segLen = (int)mxGetScalar(prhs[2]); /* Segment length */ | |
117 Cr = mxGetPr(prhs[3]); /* Real part of DFT coefficients */ | |
118 Ci = mxGetPi(prhs[3]); /* Imag part of DFT coefficients */ | |
119 olap = mxGetScalar(prhs[4]); /* Overlap percentage */ | |
120 order = (int)mxGetScalar(prhs[5]); /* Order of detrending */ | |
121 | |
122 if (order > 10 || order < -1) | |
123 mexErrMsgTxt("Detrending order must be between -1 and 10"); | |
124 | |
125 if (nxData != nyData) | |
126 mexErrMsgTxt("The two input data vector should be the same length."); | |
127 | |
128 nData = nxData; | |
129 | |
130 /*mexPrintf("Input data: %d samples\n", nData);*/ | |
131 /*mexPrintf("Segment length: %d\n", segLen);*/ | |
132 /*mexPrintf("Overlap: %f\n", olap);*/ | |
133 | |
134 /* Compute DFT */ | |
135 xdft(&Mr, &Mi, &XX, &YY, &M2, &nSegs, xdata, ydata, nData, segLen, Cr, Ci, olap, order); | |
136 | |
137 /* Set output matrices */ | |
138 plhs[0] = mxCreateDoubleMatrix(1, 1, mxCOMPLEX); | |
139 plhs[1] = mxCreateDoubleMatrix(1, 1, mxREAL); | |
140 plhs[2] = mxCreateDoubleMatrix(1, 1, mxREAL); | |
141 plhs[3] = mxCreateDoubleMatrix(1, 1, mxREAL); | |
142 plhs[4] = mxCreateDoubleMatrix(1, 1, mxREAL); | |
143 | |
144 /* Get pointers to output matrices */ | |
145 ptr = mxGetPr(plhs[0]); | |
146 ptr[0] = Mr; | |
147 ptr = mxGetPi(plhs[0]); | |
148 ptr[0] = Mi; | |
149 ptr = mxGetPr(plhs[1]); | |
150 ptr[0] = XX; | |
151 ptr = mxGetPr(plhs[2]); | |
152 ptr[0] = YY; | |
153 ptr = mxGetPr(plhs[3]); | |
154 ptr[0] = M2; | |
155 ptr = mxGetPr(plhs[4]); | |
156 ptr[0] = nSegs; | |
157 | |
158 | |
159 } | |
160 else /* we have an error */ { | |
161 print_usage(VERSION); | |
162 mexErrMsgTxt("### incorrect usage"); | |
163 } | |
164 } | |
165 | |
166 /* | |
167 * Output usage to MATLAB terminal | |
168 * | |
169 */ | |
170 void print_usage(char *version) { | |
171 mexPrintf("ltpda_dft version %s\n", version); | |
172 mexPrintf(" usage: function [P, navs] = ltpda_dft(x, seglen, DFTcoeffs, olap, order); \n"); | |
173 } | |
174 | |
175 /* | |
176 * Short routine to compute the DFT at a single frequency | |
177 * | |
178 */ | |
179 void dft(double *Pr, double *Vr, long int *Navs, | |
180 double *xdata, long int nData, long int segLen, double *Cr, double *Ci, double olap, int order) { | |
181 long int istart; | |
182 double shift, start; | |
183 double *px, *cr, *ci; | |
184 double rxsum, ixsum; | |
185 double Xr, Mr, M2, Qr; | |
186 double p, *x, *a; | |
187 long int jj, ii; | |
188 | |
189 | |
190 /* Compute the number of averages we want here */ | |
191 double ovfact = 1. / (1. - olap / 100.); | |
192 double davg = ((double) ((nData - segLen)) * ovfact) / segLen + 1; | |
193 long int navg = myround(davg); | |
194 | |
195 /* Compute steps between segments */ | |
196 if (navg == 1) | |
197 shift = 1; | |
198 else | |
199 shift = (double) (nData - segLen) / (double) (navg - 1); | |
200 | |
201 if (shift < 1) | |
202 shift = 1; | |
203 | |
204 /* mexPrintf("Seglen: %d\t | Shift: %f\t | navs: %d\n", segLen, shift, navg);*/ | |
205 | |
206 /* allocate vectors */ | |
207 x = (double*)mxCalloc(segLen, sizeof(double)); /* detrending output */ | |
208 a = (double*)mxCalloc(order+1, sizeof(double)); /* detrending coefficients */ | |
209 | |
210 /* Loop over segments */ | |
211 start = 0.0; | |
212 Xr = 0.0; | |
213 Qr = 0.0; | |
214 Mr = 0.0; | |
215 M2 = 0.0; | |
216 | |
217 for (ii = 0; ii < navg; ii++) { | |
218 /* compute start index */ | |
219 istart = myround(start); | |
220 start += shift; | |
221 | |
222 /* pointer to start of this segment */ | |
223 px = &(xdata[istart]); | |
224 | |
225 /* pointer to DFT coeffs */ | |
226 cr = &(Cr[0]); | |
227 ci = &(Ci[0]); | |
228 | |
229 /* Detrend segment */ | |
230 switch (order) { | |
231 case -1: | |
232 /* no detrending */ | |
233 memcpy(x, px, segLen*sizeof(double)); | |
234 break; | |
235 case 0: | |
236 /* mean removal */ | |
237 polyreg0(px, segLen, x, a); | |
238 break; | |
239 case 1: | |
240 /* linear detrending */ | |
241 polyreg1(px, segLen, x, a); | |
242 break; | |
243 case 2: | |
244 /* 2nd order detrending */ | |
245 polyreg2(px, segLen, x, a); | |
246 break; | |
247 case 3: | |
248 /* 3rd order detrending */ | |
249 polyreg3(px, segLen, x, a); | |
250 break; | |
251 case 4: | |
252 /* 4th order detrending */ | |
253 polyreg4(px, segLen, x, a); | |
254 break; | |
255 case 5: | |
256 /* 5th order detrending */ | |
257 polyreg5(px, segLen, x, a); | |
258 break; | |
259 case 6: | |
260 /* 6th order detrending */ | |
261 polyreg6(px, segLen, x, a); | |
262 break; | |
263 case 7: | |
264 /* 7th order detrending */ | |
265 polyreg7(px, segLen, x, a); | |
266 break; | |
267 case 8: | |
268 /* 8th order detrending */ | |
269 polyreg8(px, segLen, x, a); | |
270 break; | |
271 case 9: | |
272 /* 9th order detrending */ | |
273 polyreg9(px, segLen, x, a); | |
274 break; | |
275 case 10: | |
276 /* 10th order detrending */ | |
277 polyreg10(px, segLen, x, a); | |
278 break; | |
279 } | |
280 | |
281 /* Go over all samples in this segment */ | |
282 rxsum = ixsum = 0.0; | |
283 for (jj=0; jj<segLen; jj++) { | |
284 p = x[jj]; | |
285 rxsum += (*cr) * p; /* cos term */ | |
286 ixsum += (*ci) * p; /* sin term */ | |
287 | |
288 /* increment pointers */ | |
289 cr++; | |
290 ci++; | |
291 } | |
292 /*mexPrintf(" xsum=(%g +i %g), ysum=(%g + i%g)\n", rxsum, ixsum, rysum, iysum);*/ | |
293 | |
294 /* Average the cross-power | |
295 * Rsum += rxsum*rxsum + ixsum*ixsum; | |
296 */ | |
297 | |
298 /* Welford's algorithm to update mean and variance */ | |
299 if (ii == 0) { | |
300 Mr = (rxsum*rxsum + ixsum*ixsum); | |
301 } else { | |
302 Xr = (rxsum*rxsum + ixsum*ixsum); | |
303 Qr = Xr - Mr; | |
304 Mr += Qr/ii; | |
305 M2 += Qr * (Xr - Mr); | |
306 } | |
307 | |
308 } | |
309 | |
310 /* mexPrintf(" start: %f \t istart: %d | %d \n", start, istart, nData-istart);*/ | |
311 /*mexPrintf(" Rsum=%g, MR=%g \n", Rsum,MR); */ | |
312 | |
313 /* clean up */ | |
314 mxFree(x); | |
315 mxFree(a); | |
316 | |
317 /* Outputs */ | |
318 *Pr = Mr; | |
319 if(navg == 1){ | |
320 *Vr = Mr*Mr; | |
321 } else { | |
322 *Vr = M2/(navg-1); | |
323 } | |
324 *Navs = navg; | |
325 } | |
326 | |
327 /* | |
328 * Short routine to compute the cross-DFT at a single frequency | |
329 * | |
330 */ | |
331 void xdft(double *Pxyr, double *Pxyi, double *Pxx, double *Pyy, double *Vr, long int *Navs, | |
332 double *xdata, double *ydata, long int nData, long int segLen, double *Cr, double *Ci, double olap, int order) { | |
333 long int istart; | |
334 double shift, start; | |
335 double *px, *py, *cr, *ci; | |
336 double rxsum, ixsum, rysum, iysum; | |
337 double XYr, XYi, QXYr, QXYi, QXYrn, QXYin; | |
338 double MXYr, MXYi, MXY2; | |
339 double XX, YY, QXX, QYY; | |
340 double MXX, MYY, MXX2, MYY2; | |
341 double p, *x, *y, *a; | |
342 double ct, st; | |
343 long int jj, ii; | |
344 | |
345 /* Compute the number of averages we want here */ | |
346 double ovfact = 1. / (1. - olap / 100.); | |
347 double davg = ((double) ((nData - segLen)) * ovfact) / segLen + 1; | |
348 long int navg = myround( davg ); | |
349 | |
350 /* Compute steps between segments */ | |
351 if (navg == 1) | |
352 shift = 1; | |
353 else | |
354 shift = (double) (nData - segLen) / (double) (navg - 1); | |
355 | |
356 if (shift < 1) | |
357 shift = 1; | |
358 | |
359 /* mexPrintf("Seglen: %d\t | Shift: %f\t | navs: %d\n", segLen, shift, navg); */ | |
360 | |
361 /* allocate vectors */ | |
362 y = (double*)mxCalloc(segLen, sizeof(double)); /* detrending output */ | |
363 x = (double*)mxCalloc(segLen, sizeof(double)); /* detrending output */ | |
364 a = (double*)mxCalloc(order+1, sizeof(double)); /* detrending coefficients */ | |
365 | |
366 /* Loop over segments */ | |
367 start = 0.0; | |
368 MXYr = 0.0; | |
369 MXYi = 0.0; | |
370 MXY2 = 0.0; | |
371 MXX = 0.0; | |
372 MYY = 0.0; | |
373 MXX2 = 0.0; | |
374 MYY2 = 0.0; | |
375 | |
376 for (ii = 0; ii < navg; ii++) { | |
377 /* compute start index */ | |
378 istart = myround(start); | |
379 start += shift; | |
380 | |
381 /* pointer to start of this segment */ | |
382 px = &(xdata[istart]); | |
383 py = &(ydata[istart]); | |
384 | |
385 /* pointer to DFT coeffs */ | |
386 cr = &(Cr[0]); | |
387 ci = &(Ci[0]); | |
388 | |
389 /* Detrend segment */ | |
390 switch (order) { | |
391 case -1: | |
392 /* no detrending */ | |
393 memcpy(x, px, segLen*sizeof(double)); | |
394 memcpy(y, py, segLen*sizeof(double)); | |
395 break; | |
396 case 0: | |
397 /* mean removal */ | |
398 polyreg0(px, segLen, x, a); | |
399 polyreg0(py, segLen, y, a); | |
400 break; | |
401 case 1: | |
402 /* linear detrending */ | |
403 polyreg1(px, segLen, x, a); | |
404 polyreg1(py, segLen, y, a); | |
405 break; | |
406 case 2: | |
407 /* 2nd order detrending */ | |
408 polyreg2(px, segLen, x, a); | |
409 polyreg2(py, segLen, y, a); | |
410 break; | |
411 case 3: | |
412 /* 3rd order detrending */ | |
413 polyreg3(px, segLen, x, a); | |
414 polyreg3(py, segLen, y, a); | |
415 break; | |
416 case 4: | |
417 /* 4th order detrending */ | |
418 polyreg4(px, segLen, x, a); | |
419 polyreg4(py, segLen, y, a); | |
420 break; | |
421 case 5: | |
422 /* 5th order detrending */ | |
423 polyreg5(px, segLen, x, a); | |
424 polyreg5(py, segLen, y, a); | |
425 break; | |
426 case 6: | |
427 /* 6th order detrending */ | |
428 polyreg6(px, segLen, x, a); | |
429 polyreg6(py, segLen, y, a); | |
430 break; | |
431 case 7: | |
432 /* 7th order detrending */ | |
433 polyreg7(px, segLen, x, a); | |
434 polyreg7(py, segLen, y, a); | |
435 break; | |
436 case 8: | |
437 /* 8th order detrending */ | |
438 polyreg8(px, segLen, x, a); | |
439 polyreg8(py, segLen, y, a); | |
440 break; | |
441 case 9: | |
442 /* 9th order detrending */ | |
443 polyreg9(px, segLen, x, a); | |
444 polyreg9(py, segLen, y, a); | |
445 break; | |
446 case 10: | |
447 /* 10th order detrending */ | |
448 polyreg10(px, segLen, x, a); | |
449 polyreg10(py, segLen, y, a); | |
450 break; | |
451 } | |
452 | |
453 /* Go over all samples in this segment */ | |
454 rxsum = ixsum = 0.0; | |
455 rysum = iysum = 0.0; | |
456 for (jj=0; jj<segLen; jj++) { | |
457 ct = (*cr); | |
458 st = (*ci); | |
459 p = x[jj]; | |
460 rxsum += ct * p; /* cos term */ | |
461 ixsum += st * p; /* sin term */ | |
462 p = y[jj]; | |
463 rysum += ct * p; /* cos term */ | |
464 iysum += st * p; /* sin term */ | |
465 | |
466 /* increment pointers */ | |
467 cr++; | |
468 ci++; | |
469 } | |
470 /*mexPrintf(" xsum=(%g +i %g), ysum=(%g + i%g)\n", rxsum, ixsum, rysum, iysum);*/ | |
471 | |
472 /* Average XX and YY power | |
473 * XXsum += rxsum*rxsum + ixsum*ixsum; | |
474 * YYsum += rysum*rysum + iysum*iysum; | |
475 * XYRsum += rysum*rxsum + iysum*ixsum; | |
476 * XYIsum += iysum*rxsum - rysum*ixsum; */ | |
477 | |
478 /* Welford's algorithm to update mean and variance for cross-power */ | |
479 if (ii == 0) { | |
480 /* for XY */ | |
481 MXYr = rysum*rxsum + iysum*ixsum; | |
482 MXYi = iysum*rxsum - rysum*ixsum; | |
483 /* for XX */ | |
484 MXX = rxsum*rxsum + ixsum*ixsum; | |
485 /* for YY */ | |
486 MYY = rysum*rysum + iysum*iysum; | |
487 } else { | |
488 /* for XY cross - power */ | |
489 XYr = rysum*rxsum + iysum*ixsum; | |
490 XYi = iysum*rxsum - rysum*ixsum; | |
491 QXYr = XYr - MXYr; | |
492 QXYi = XYi - MXYi; | |
493 MXYr += QXYr/ii; | |
494 MXYi += QXYi/ii; | |
495 /* new Qs, using new mean */ | |
496 QXYrn = XYr - MXYr; | |
497 QXYin = XYi - MXYi; | |
498 /* taking abs to get real variance */ | |
499 MXY2 += sqrt(pow(QXYr * QXYrn - QXYi * QXYin, 2) + pow(QXYr * QXYin + QXYi * QXYrn, 2)); | |
500 /* for XX */ | |
501 XX = rxsum*rxsum + ixsum*ixsum; | |
502 QXX = XX - MXX; | |
503 MXX += QXX/ii; | |
504 MXX2 += QXX*(XX - MXX); | |
505 /* for YY */ | |
506 YY = rysum*rysum + iysum*iysum; | |
507 QYY = YY - MYY; | |
508 MYY += QYY/ii; | |
509 MYY2 += QYY*(YY - MYY); | |
510 } | |
511 | |
512 } | |
513 | |
514 /* mexPrintf(" start: %f \t istart: %d | %d \n", start, istart, nData-istart);*/ | |
515 /*mexPrintf(" Rsum=%g, Isum=%g\n", Rsum, Isum);*/ | |
516 | |
517 /* clean up */ | |
518 mxFree(y); | |
519 mxFree(x); | |
520 mxFree(a); | |
521 | |
522 /* Outputs */ | |
523 *Pxyr = MXYr; | |
524 *Pxyi = MXYi; | |
525 if(navg == 1){ | |
526 *Vr = MXYr*MXYr; /* set to mean^2 here, but at the end returns Inf in the ao.dy field */ | |
527 } else { | |
528 *Vr = MXY2/(navg-1); | |
529 } | |
530 *Pxx = MXX; | |
531 *Pyy = MYY; /* we don't return variance for XX and YY */ | |
532 *Navs = navg; | |
533 } | |
534 | |
535 /* | |
536 * Fast linear detrending routine | |
537 * | |
538 */ | |
539 void remove_linear_drift(double *segm, double *data, int nfft) { | |
540 int i; | |
541 long double sx, sy, stt, sty, a, b, xm; | |
542 | |
543 sx = (long double) nfft *(long double) (nfft - 1) / 2.0L; | |
544 xm = (long double) (nfft - 1) / 2.0L; | |
545 stt = ((long double) nfft * (long double) nfft * (long double) nfft - (long double) nfft) / 12.0L; | |
546 | |
547 sy=sty = 0; | |
548 for (i = 0; i < nfft; i++) { | |
549 sy += data[i]; | |
550 sty += (i-xm) * data[i]; | |
551 } | |
552 b = sty / stt; | |
553 a = (sy - sx * b) / nfft; | |
554 for (i = 0; i < nfft; i++) { | |
555 segm[i] = data[i] - (a + b * i); | |
556 } | |
557 } | |
558 | |
559 |