Metropoli BBS
VIEWER: okwrite.c MODE: TEXT (ASCII)
/***************************************************************************
 *		  Copyright (C) 1994  Charles P. Peterson                  *
 *	     4007 Enchanted Sun, San Antonio, Texas 78244-1254             *
 *              Email: Charles_P_Peterson@fcircus.sat.tx.us                *
 *                                                                         *
 *		  This is free software with NO WARRANTY.                  *
 *	      See gfft.c, or run program itself, for details.              *
 *		      Support is available for a fee.                      *
 ***************************************************************************
 *
 * Program:     gfft--General FFT analysis
 * File:        okwrite.c
 * Purpose:     Write the results of a spectrum analysis
 * Author:      Charles Peterson (CPP)
 * History:     23-August-1993 CPP; Created.
 *              6-Aug-1994 CPP (1.10); Add spectrum file identifier #FFT
 *              28-Aug-94 CPP (1.12); fix LowFrequency for FFP
 *              7-Feb-95 CPP (1.31); Progress Requester
 *              13-Feb-95 CPP (1.40); Header on spectrum files (moved to OK)
 *
 * Comment:     This sure has gotten complicated.  Sorry.
 */

/* #define PARSEVAL_DEBUG */

#define PROGRESS_INCREMENT 128

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

#ifdef _AMIGA
#ifdef _M68881
#include <m68881.h>
#endif
#endif

#include "gfft.h"
#include "settings.h" /* OkRate, WritePtr, Mean, Amplitude */
#include "errcodes.h"
#include "wbench.h"   /* progress requester */

double LowestFrequencyWritten = 0;
double HighestFrequencyWritten = 0;

void ok_write (BIN_TYPE *bins, long number_bins, long number_segments)
{
    long i;
    double frequency;
    FINAL_TYPE value;
/*    double two_doubles[2]; */
    double nyquist_frequency = OkRate / (2.0L * Interleave);
    double delta_frequency = 0;
    double value_divisor_temp = 1.0 * number_segments;
    FINAL_TYPE multiply = Multiply;
    FINAL_TYPE value_divisor;
    double *mesh;
    FINAL_TYPE smoothing_value_sum = 0.0L;
    double smoothing_frequency_sum = 0.0L;
    FINAL_TYPE save_value;
    double save_frequency;
    double test_frequency;
    long mesh_index = 0;
    long smoothing_count = 0;
    BOOLEAN first_db_of_zero = TRUE;
    double sum_of_bins;
    extern double Ending_Progress;
    double first_write_progress = 100.0 - Ending_Progress;
    double incremental_progress_divisor = number_bins / Ending_Progress;

    if (number_segments == 0) return;

/*
 * reset index used internally in calibration
 * because we're starting from lowest frequency (again, maybe)
 */
    calibration_list__reset (&CalibrationList);

/*
 * If 0 bins (i.e., the DC one only)
 * avoid divide by zero problems
 */
    if (number_bins)
    {
	delta_frequency = nyquist_frequency / number_bins;
	mesh  = ok_mesh (nyquist_frequency, delta_frequency);

	if (Mean)
	{
	/*
         * Mean normalization for power or amplitude.
	 * See Press, et. al, page 439.
	 * To get mean, we'll have to divide by N^2, then by number_segments
         * Note: N is number_bins * 2 if number_bins > 0, thus
	 *  (number_bins * 2)^2 == 4 * number_bins^2
         * 
         * READ THIS: if Parseval option selected (now default)
	 *  we recompute this later using Parseval's theorem.  The
	 *  results are remarkably similar...showing that I WAS doing
	 *  the correct thing here.  The new Parseval method is
	 *  more accurate, but the old method is 2.5% faster overall
	 *  because the squaring and summation in ok_read is not needed.
         */
	    value_divisor_temp = (((4.0 * number_bins) * number_bins) * 
			    number_segments);
	}
	else /* !Mean */
	{

/*          value_divisor_temp = 2.0 * number_bins; */

/*
 * We correct for overlapping bins, by multiplying by extra factor
 * (number_segments * <segment size> / Sample_Frame_Count)
 */

/*	    value_divisor_temp = 2.0 * number_bins * number_segments *
	    2.0 * number_bins / Sample_Frame_Count */
	    
/*
 * which simplifies to the following:
 */

	    value_divisor_temp = 4.0 * number_bins * number_bins *
	      number_segments / Sample_Frame_Count;
	}
    }

    if (WindowType)
/*
 * Special extra correction if non-square window is used
 * NOTE: as currently done, this is an estimate
 * based on window's mean squared average gain...
 * Parseval method works better.
 */
    {
	value_divisor_temp *= ok_window__gain2();
    }

/*
 * This is now where the normalization normally gets done
 * Using Parseval's theorem, i.e.
 *   sum of power in the time domain equals sum of power in freq domain
 */
    if (Parseval)
    {
	sum_of_bins = bins[0];
	if (number_bins > 0)
	{
	    sum_of_bins += bins[number_bins];
	}
	for (i = 1; i <= number_bins-1; i++)
	{
	    sum_of_bins += bins[i] * 2.0;
	}

	if (Mean && Sample_Sum_Of_Squares != 0.0)
	{

#ifdef PARSEVAL_DEBUG
	fprintf (stderr,"Sample MSS: %g, Bin MSS: %g, Old Divisor: %g\n",
		 Sample_Sum_Of_Squares / Sample_Frame_Count,
		 sum_of_bins / value_divisor_temp,
		 value_divisor_temp);
#endif

	    value_divisor_temp = sum_of_bins * Sample_Frame_Count /
	      Sample_Sum_Of_Squares;

#ifdef PARSEVAL_DEBUG
	fprintf (stderr,"Corrected Sample Bin MSS: %g\n",
		 sum_of_bins / value_divisor_temp);
#endif

	}
	else if (!Mean && Sample_Sum_Of_Squares != 0.0)
	{
#ifdef PARSEVAL_DEBUG
	    fprintf (stderr,"Original divisor: %g\n",value_divisor_temp);
#endif

	    value_divisor_temp = sum_of_bins / Sample_Sum_Of_Squares;

#ifdef PARSEVAL_DEBUG
       fprintf (stderr,
		"Post correction: Sample SS: %g, Bin SS: %g, Divisor: %g\n",
		Sample_Sum_Of_Squares,
		sum_of_bins / value_divisor_temp,
		value_divisor_temp);
#endif

	}
	/* else...all's zeros anyway */
    }

    if (PSDensity)
    {
	/*
	 * (Re-)Adjust for PSDensity:
	 * The effect of this is to allow you to splice together curves
	 * computed with different numbers of bins, which is useful in some
	 * applications.
	 *
	 * Ok, we could multiply back in an extra N,
	 * but, I've chosen to divide by the bin size
	 * giving more meaningful units of (.../Hz) or something like that,
	 * I think...
	 */
	value_divisor_temp *= delta_frequency;
    }


    value_divisor = value_divisor_temp;  /* reduce to final precision */

    LowestFrequencyWritten = -1.0;
/*
 * Thanks to bug in FFP handing of DBL_MIN, LOWEST_FREQUENCY is now -1
 * and I have to do this extra stuff
 */
    if (LowFrequency == LOWEST_FREQUENCY)
    {
	i = 1;
    }
    else
    {
	i = 0;
    }
    for (; i <= number_bins; i++)
    {
	if (i % PROGRESS_INCREMENT == 1 && !Time3D)
	{
	    progress_requester__update 
	      ((int) (first_write_progress + 
		      (i / incremental_progress_divisor)));
	}

	frequency = delta_frequency * i;  /* Note 888 below if changing */

	if (frequency < LowFrequency)
	{
	    continue;
	}
	if (frequency > HighFrequency)
	{
	    continue;
	}

	value = bins[i];

	if (i != 0 && i != number_bins)
	{
	    value *= 2.0;    /* Correct for one-sidedness */
	}

	value /= value_divisor;

	if (Pink)
	{
	    if (frequency == 0)
	    {
		continue;
	    }
        /*
         * The following normalizes the spectrum values to about what
         * would be reported by an octave band spectrum analyzer.
         *  ** Warning ** This is a guess, not known to be correct!
         */
	    value *= ((frequency + delta_frequency) / delta_frequency);
	}

	if (Amplitude)
	{
	    value = sqrt (value);
	}

	if (multiply != 1.0)
	{
	    value *= multiply;
	}

	if (mesh && frequency > 0.0L)
	{
	    if (frequency > mesh[mesh_index] && smoothing_count == 0)
	    {
		while (frequency > mesh[++mesh_index]);  /* Catch up */
	    }
	    if (frequency < mesh[mesh_index])
	    {
		smoothing_value_sum += (SquaredSmoothing) ?
		  value * value : value;
		smoothing_frequency_sum += frequency;
		smoothing_count++;
		continue;  /* Nothing to write this time */
	    }
	    else if (frequency == mesh[mesh_index])
	    {
		/* Set up value and frequency for this write */
		smoothing_value_sum += (SquaredSmoothing) ?
		  value * value : value;
		smoothing_frequency_sum += frequency;
		value = smoothing_value_sum / ++smoothing_count;
		if (SquaredSmoothing) value = sqrt (value);
		frequency = smoothing_frequency_sum / smoothing_count;

		/* Set up for next pass */
		smoothing_value_sum = 0.0;
		smoothing_frequency_sum = 0.0;
		smoothing_count = 0;
		mesh_index++;
	    }
	    else if (frequency > mesh[mesh_index])
	    {
		/* Set up value and frequency for this write */
		save_value = value;
		save_frequency = frequency;
		value = smoothing_value_sum / smoothing_count;
		if (SquaredSmoothing) value = sqrt (value);
		frequency = smoothing_frequency_sum / smoothing_count;

		/* Set up for next pass */
		smoothing_value_sum = (SquaredSmoothing) ?
		  save_value * save_value : save_value;
		smoothing_frequency_sum = save_frequency;
		smoothing_count = 1;
		test_frequency = (i+1) * delta_frequency; /*Note 888 above*/
		if (test_frequency > mesh[++mesh_index])
		{   /* Catch up for next pass */
		    while (test_frequency > mesh[++mesh_index]);
		}
	    }
	}		
	if (Db)
	{
	    if (value > 0.0)
	    {
		value = ((Amplitude) ? 20 : 10) * log10 (value);
	    }
	    else
	    {
		if (first_db_of_zero)
		{
		    error_message (DB_OF_ZERO);
		    first_db_of_zero = FALSE;
		}
		continue;  /* Just skip (unless user decides not to) */
	    }
	}

	if (CalibrationList)
	{
	    CATCH_ERROR
	    {
		value = calibration_list__apply (&CalibrationList, 
						 value, frequency);
	    }
	    ON_ERROR
	    {
		if (first_db_of_zero)
		{
		    error_message (DB_OF_ZERO);
		    first_db_of_zero = FALSE;
		}
		continue;  /* Just skip (unless user decides not to) */
	    }
	    END_CATCH_ERROR;
	} /* end if CalibrationList */

	if (Quantization != MIN_QUANTIZATION)
	{
	    long factor = value / Quantization;
	    double value1 = (factor-1) * Quantization;
	    double value2 = factor * Quantization;
	    double value3 = (factor+1) * Quantization;
	    double delta1 = value - value1;
	    double delta2 = fabs (value - value2);
	    double delta3 = value3 - value;
	    if (delta2 <= delta1 && delta2 <= delta3)
	    {
		value = value2;
	    }
	    else if (delta1 <= delta3)
	    {
		value = value1;
	    }
	    else
	    {
		value = value3;
	    }
	}

	if (LowestFrequencyWritten < 0) LowestFrequencyWritten = frequency;
	if (!OutputFormat.binary)
	{
	    if (Time3D)
	    {
#ifdef _FFP
		fprintf (WritePtr, "%-15.8g %-15.8g %-13.7g\n",
			 frequency, OkSegmentTime, value);
#else
		fprintf (WritePtr, "%-19.12g %-19.12g %-13.7g\n", 
			 frequency, OkSegmentTime, value);
#endif
	    }
	    else
	    {
#ifdef _FFP
		fprintf (WritePtr, "%-15.8g %-13.7g\n", frequency, value);
#else
		fprintf (WritePtr, "%-19.12g %-13.7g\n", frequency, value);
#endif
	    }
	}
/*	else  ** This didn't work...need to figure binary formats out **
 *	{
 *	    two_doubles[0] = frequency;
 *	    two_doubles[1] = value;
 *	    fwrite (two_doubles, sizeof(double), (size_t) 2, WritePtr);
 *	}
 */	    
    }
    if (mesh) gfree (mesh);
    if (Time3D) fprintf (WritePtr, "\n");
    HighestFrequencyWritten = frequency;
}

      
[ RETURN TO DIRECTORY ]