/* 
 * pvffft.c
 *
 * Original code by ulrich@Gaston.westfalen.de (Heinz Ulrich Stille),
 * badly hacked around in by me. Thank him for the good idea, blame me
 * for the bugs... 
 *
 */

char pvffft_c[] = "$Id: pvffft.c,v 1.8 1995/03/29 18:27:16 marc Exp $";

#include <stdio.h>

#ifndef _NOSTDLIB_H
#include <stdlib.h>
#endif

#include <math.h>
#include <signal.h>

#include "../ugly.h"
#include "pvflib.h"

static void
fft _P3((real, imag, n), float* real, float *imag, int n)
{
    float a, tr, ti, wr, wi;
    int mr=0;
    int l, istep, m, i, j;
    
    for (m = 1; m <= n-1; m++) {
	l = n / 2;
	while (mr + l > n-1)
	    l /= 2;
	mr = (mr % l) + l;
	if (mr > m) {
	    tr = real[m];
	    real[m] = real[mr];
	    real[mr] = tr;
	    ti = imag[m];
	    imag[m] = imag[mr];
	    imag[mr] = ti;
        }
    }
    for (l = 1; l < n; l = istep) {
	istep = 2 * l;
	for (m = 1; m <= l; m++) {
	    a = -M_PI * (m - 1.0) / l;
	    wr = cos(a);
	    wi = sin(a);
	    i = m - 1;
	    do {
		j = i + l;
		tr = wr * real[j] - wi * imag[j];
		ti = wr * imag[j] + wi * real[j];
		real[j] = real[i] - tr;
		imag[j] = imag[i] - ti;
		real[i] += tr;
		imag[i] += ti;
		i += istep;
            } while (i < n);
        }
    }
}

int /* is_data */
pvffft _P2((argc, argv), int argc, char **argv )
/* Read pvf data from a pipe, transform it into amplitude data and
 * compute an index for the distribution of energy over the
 * frequencies.
 *
 * Return true if it is a data call, i.e. the computed index is less
 * than a given threshold.
 *
 * If there isn't enough data to do the fft, return false.
 */
{
    FILE *in=stdin;
    int verbose = 0;
    int n, skip;
    int pid = -1;
    int i;
    float val, sum, max;
    float *real, *imag;
    double thresh;
    
    while (argc>1 && argv[1][0] == '-') {
	if (strcmp(argv[1], "-info")==0) {
	    verbose = 1;
	    argc--; argv++;
	} else if (argc>2 && strcmp(argv[1], "-kill")==0) {
	    pid = atoi(argv[2]);
	    argc-=2; argv+=2;
	}
    }
    if (argc != 4) USAGE("[-info] [-kill <PID>] <skip> <sample_size> <threshold>");
    skip = atoi(argv[1]);
    n = atoi(argv[2]);
    thresh = atof(argv[3]);

    for (i=n; i>0; i>>=1) {
	if ((i&1) && i!=1) ERRORRETURN("sample size must be a power of 2");
    }

    real=malloc((n+8)*sizeof(float));
    imag=malloc((n+8)*sizeof(float));

    for(i=0; i<n; i++) real[i]=imag[i]=0;

    if (!real || !imag) ERRORRETURN("not enough memory");

    /* skip the first few seconds */
    for (i = 0; i < skip; i++) zget(in);

    /* store a few seconds' worth of data */
    for (i=0; i < n; i++) {
	if (feof(in)) {
	    /* assume it isn't data */
	    if(verbose) fprintf(stderr, "FFT level not computed\n");
	    return 0;
	}
	real[i] = (float)zget(in);
    }

    /* tell calling process that it can stop writing */
    if (pid>=0) kill(pid, SIGPIPE);
    /* note that it will get another sigpipe once this is
     * really finished - a close won't do since other processes
     * (i.e. the calling shell) will still have the fd opened. */

    fft(real, imag, n);
    
    sum = 0;
    max = 0;
    
    for (i = 1; i < n; i++) {
	val = abs(real[i]) + abs(imag[i]);
	sum += val;
	if (val > max) max = val;
    }
    sum = sum*1000/n;
    if (max>1e-10) sum/=max;
    
    if(verbose) fprintf(stderr, "FFT level: %g\n", sum);

    if (pid>=0 && sum < thresh) 
	kill(pid, SIGUSR2);

    return (sum < thresh);
}
