home *** CD-ROM | disk | FTP | other *** search
- /***************************************************************************
- * 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: okspctrm.c
- * Purpose: OK! Do an fft spectrum analysis
- * Author: Charles Peterson (CPP)
- * History: 24-July-1993 CPP; Created.
- * Comment:
- *
- * As used here, a 'spectrum analysis' is one which starts with real
- * samples, and ends up with real magnitudes of some sort (possibly with
- * some sort of 'windowing' applied to the input which may be analyzed
- * in 'segments' which might be padded and/or overlapped, and possibly
- * with normalization applied to the output). The output magnitudes may
- * either be a 'power' spectrum or an 'amplitude' or 'voltage' spectrum
- * (the latter being the positive square root of the former.) *
- *
- * (Ordinary fft, in contrast, may begin with complex input values and
- * usually returns complex output values. Usually the input to an
- * ordinary fft is not segmented, and doing so may present additional
- * problems. Most frequently, ordinary fft is actually part of some
- * larger problem, such as convolution or spectrum analysis.)
- *
- * This function has gotten a little out of hand, primarily because of
- * all the possible combinations of options that are applied here.
- * Possibly 'Interleave' might be left out next time unless someone
- * notes a use for it. (It didn't turn out to be as useful as I
- * expected. When curves made with different amounts of interleave are
- * spliced together, they don't meet, even in 'PSDensity' mode.) Likewise
- * for 'Pad,' an option which I've never seen do anything but make
- * matters much worse. But somehow, some people seem to think that it's
- * useful, or at least harmless. It isn't either, in my experience.
- * Leaving out those two options would reduce this size of this function
- * substantially.
- *
- * But, don't expect any such streamlining to make a signficant (or
- * even measureable) difference in performance. My lprof tests indicate
- * that this entire beast takes less than 1% of the total time under
- * typical conditions. Considering that there is real work going on
- * here--even if you don't use the more esoteric options--that is quite
- * reasonable. (As expected, cfft takes around 70% of the time under
- * typical conditions, which isn't unreasonable either.)
- *
- * Anyway, if you want to see my best programming and comment writing,
- * go to cfft.c, the function which actually does the fft, though it
- * has gotten harrier since I added an accelerated mode.
- *
- */
-
- #define USE_MEMMOVE
- /* Also uses MEMCPY, where applicable */
- /* Measured with lprof, only about 0.3% improvement overall. */
- /* But, even with 1 bin, didn't make matters worse either. */
-
- #include <time.h> /* time and difftime */
- #include <string.h> /* memmove and memcpy */
-
- #include "gfft.h"
- #include "settings.h"
-
- double LoopTimeElapsed = 0.0;
-
- static BIN_TYPE *bins = NULL;
- static ULONG number_bins = 0;
- static int segment_count = 0;
-
- void do_re_output (void)
- {
- if (bins && number_bins && segment_count)
- {
- ok_write (bins, (long) number_bins, (long) segment_count);
- }
- else
- {
- error_message (CANT_RE_OUTPUT);
- RAISE_ERROR (NOTHING_SPECIAL); /* longjmp outa here */
- }
- }
-
-
- ULONG ok_spectrum (BOOLEAN do_it_for_real)
- {
- static float *indata = NULL;
- static float *overlapdata = NULL;
- static float *interleavedata = NULL; /* e.g. Aseg Bseg Aseg Bseg ... */
- ULONG actually_read = 0;
- ULONG number_samples = 1;
- ULONG number_interleave_samples = 1;
- ULONG half_samples = 0;
- int more_data = TRUE;
- int error_in_loop = FALSE;
- int overlap = Overlap; /* May be changed if overlap impossible */
- int last_partial_segment_used = FALSE;
- ULONG loop_count = 0;
- ULONG i;
- ULONG j;
- ULONG ai;
- ULONG aj;
- time_t time_beginning;
- time_t time_ending;
-
- number_bins = 0;
-
- Sample_Sum_Of_Squares = 0.0;
- Sample_Frame_Count = 0;
-
- if (NumberBins == MAX_BINS)
- {
- ULONG total_actually_read;
- total_actually_read = ok_read (NULL, NULL);
- if (!total_actually_read)
- {
- error_message (NO_DATA_PRESENT);
- RAISE_ERROR (NOTHING_SPECIAL); /* longjmp outa here! */
- }
- else
- {
- ULONG next_power;
- ULONG total_in_leaf = total_actually_read / Interleave;
- if (!total_in_leaf)
- {
- error_message (INSUFFICIENT_DATA);
- RAISE_ERROR (NOTHING_SPECIAL); /* longjmp outa here! */
- }
- next_power = get_pos_power_2 (total_in_leaf);
- if (Pad || next_power == total_in_leaf)
- {
- number_bins = next_power / 2; /* pad if required*/
- }
- else
- {
- number_bins = next_power / 4; /* truncate if required */
- }
- number_samples = number_bins * 2 * Interleave;
- bins_d_message (total_actually_read, number_bins);
- }
- }
- else
- {
- number_bins = (ULONG) NumberBins;
- if (number_bins > 0)
- {
- number_samples = number_bins * 2 * Interleave;
- }
- else
- {
- number_samples = 1;
- }
- }
- /*
- * Now we have number_samples and number_bins
- */
- if (!do_it_for_real) return number_bins;
-
- segment_count = 0;
-
- time_beginning = time (NULL);
-
- half_samples = number_samples / 2;
- if (number_samples < 2)
- {
- /*
- * Can't overlap segments of length 1
- */
- overlap = FALSE;
- }
- if (bins)
- {
- gfree (bins);
- }
- if (indata)
- {
- gfree (indata);
- }
- if (overlapdata)
- {
- gfree (overlapdata);
- overlapdata = NULL;
- }
- if (interleavedata)
- {
- gfree (interleavedata);
- interleavedata = NULL;
- }
-
- /*
- * Note that there is one extra bin for 0 Hz; bins must be pre-zeroed
- */
- bins = gcalloc ((number_bins + 1), (long) sizeof(BIN_TYPE),
- NOTHING_SPECIAL);
- indata = gmalloc (number_samples * sizeof(float),
- NOTHING_SPECIAL);
- if (overlap)
- {
- overlapdata = gmalloc (number_samples * sizeof(float),
- NOTHING_SPECIAL);
- }
- if (Interleave > 1)
- {
- number_interleave_samples = number_samples / Interleave;
- if (!number_interleave_samples)
- {
- error_message (INSUFFICIENT_DATA);
- RAISE_ERROR (NOTHING_SPECIAL); /* longjmp outa here! */
- }
- interleavedata = gmalloc (number_interleave_samples * sizeof (float),
- NOTHING_SPECIAL);
- }
-
- /*
- * Now that these preliminaries are out of the way, we can
- * begin looping over input data
- */
- time_beginning = time (NULL);
-
- while (more_data)
- {
- actually_read = ok_read (indata, number_samples);
- if (!actually_read)
- {
- more_data = FALSE;
- if (!loop_count)
- {
- error_message (NO_DATA_PRESENT);
- error_in_loop = TRUE;
- }
- break;
- }
- if (actually_read < number_samples) /* Deal with partial segment */
- {
- more_data = FALSE;
- if (overlap && loop_count)
- {
- for (i = 0, j = actually_read; j < number_samples;
- i++, j++)
- {
- overlapdata[i] = overlapdata[j];
- }
- for (j = 0; j < actually_read; i++, j++)
- {
- overlapdata[i] = indata[j];
- }
- if (Interleave > 1) /* Overlap and Interleave */
- {
- for (ai = 0; ai < Interleave; ai++)
- {
- float *a_data = interleavedata;
- for (aj = ai; aj < number_samples; aj += Interleave)
- {
- *a_data++ = overlapdata[aj];
- }
- ok_window (interleavedata, number_interleave_samples);
- ok_rfft (interleavedata, number_interleave_samples);
- ok_sigma (interleavedata, bins, number_bins);
- segment_count++;
- }
- }
- else /* Overlap only...no Interleave */
- {
- ok_window (overlapdata, number_samples);
- ok_rfft (overlapdata, number_samples);
- ok_sigma (overlapdata, bins, number_bins);
- segment_count++;
- }
- overlap = FALSE; /* overlap buffer now used up */
- last_partial_segment_used = TRUE;
- }
- if (Pad)
- {
- error_message (PADDING_TAILEND);
- for (i = actually_read; i < number_samples; i++)
- {
- indata[i] = 0.0;
- }
- }
- else
- {
- if (!segment_count)
- {
- error_message (INSUFFICIENT_DATA);
- error_in_loop = TRUE;
- }
- /* else if (!last_partial_segment_used)
- {
- error_message (IGNORING_TAILEND);
- } */
- break;
- }
- }
- /*
- * Here, we've got a full segment to work with (padded or not)
- */
- if (overlap)
- {
- if (loop_count) /* Can't process overlap first time around */
- {
- #ifdef USE_MEMMOVE
- memmove (overlapdata, &overlapdata[half_samples],
- half_samples * sizeof (float));
- memcpy (&overlapdata[half_samples], indata,
- half_samples * sizeof (float));
- #else
- for (i = 0, j = half_samples; i < half_samples; i++, j++)
- {
- overlapdata[i] = overlapdata[j];
- }
- for (j = 0; j < half_samples; i++, j++)
- {
- overlapdata[i] = indata[j];
- }
- #endif
- if (Interleave > 1) /* Overlap and Interleave */
- {
- for (ai = 0; ai < Interleave; ai++)
- {
- float *a_data = interleavedata;
- for (aj = ai; aj < number_samples; aj += Interleave)
- {
- *a_data++ = overlapdata[aj];
- }
- ok_window (interleavedata, number_interleave_samples);
- ok_rfft (interleavedata, number_interleave_samples);
- ok_sigma (interleavedata, bins, number_bins);
- segment_count++;
- }
- }
- else /* Overlap only...no Interleave */
- {
-
- ok_window (overlapdata, number_samples);
- ok_rfft (overlapdata, number_samples);
- ok_sigma (overlapdata, bins, number_bins);
- segment_count++;
- }
- }
- #ifdef USE_MEMMOVE
- memcpy (overlapdata, indata, number_samples * sizeof (float));
- #else
- for (i = 0; i < number_samples; i++)
- {
- overlapdata[i] = indata[i];
- }
- #endif
- }
- if (Interleave > 1) /* Regular (unoverlapped) alternation */
- {
- for (ai = 0; ai < Interleave; ai++)
- {
- float *a_data = interleavedata;
- for (aj = ai; aj < number_samples; aj += Interleave)
- {
- *a_data++ = indata[aj];
- }
- ok_window (interleavedata, number_interleave_samples);
- ok_rfft (interleavedata, number_interleave_samples);
- ok_sigma (interleavedata, bins, number_bins);
- segment_count++;
- }
- }
- else
- {
- ok_window (indata, number_samples);
- ok_rfft (indata, number_samples);
- ok_sigma (indata, bins, number_bins);
- segment_count++;
- }
- loop_count++;
- }
- time_ending = time (NULL);
- LoopTimeElapsed = difftime (time_ending, time_beginning);
-
- if (!error_in_loop)
- {
- loop_time_message (LoopTimeElapsed);
- ok_write (bins, number_bins, segment_count);
- }
- gfree (indata);
- indata = NULL;
- /* gfree (bins); save for ReOut */
- /* bins = NULL; */
- if (overlapdata)
- {
- gfree (overlapdata);
- overlapdata = NULL;
- }
- if (interleavedata)
- {
- gfree (interleavedata);
- interleavedata = NULL;
- }
- if (error_in_loop)
- {
- RAISE_ERROR (NOTHING_SPECIAL); /* longjmp outa here! */
- }
- return number_bins;
- }
-
-
-