/***************************************************************************** Major portions of this software are copyrighted by the Medical College of Wisconsin, 1994-2000, and are released under the Gnu General Public License, Version 2. See the file README.Copyright for details. ******************************************************************************/ /*#define CLIPSECTIONS */ /* Contrary to the good tradition, this .h file will include */ /* function declarations and definitions. */ /* The reason for this is that the plugin has to be a stand alone*/ /* code and I must provide all the code for the functions. */ /* This is just a way of splitting the code into smaller pieces */ /* This file will hold all support functions for the plugin code */ /*-------------------------------------------------------------------*/ /* taken from #include "/usr/people/ziad/Programs/C/Z/Zlib/prototype.h" */ #if defined(SCO) || defined(SOLARIS) #define drem remainder #endif #ifndef NOWAYXCORCOEF #define NOWAYXCORCOEF 0 /* A flag value indicating that something lethal went on */ #endif /* do not change these three*/ #define METH_SECONDS 0 #define METH_DEGREES 1 #define METH_RADIANS 2 static int Read_file (float *x,char *f_name,int n_points); static void linear_reg (float *x,float *y,int size,float *a,float *b,int *err); static int equal_strings (char *s1,char *s2); static int float_file_size (char *f_name); static void error_message (char *origin,char *cause,int ext); static char **allocate2D (int rows,int cols,int element_size); static void free2D(char **a,int rows); static void linear_interp (float *x1,float *y1,float *x2,float *y2,float *x,float *y,int ln); static void float_to_complex (float *x,COMPLEX *xc,int ln); static void c_conj (COMPLEX *x,COMPLEX *y,int ln); static void c_get (COMPLEX *x,float *y,int p,int ln); static void c_scale (COMPLEX *x,COMPLEX *y,float scl,int ln); static void c_padd (COMPLEX *x,COMPLEX *y,COMPLEX pad_val,int ix,int lnx,int lny); static void c_mult (COMPLEX *x,COMPLEX *y,COMPLEX *z,int ln); static void detrend (float *y,float *yd,int lny,float *a,float *b); static void padd (float *x,float *y,float pad_val,int ix,int lnx,int lny); static void hanning (float *x,int l,int opt); static float punwrap (float p,int opt ); static float Lagrange_interp (float *x,float *y,float xi,int ln); static void f_mult (float *x,float *y,float *z,int ln); static int hilbertdelay_V2 (float *x,float *y,int lng_full,int Nseg,int Pover,\ int opt,int dtrnd, float Dtx, int biasrem,\ float *del,float *slp,float *xcor,float *xcorCoef,\ float *vx, float *vy); static void hunwrap (float del, float fs, float T, float slp, int wrp, int unt, float *delu ); static int isarg (int argc, char *argv[], char *probe); static float mean_array (float *ar,int size); static void zeromean (float *x, float *y, int ln ); static void disp_comp_vect (COMPLEX *v,int l); static void disp_vect (float *v,int l); static int is_vect_null ( float * v , int npts ); static int write_float (float *x,char *f_name,int n_points); /* definition and declaration part to suport the main algorithm */ /* -----------------------END-----------------------------------*/ /* -------------------------------------------------------------*/ /* support functions declaration for main algorithm */ /* -----------------------START---------------------------------*/ static void error_message (char *s1,char *s2,int ext) { printf ("\n\n\a\33[1mError: \33[0m%s\n",s2); printf ("\33[1mError origin:\33[0m %s\n\n",s1); if (ext == 1) { printf ("Exiting Program ..\n\n"); exit (0); } else return; } /*-----------------------------------------------------------------------------------*/ /************************************************************************** allocate2D.c - Make matrix of given size (rows x cols) and type The type is given by element_size (2 = ints, 4 = floats, 8 = doubles). Exits if the matrix could not be allocated. char **allocate2D(int rows,int cols,int element_size) SIZE might vary depending on platform used This function was adapted from DSP_in_C library functions Ziad Saad Oct_21_96 *************************************************************************/ static char **allocate2D (int rows,int cols,int element_size) { int i; char **A; /* try to allocate the request */ switch(element_size) { case sizeof(short): { /* integer matrix */ short **int_matrix; int_matrix = (short **)calloc(rows,sizeof(short *)); if(!int_matrix) { printf("\nError making pointers in %dx%d int matrix\n" ,rows,cols); exit(1); } for(i = 0 ; i < rows ; i++) { int_matrix[i] = (short *)calloc(cols,sizeof(short)); if(!int_matrix[i]) { printf("\nError making row %d in %dx%d int matrix\n" ,i,rows,cols); exit(1); } } A = (char **)int_matrix; break; } case sizeof(float): { /* float matrix */ float **float_matrix; float_matrix = (float **)calloc(rows,sizeof(float *)); if(!float_matrix) { printf("\nError making pointers in %dx%d float matrix\n" ,rows,cols); exit(1); } for(i = 0 ; i < rows ; i++) { float_matrix[i] = (float *)calloc(cols,sizeof(float)); if(!float_matrix[i]) { printf("\nError making row %d in %dx%d float matrix\n" ,i,rows,cols); exit(1); } } A = (char **)float_matrix; break; } case sizeof(double): { /* double matrix */ double **double_matrix; double_matrix = (double **)calloc(rows,sizeof(double *)); if(!double_matrix) { printf("\nError making pointers in %dx%d double matrix\n" ,rows,cols); exit(1); } for(i = 0 ; i < rows ; i++) { double_matrix[i] = (double *)calloc(cols,sizeof(double)); if(!double_matrix[i]) { printf("\nError making row %d in %dx%d double matrix\n" ,i,rows,cols); exit(1); } } A = (char **)double_matrix; break; } default: printf("\nERROR in matrix_allocate: unsupported type\n"); exit(1); } return(A); } /*-----------------------------------------------------------------------------------*/ /************************************************************************** free2D.c - Free all elements of matrix Frees the 2D array (rows and cols) allocated using allocate2D Error message and exit if improper structure is passed to it (null pointers or zero size matrix). void free2D(char **a, int rows); This function was adapted from DSP_in_C library functions Ziad Saad Oct_22_96 *************************************************************************/ static void free2D(char **a,int rows) { int i; /* free each row of data */ for(i = 0 ; i < rows ; i++) free(a[i]); /* free each row pointer */ free((char *)a); a = NULL; /* set to null for error */ return; } /*-----------------------------------------------------------------------------------*/ static void hanning (float *x,int l,int opt) { int i; float arg; arg = 2.0*3.14159/(l-1); if (opt == 0) { for (i=0;i lnx+1) { error_message ("padd","ix > lnx+1 !",1); exit(1); } for (i=0;ireal = (float)wrecur_real; xj->imag = (float)wrecur_imag; xj++; wtemp_real = wrecur_real*w_real - wrecur_imag*w_imag; wrecur_imag = wrecur_real*w_imag + wrecur_imag*w_real; wrecur_real = wtemp_real; } } /* start fft */ le = n; windex = 1; for (l = 0 ; l < m ; l++) { le = le/2; /* first iteration with no multiplies */ for(i = 0 ; i < n ; i = i + 2*le) { xi = x + i; xip = xi + le; temp.real = xi->real + xip->real; temp.imag = xi->imag + xip->imag; xip->real = xi->real - xip->real; xip->imag = xi->imag - xip->imag; *xi = temp; } /* remaining iterations use stored w */ wptr = w + windex - 1; for (j = 1 ; j < le ; j++) { u = *wptr; for (i = j ; i < n ; i = i + 2*le) { xi = x + i; xip = xi + le; temp.real = xi->real + xip->real; temp.imag = xi->imag + xip->imag; tm.real = xi->real - xip->real; tm.imag = xi->imag - xip->imag; xip->real = tm.real*u.real - tm.imag*u.imag; xip->imag = tm.real*u.imag + tm.imag*u.real; *xi = temp; } wptr = wptr + windex; } windex = 2*windex; } /* rearrange data by bit reversing */ j = 0; for (i = 1 ; i < (n-1) ; i++) { k = n/2; while(k <= j) { j = j - k; k = k/2; } j = j + k; if (i < j) { xi = x + i; xj = x + j; temp = *xj; *xj = *xi; *xi = temp; } } } /*-----------------------------------------------------------------------------------*/ /************************************************************************** ifft - In-place radix 2 decimation in time inverse FFT Requires pointer to complex array, x and power of 2 size of FFT, m (size of FFT = 2**m). Places inverse FFT output on top of input frequency domain COMPLEX array. void ifft(COMPLEX *x, int m) *************************************************************************/ static void ifft(COMPLEX *x,int m) { static COMPLEX *w; /* used to store the w complex array */ static int mstore = 0; /* stores m for future reference */ static int n = 1; /* length of ifft stored for future */ COMPLEX u,temp,tm; COMPLEX *xi,*xip,*xj,*wptr; int i,j,k,l,le,windex; double arg,w_real,w_imag,wrecur_real,wrecur_imag,wtemp_real; float scale; if(m != mstore) { /* free previously allocated storage and set new m */ if(mstore != 0) free(w); mstore = m; if(m == 0) return; /* if m=0 then done */ /* n = 2**m = inverse fft length */ n = 1 << m; le = n/2; /* allocate the storage for w */ w = (COMPLEX *) calloc(le-1,sizeof(COMPLEX)); if(!w) { printf("\nUnable to allocate complex W array\n"); exit(1); } /* calculate the w values recursively */ arg = 4.0*atan(1.0)/le; /* PI/le calculation */ wrecur_real = w_real = cos(arg); wrecur_imag = w_imag = sin(arg); /* opposite sign from fft */ xj = w; for (j = 1 ; j < le ; j++) { xj->real = (float)wrecur_real; xj->imag = (float)wrecur_imag; xj++; wtemp_real = wrecur_real*w_real - wrecur_imag*w_imag; wrecur_imag = wrecur_real*w_imag + wrecur_imag*w_real; wrecur_real = wtemp_real; } } /* start inverse fft */ le = n; windex = 1; for (l = 0 ; l < m ; l++) { le = le/2; /* first iteration with no multiplies */ for(i = 0 ; i < n ; i = i + 2*le) { xi = x + i; xip = xi + le; temp.real = xi->real + xip->real; temp.imag = xi->imag + xip->imag; xip->real = xi->real - xip->real; xip->imag = xi->imag - xip->imag; *xi = temp; } /* remaining iterations use stored w */ wptr = w + windex - 1; for (j = 1 ; j < le ; j++) { u = *wptr; for (i = j ; i < n ; i = i + 2*le) { xi = x + i; xip = xi + le; temp.real = xi->real + xip->real; temp.imag = xi->imag + xip->imag; tm.real = xi->real - xip->real; tm.imag = xi->imag - xip->imag; xip->real = tm.real*u.real - tm.imag*u.imag; xip->imag = tm.real*u.imag + tm.imag*u.real; *xi = temp; } wptr = wptr + windex; } windex = 2*windex; } /* rearrange data by bit reversing */ j = 0; for (i = 1 ; i < (n-1) ; i++) { k = n/2; while(k <= j) { j = j - k; k = k/2; } j = j + k; if (i < j) { xi = x + i; xj = x + j; temp = *xj; *xj = *xi; *xi = temp; } } /* scale all results by 1/n */ scale = (float)(1.0/n); for(i = 0 ; i < n ; i++) { x->real = scale*x->real; x->imag = scale*x->imag; x++; } } /************************************************************ rfft - trig recombination real input FFT Requires real array pointed to by x, pointer to complex output array, y and the size of real FFT in power of 2 notation, m (size of input array and FFT, N = 2**m). On completion, the COMPLEX array pointed to by y contains the lower N/2 + 1 elements of the spectrum. void rfft(float *x, COMPLEX *y, int m) ***************************************************************/ static void rfft(float *x,COMPLEX *y,int m) { static COMPLEX *cf; static int mstore = 0; int p,num,k,index; float Realsum, Realdif, Imagsum, Imagdif; double factor, arg; COMPLEX *ck, *xk, *xnk, *cx; /* First call the fft routine using the x array but with half the size of the real fft */ p = m - 1; cx = (COMPLEX *) x; fft(cx,p); /* Next create the coefficients for recombination, if required */ num = 1 << p; /* num is half the real sequence length. */ if (m!=mstore){ if (mstore != 0) free(cf); cf = (COMPLEX *) calloc(num - 1,sizeof(COMPLEX)); if(!cf){ printf("\nUnable to allocate trig recomb coefficients."); exit(1); } factor = 4.0*atan(1.0)/num; for (k = 1; k < num; k++){ arg = factor*k; cf[k-1].real = (float)cos(arg); cf[k-1].imag = (float)sin(arg); } } /* DC component, no multiplies */ y[0].real = cx[0].real + cx[0].imag; y[0].imag = 0.0; /* other frequencies by trig recombination */ ck = cf; xk = cx + 1; xnk = cx + num - 1; for (k = 1; k < num; k++){ Realsum = ( xk->real + xnk->real ) / 2; Imagsum = ( xk->imag + xnk->imag ) / 2; Realdif = ( xk->real - xnk->real ) / 2; Imagdif = ( xk->imag - xnk->imag ) / 2; y[k].real = Realsum + ck->real * Imagsum - ck->imag * Realdif ; y[k].imag = Imagdif - ck->imag * Imagsum - ck->real * Realdif ; ck++; xk++; xnk--; } } /*-----------------------------------------------------------------------------------*/ static void float_to_complex (float *x,COMPLEX *xc,int ln) { int i; if ((ln-1) == 0) { (*xc).real = (*x); (*xc).imag = 0.0; return; } else { for (i=0;i lnx+1) { error_message ("c_padd","ix > lnx+1 !",1); exit(1); } for (i=0;i topi/2.0) alf = topi/2.0-alf; return (alf); }/*punwrap*/ /*-----------------------------------------------------------------------------------*/ static float Lagrange_interp (float *x,float *y,float xi,int ln) { int i,j; float yi,p; yi = 0.0; for (i=0;i 0) /* Execution mode */ {/* opt >0 */ #ifdef DEBUG_ZEO_2 printf ("\nFunction call #%d\n",i_call); #endif /*-----------------------------------------------------------------------------------*/ /* Steps that need to be perfromed the first time the function is called */ /*-----------------------------------------------------------------------------------*/ if (i_call == 0) {/*i_call == 0*/ if ((Nseg < 1) || (Nseg >= lng_full/5)) { sprintf (buf,"Number of segments (%d) null or too large, or vector length too small (%d) for %d segments ",Nseg,lng_full,Nseg); error_message ("hilbertdelay_V2",buf,0); return (2); } lng = (int)((float)lng_full / (float)Nseg); /* calculating individual segment length */ maxdel = lng/2; /* delays should not exceed one third of the segment length (and that's pushing it !)*/ m=0; while ((int)pow((double)2,(double)m) < lng) ++m; /* find closest power of two to the length of the series */ olng = lng; /* save old length*/ lng = (int)pow((double)2,(double)m); /* set new length as power of two actual padding length will double*/ /* in order to correct for circular convolution effects */ lng_use = lng; /* useful length of spectrum after correction for circular convolution effects */ #ifdef DEBUG_ZEO_2 printf ("selected m=%d for a padded segment length of %d, old segment length was %d\nVector holds %d segments\n",m,lng,olng,Nseg); #endif fftx = (COMPLEX *) calloc ((2*lng)+2,sizeof(COMPLEX)); fftxc = (COMPLEX *) calloc ((2*lng)+2,sizeof(COMPLEX)); ffty = (COMPLEX *) calloc ((2*lng)+2,sizeof(COMPLEX)); fftyc = (COMPLEX *) calloc ((2*lng)+2,sizeof(COMPLEX)); Pxy = (COMPLEX *) calloc ((2*lng)+2,sizeof(COMPLEX)); Px = (float *) calloc ((2*lng)+2,sizeof(float)); Pxx = (COMPLEX *) calloc ((2*lng)+2,sizeof(COMPLEX)); Py = (float *) calloc ((2*lng)+2,sizeof(float)); Pyy = (COMPLEX *) calloc ((2*lng)+2,sizeof(COMPLEX)); Rxx = (COMPLEX *) calloc ((2*lng)+2,sizeof(COMPLEX)); Ryy = (COMPLEX *) calloc ((2*lng)+2,sizeof(COMPLEX)); Rxy = (float *) calloc ((2*lng)+2,sizeof(float)); HRxy = (float *) calloc ((2*lng)+2,sizeof(float)); xcpy = (float *) calloc ((2*olng)+2,sizeof(float)); ycpy = (float *) calloc ((2*olng)+2,sizeof(float)); xp = (float *) calloc ((2*lng)+2,sizeof(float)); yp = (float *) calloc ((2*lng)+2,sizeof(float)); ubias = (float *) calloc ((2*lng)+2,sizeof(float)); tmp_f_vect = (float *) calloc ((2*lng)+2,sizeof(float)); tmp_f_vect2 = (float *) calloc ((2*lng)+2,sizeof(float)); fftyca = (COMPLEX **) allocate2D ((2*lng)+2,Nseg,sizeof(COMPLEX)); Pxya = (COMPLEX **) allocate2D ((2*lng)+2,Nseg,sizeof(COMPLEX)); Ryya = (COMPLEX **) allocate2D ((2*lng)+2,Nseg,sizeof(COMPLEX)); Rxxa = (COMPLEX **) allocate2D ((2*lng)+2,Nseg,sizeof(COMPLEX)); if (fftx == NULL || fftxc == NULL || ffty == NULL || fftyc == NULL || \ Pxy == NULL || Px == NULL || Py == NULL || xp == NULL || yp == NULL || \ ubias == NULL || tmp_f_vect == NULL || Pxx == NULL || Pyy == NULL || \ Rxx == NULL || Ryy == NULL || fftyca == NULL || Pxya == NULL || \ Ryya == NULL || Rxxa == NULL || tmp_f_vect2 == NULL) { printf ("\nFatal Error : Failed to Allocate memory\a\n"); printf ("Abandon Lab Immediately !\n\n"); return(2); }; /* creating a vector to remove the bowtie artifact from the auto and cross correlation curves, and set to zero their irrelevant values */ if (biasrem == 1) { for (i=0;i<(2*lng);++i) { if (i < olng) { ubias[i] = (float)olng / (float)(olng - i); } else { ubias[i] = 0.0; } } } a = (float)olng; /* Scaling parameter for spectral estimates */ strt = 0; /* setting up for segments loop */ nd = 0; for (sg=0;sg 0 */ Ryy[i].real = Ryy[i].real + Ryya[i][sg].real; Ryy[i].imag = Ryy[i].imag + Ryya[i][sg].imag; }/* sg > 0 */ }/* for i */ }/* for sg */ c_scale (Ryy,Ryy,1.0/((float)Nseg * a),2*lng); /* scaling periodogram */ ifft (Ryy,m+1); /* calculating autocorrelation of y*/ }/*i_call == 0*/ /*-----------------------------------------------------------------------------------*/ /* Steps that need to be repeated each time the function is called with a new vector */ /*-----------------------------------------------------------------------------------*/ strt = 0; /* setting up for segments loop */ nd =0; for (sg=0;sg maxdel) {/* i > maxdel */ /*sprintf (buf,"Delay larger than 1/2 segment length (%d)",maxdel); error_message ("hilbertdelay_V2",buf,0);*/ return (3); }/* i > maxdel */ if (HRxy[i] == 0.0) {/* HRxy[i] == 0.0 */ if (Rxy[i] > 0.0) *slp = 1.0; else *slp = -1.0; izero = (float) i; *xcor = Rxy[i]; /* storing value of max covariance */ i=lng; }/* HRxy[i] == 0.0 */ else {/* HRxy[i] != 0.0 */ if ((HRxy[i] * HRxy[i+1]) < 0.0) { /* the sign of slp was used to determine the sign of the cross correlation */ /* the sign of slp should be the same as that of Rxy[i] close to izero */ /* moreover, I think the use of the sign of Rxy[i] is better since with no */ /*subtraction performed, I am less sensitive to high freq. noise */ if (Rxy[i] >= 0.0) *slp = 1.0; else *slp = -1.0; if ((*slp > 0.0)) { f_i = (float) (i); f_i1 = (float) (i+1); reg_pnt = 0.0; linear_interp (&HRxy[i],&f_i,&HRxy[i+1],&f_i1,®_pnt,&izero,1); /* find the peak of the cross correlation curve */ k = 0; for (j=-3;j<=3;++j) { if (((i+j) >= 0) && ((i+j) < lng)) { tmp_f_vect[k] = (float) (i+j); tmp_f_vect2[k] = Rxy[i+j]; ++k; } } if (k > 1) {/* at least a 1st order interpolation must be performed */ *xcor = Lagrange_interp (tmp_f_vect,tmp_f_vect2,izero,k); } i = lng; } } }/* HRxy[i] != 0.0 */ }/* for i */ *del = izero + Dtx; /* delay is in sample units corrected by the sampling time difference*/ if (Rxx[0].real && Ryy[0].real) *xcorCoef = *xcor / sqrt (Rxx[0].real * Ryy[0].real) * *slp; /*correction for sign of cross correlation coefficient (slp = 1.0 or -1.00*/ else { #ifdef DEBUG_ZEO_3 printf ("\nZero Variance...\n"); #endif } /* set vx and vy */ *vx = Rxx[0].real; *vy = Ryy[0].real; ++i_call; return (0); }/* opt > 0 */ /* Execution mode */ else if (opt == 0) {/* opt == 0 */ #ifdef DEBUG_ZEO_3 printf ("\nCleaning Up...\n"); #endif free (fftx); /*Cleaning up used space*/ free (fftxc); free (ffty); free (fftyc); free (Px); free (Py); free (Pxx); free (Pyy); free (Pxy); free (Rxx); free (Ryy); free (Rxy); free (HRxy); free (xp); free (yp); free (ubias); free (xcpy); free (ycpy); free (tmp_f_vect); free (tmp_f_vect2); free2D ((char **)fftyca,lng+2); free2D ((char **)Pxya,lng+2); free2D ((char **)Rxxa,lng+2); free2D ((char **)Ryya,lng+2); i_call = 0; *del = NoWayDelay; /* setting variables to out of bound values for safety */ *xcorCoef = NoWayxcorCoef; return (0); }/* opt == 0 */ return(0) ; } /*-----------------------------------------------------------------------------------*/ static void hunwrap (float del, float fs, float T, float slp, int wrp, int unt, float *delu ) {/*hunwrap*/ float pi = 3.1416, tmp; if (fs > 0.0) { /* delay should be in seconds */ del = del / fs; } if (T > 0.0) {/* Period unwrapping possible */ if (slp < 0.0) {/* Unwrapping required to determine correct delay */ tmp = del * 360.0 / T; /* from time to polar angle */ tmp = punwrap (tmp,1); /* unwrap */ tmp = tmp + 180.0; /* augment by pi */ del = tmp * T / 360.0; /* from polar to time */ } /* Now for the case where we get negative delays although no wrapping has been done, namely because of the sampling time correction. */ if (del < 0.0 || del > T) { tmp = del * 360.0 / T; /* from time to polar angle */ if (del < 0.0) { tmp = tmp + 360.0; } else { if (del > T) tmp = tmp - 360.0; } del = tmp * T / 360.0; /* from polar to time */ } if (wrp == 1) {/* map of (0-pi) to (0-pi) and (pi-2pi) to (pi-0) */ tmp = del * 360.0 / T; /* from time to polar angle */ tmp = punwrap (tmp,1); /* unwrap */ del = tmp * T / 360.0; /* from polar to time */ }/* map of (0-pi) to (0-pi) and (pi-2pi) to (pi-0) */ if (unt == METH_DEGREES) del = del * 360.0 / T; /* from time to polar angle in degrees*/ if (unt == METH_RADIANS) del = del * 2 * pi / T; /* from time to polar angle in degrees*/ }/* Period unwrapping possible */ *delu = del; return; }/*hunwrap*/ /*-----------------------------------------------------------------------------------*/ static void disp_comp_vect (COMPLEX *v,int l) { int i; printf ("\n"); if ((l-1) == 0) { printf ("V = (%f,%f)\n",(*v).real,(*v).imag); } else { for (i=0;i