[comp.lang.c] Numerical Recipes in C: problems with cross-correlation code

trejo@nprdc.arpa (Leonard J. Trejo) (08/22/89)

We are experiencing problems with the subroutine "correl()" on page
433 of "Numerical Recipes in C" by Press et al.  Specifically, we find
that the correlation function returned by a program using this
subroutine and the supporting subroutines (vector(), twofft(),
realft(), free_vector()) is incorrect.  For example, the correlation of
two identical cosine bursts is asymmetrical.  

If other have detected problems with this code, please let us know
about it by e-mail.

I'm attaching a copy of the program we wrote to this message.
It attempts to correlate two functions in ascii files "mask" and 
"wave" and puts the result in "ans".

				L. J. T.
===============================================================================

/*  Maria 8/10/89  */
/*  This program takes bogus files <data1> and <data2>,  */
/*  cross-correlates them, and stores the corr. values   */
/*  in bogus file <ans>.                                 */

#include <stdio.h>
#include <fcntl.h>
#include <math.h>

/*  for function four1....  */
#define SWAP(a,b) tempr=(a);(a)=(b);(b)=tempr

FILE *ptrdata1, *ptrdata2, *ptrout;
/* file pointers to <data1>, <data2>, and <ans> */ 


/*  ##### BEGIN function free_vector #####  */
/*  Frees a float vector allocated by vector().         */ 

void free_vector(v,nl,nh)

float *v;
int nl,nh;

{
	free((char*) (v+nl));
}
/*  ##### END function free_vector #####  */


/* ##### BEGIN function four1 #####  */
/*  Replaces data by its discrete Fourier transform,    */
/*  if isign is input as 1; or replaces data by nn      */
/*  times its inverse discrete Fourier transform, if    */
/*  isign is input as -1.  data is a complex array of   */
/*  length nn, input as a real array data[1..2*nn].     */
/*  nn MUST be an integer power of 2 (this is not       */
/*  checked for!!!!!).                                  */

void four1(data,nn,isign)

float data[];
int nn,isign;

{
	int n,mmax,i,m,j,istep;
	float tempr,tempi;

	double wtemp,wr,wpr,wpi,wi,theta;
	/*  double precision for the trigonometric recurrences.  */

	n = nn << 1;
	j = 1;

	/*  This is the bit-reversal section of the routine.  */
	for (i = 1; i < n; i += 2) {
		if (j>i) {
		/*  Exchange the two complex numbers.  */
			SWAP(data[j],data[i]);
			SWAP(data[j+1],data[i+1]);
		}
		m = n >> 1;
		while (m >= 2 && j > m) {
			j -= m;
			m >>= 1;
		}
		j += m;
	}

	/*  Here begins the Danielson-Lanczos section of the routine.  */
	mmax = 2;
	while (n > mmax) {
	/*  Outer loop executed log(base 2) nn times.  */
		istep = 2*mmax;

		/*  Initialize for the trigonometric recurrence.  */
		theta = 6.28318530717959/(isign*mmax);

		wtemp = sin(0.5*theta);
		wpr = -2.0*wtemp*wtemp;
		wpi = sin(theta);
		wr = 1.0;
		wi = 0.0;

		/*  Here are the two nested inner loops.  */
		for (m = 1; m < mmax; m += 2) {
			for (i = m; i <= n; i += istep) {

			/*  This is the Danielson_Lanczos:  */
				j = i+mmax;
				tempr = wr*data[j]-wi*data[j+1];
				tempi = wr*data[j+1]+wi*data[j];
				data[j] = data[i]-tempr;
				data[j+1] = data[i+1]-tempi;
				data[i] += tempr;
				data[i+1] += tempi;
			}

			/*  Trigonometric recurrence.  */
			wr = (wtemp = wr)*wpr-wi*wpi+wr;
			wi = wi*wpr+wtemp*wpi+wi;
		}
		mmax = istep;
	}
}
/*  ##### END function four1 #####  */


/*  ##### BEGIN function realft ##### */
/*  This procedure calculates the Fourier Transform      */
/*  of a set of 2n real-valued data points.              */
 
void realft(data,n,isign)

float data[];
int n, isign;

{ 
	int i,i1,i2,i3,i4,n2p3;
	float c1=0.5,c2,h1r,h1i,h2r,h2i;

	double wr,wi,wpr,wpi,wtemp,theta;
	/*  double precision for the trigonometric recurrences  */
	
	void four1();

	/*  initialize the recurrence  */
	theta=3.141592653589793/(double) n;
	if (isign == 1) {
		/*  the forward transform is here  */
		c2 = -0.5;
		four1(data,n,1);
	} else {
		/*  otherwise set up for an inverse transform  */
		c2 = 0.5; 
	theta = -theta;
	}
	wtemp = sin(0.5 * theta);
	wpr = -2.0 * wtemp * wtemp;
	wpi = sin(theta);
	wr = 1.0 + wpr;
	wi = wpi;
	n2p3 = 2 * n + 3;
	for ( i = 2; i <= n/2; i++) {
	/*  case i=1 done separately below  */
		i4 = 1+(i3 = n2p3-(i2 = 1+(i1 = i+i-1)));

		/*  The two separate transforms are  */
		/*  separated out of data.  */
		h1r = c1*(data[i1]+data[i3]);
		h1i = c1*(data[i2]-data[i4]);
		h2r = -c2*(data[i2]+data[i4]);
		h2i = c2*(data[i1]-data[i3]);

		/*  Here they are recombined to form the true  */
		/*  transform of the original real data.  */
		data[i1] = h1r+wr*h2r-wi*h2i;
		data[i2] = h1i+wr*h2i+wi*h2r;
		data[i3] = h1r-wr*h2r+wi*h2i;
		data[i4] = -h1i+wr*h2i+wi*h2r;

		/*  The recurrence.  */
		wr = (wtemp = wr)*wpr-wi*wpi+wr;
		wi = wi*wpr+wtemp*wpi+wi;
	}
	if (isign == 1) {
	/*  squeeze the first and last data together  */
	/*  to get them all in the original array.  */ 
		data[1] = (h1r = data[1]+data[2]);
		data[2] = h1r-data[2];
	} else {
		data[1] = c1*((h1r = data[1])+data[2]);
		data[2] = c1*(h1r-data[2]);

		/*  This is the inverse transform for the case isign=-1  */
		four1(data,n,-1);
	}
}
/*  ##### END function realft #####  */


/*  ##### BEGIN function twofft #####  */
/*  Given two real input arrays data[1..n] and          */
/*  data2[1..n], this routine calls four1 and returns   */
/*  two complex output arrays, fft1 and fft2, each of   */
/*  complex length n (ie. real dimensions [1..2n], which*/
/*  contain the discrete Fourier transforms of the      */
/*  respective datas.  n MUST be an integer power of 2. */
							 
void twofft(data1,data2,fft1,fft2,n)

float data1[],data2[],fft1[],fft2[];
int n;

{
	int nn3,nn2,jj,j;
	float rep,rem,aip,aim;
	
	void four1();

	nn3 = 1+(nn2 = 2+n+n);
	for ( j = 1, jj = 2; j <= n; j++, jj += 2) {
	/*  Pack the two real arrays into one complex array.  */
		fft1[jj-1] = data1[j];
		fft1[jj] = data2[j];
	}

	/*  Transform the complex array.  */
	four1(fft1,n,1);
	fft2[1] = fft1[2];
	fft1[2] = fft2[2] = 0.0;
	for (j = 3; j <= n+1; j += 2) {
	/*  Use symmetries to separate the two transforms.  */
		rep = 0.5*(fft1[j]+fft1[nn2-j]);
		rem = 0.5*(fft1[j]-fft1[nn2-j]);
		aip = 0.5*(fft1[j+1]+fft1[nn3-j]);
		aim = 0.5*(fft1[j+1]-fft1[nn3-j]);

	 	/*  Ship them out in the complex arrays.  */
		fft1[j] = rep;
		fft1[j+1] = aim;
		fft1[nn2-j] = rep;
		fft1[nn3-j] = -aim;
		fft2[j] = aip;
		fft2[j+1] = -rem;
		fft2[nn2-j] = aip;
		fft2[nn3-j] = rem;
	}
}
/*  ##### END function twofft #####  */


/*  ##### BEGIN function nrerror #####  */
/*  The procedure  is the numerical recipes standard error handler  */

void nrerror(error_text)

char error_text[];

{
	void exit();
	
	fprintf(stderr,"Numerical Recipes run-time error...\n");
	fprintf(stderr,"%s\n",error_text);
	fprintf(stderr,"...now exiting to system...\n");
	exit(1);
}
/*  ##### END function nrerror #####  */


/*  ##### BEGIN function *vector #####  */
/*  This procedure allocates a float vector with range  */
/*  [nl..nh]                                            */

float *vector(nl,nh)

int nl,nh;

{
	float *v;

 	v = (float *)malloc((unsigned) (nh-nl+1)*sizeof(float));
	if (!v) nrerror("allocation failure in vector()");
	return v-nl;
}
/*  ##### END function *vector #####  */

							
/*  ##### BEGIN function correl #####  */
/*  This procedure computes the correlation of the two  */
/*  real data sets data1[1..n] and data2[1..n] each of  */
/*  length n (including any user supplied padding).     */
/*    ******* n MUST be a power of 2!! *******          */
/*  The answer is returned as the first n points in     */
/*  ans[1..2*n] stored in wraparound order, ie.         */
/*  correlations at increasingly negative lags are in   */
/*  ans[n] on down to ans[n/2+1], while correlations    */
/*  increasingly positive lags are in and[1] on up to   */
/*  ans[n/2].                                           */ 

void correl(data1,data2,n,ans)

float data1[],data2[],ans[];
int n;

{
	int no2, i;
	float dum,  *fft, *vector();

	void twofft(), realft(), free_vector();

	fft = vector(1,2*n);

        /*  transforms both data vectors at one.  */
	twofft(data1,data2,fft,ans,n);

	/*  normalization for inverse FFT  */
	no2 = n/2;
	
	for (i = 2; i <= n + 2; i += 2) {

                /*  multiply to find FFT of their correlation  */
		ans[i-1] = (fft[i-1]*(dum=ans[i-1])+fft[i]*ans[i])/no2;
		ans[i] = (fft[i]*dum-fft[i-1]*ans[i])/no2;
	}
	/*  pack first and last into one element */
	ans[2] = ans[n+1];

	/*  inverse transform gives correlation  */
	realft(ans,no2,-1);

	free_vector(fft,1,2*n);
}
/*  ##### END function correl #####  */


/*  ##### BEGIN main body #####  */
main (argc, argv)

int argc;
char *argv[];

{
 	float data1[1024],data2[1024],datat[1024],ans[2048],hold=0.0,avg;
	int n,i=0,j,k,bot,top,c1=0,c2=0,bigger,twopwr=2,upper,lower;
	char mask[10], *wave, *out, command[100], c, npts;

	/*  check command line for correct # of args, then   */
	/*  identify the three files: mask, wave and out.    */

	if (argc != 7) {
		printf("Command line has incorrect number of entries!\n");
		exit();
	}
	while (--argc > 0 && (*++argv)[0]== '-') {
		while (c = *++argv[0]) {
			switch(c) {
				case 't':
					strcpy(mask, argv[1]);
					break;
				case 'n':
					npts = atoi(argv[1]);
sprintf(command,"series 0 %d | dm \"sin(x1*6.283/%d)\" > mask",npts,2*npts);
					system(command);
					strcpy(mask,"mask");
					break;
				case 'd':
					wave = argv[1];
					break;
				case 'r':
					out = argv[1];
					break;
				default:
		printf("This is an illegal command: %c\n",c);
					exit();
			}
		}
		++argv;
		--argc;
	}

	/*  open files wave and mask and check to make sure  */
	/*  that they are not NULL files. If they are, exit  */
	/*  this program and report NULL file to screen.     */
	if ((ptrdata1 = fopen(wave,"r")) == NULL) {
		printf("results file can't be opened!\n");
		exit();
	}
	if ((ptrdata2 = fopen(mask,"r")) == NULL) {
		printf("mask file can't be opened!\n"); 
		exit();
	}
	if ((ptrout = fopen(out,"w")) == NULL) {
		printf("results file can't be opened!\n");
		exit();
	}

	/*  Fill array data1 from file wave.  */
	while ( fscanf(ptrdata1,"%f", &data1[c1]) != 0 && !feof(ptrdata1)) {
		c1 ++;
	}
	c1 --;

	/*  Fill array data2 from file mask.  */
	while ( fscanf(ptrdata2,"%f", &data2[c2]) != 0 && !feof(ptrdata2)) {
		c2 ++;
	}
	c2 --;

	/*  Normalize mask.  */
	for ( i = 0;  i <= c2;  i++) {
		hold = hold + data2[i];  
	}
	avg = hold/(c2+1);
	hold=0;
	for ( i = 0; i <= c2; i++) {
		datat[i] = data2[i] - avg;
		hold += datat[i] * data2[i];
	}
	for (i=0; i <= c2; i++) {
		data2[i]=datat[i]/hold;
	}
	
	/*  Find out which array is larger.  */
	if ( c1 >= c2 ) 
		bigger = c1+1;
	else {
		printf("Mask file length cannot exceed ");
		printf("data file length!!!\n");
		exit();
	}	

	/*  Compare the larger array with a power of two  */
	/*  until the smallest power of two that fits the */
	/*  'biggest' array size is found.                */
	/*  Assign that power of two (- 1) to var n.      */
	while ( bigger > twopwr)
		twopwr = twopwr * 2;
	/*   Double array sizes, so convolution is not    */
	/*   circular...but linear.                       */
	n = twopwr * 2;         

	correl(data1,data2,n,ans);

	/*  Move first half of mask to end portion of new */
	/*  mask to remove delay from ans.                */
	/*  Check to see if mask size is even or odd...   */
/*	if (c2 % 2 == 0) { */ 
	/* odd */
/*		upper = c2/2;
		lower = upper - 1;

		for (i=0; i<n; i++)
			datat[i]=0;	
		k = n - upper;
		for (i=0; i<=upper; i++) {
			j=i+(c2/2);
			datat[i]=data2[j];
		}
		for (i=0; i<=lower; i++) {
			datat[k]=data2[i];
			k++;
		}
	}
	else {  */ 
	/* even */
/*		upper = c2/2 + 1;
		lower = upper - 1;

		for (i=0; i<n; i++)
			datat[i]=0;	
		k = n - upper;
		j = 0;
		for (i=upper; i<=c2; i++) {
			datat[j]=data2[i];
			j++;
		}
		for (i=0; i<=lower; i++) {
			datat[k]=data2[i];
			k++;
		}
	}	
	for (i=0; i<n; i++) 
		data2[i]=datat[i];*/	

	for (i = 1; i < n; i++) {
		/*  write array ans to file <ans>  */
		fprintf(ptrout,"%f\n", ans[i]);
	}

	close(out);
}

============================================================================
ARPANET : trejo@nprdc.navy.mil		UUCP:  ucsd!nprdc!trejo

U.S. Mail: Leonard J. Trejo, Ph. D.	Phone: (619) 553-7711
	   Neuroscience Laboratory		(AV) 553-7711
	   NPRDC, Code 141
	   San Diego, CA 92152-6800