home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
OS/2 Shareware BBS: Science
/
Science.zip
/
gmt_os2.zip
/
src
/
spectrum1d.c
< prev
next >
Wrap
C/C++ Source or Header
|
1995-03-13
|
17KB
|
556 lines
/*--------------------------------------------------------------------
* The GMT-system: @(#)spectrum1d.c 2.12 3/13/95
*
* Copyright (c) 1991-1995 by P. Wessel and W. H. F. Smith
* See README file for copying and redistribution conditions.
*--------------------------------------------------------------------*/
/*
*
* spectrum1d <file> [-C] [-D<dt>] [-N<name_stem>] -S<segment_size> [-V] [-W]
*
* Compute auto and cross spectra using Welch's method of
* multiple overlapped windows. Find 1 standard error bars
* following expressions in Bendat & Piersol.
*
* -D<dt> set delta_time, the sampling of the timeseries.
* [Default <dt> = 1.0]
*
* -N<name_stem> name stem for filenames. Files will be
* created called <name_stem>.xpower, etc.
* [Default <name_stem> = "spectrum"]
*
* -S<segment_size> give an integer radix 2 window width.
* Total timeseries will be split into pieces of
* length <segment_size>. Std errors in spectra are
* approximately 1/sqrt(n_data/segment_size).
*
* -V Verbose operation; write info to stderr.
* [Default is silent]
*
* -W write Wavelength in col 1 instead of frequency.
* [Default writes frequency in cycles/dt_units]
*
* -C input has 2 cols, X(t) Y(t); do cross-spectra.
* [Default is one column; do X power spectrum only]
*
* Author: W. H. F. Smith
* Date: 11 April 1991-1995
* Revised: 5 June 1991-1995 to add W and N options in prep for
* GMT v2.0 release.
* References: Julius S. Bendat & Allan G. Piersol,
* "Random Data", 2nd revised edition, 566pp.,
* 1986, John Wiley & Sons, New York.
*
* Peter D. Welch, "The use of Fast Fourier
* Transform for the estimation of power spectra:
* a method based on time averaging over short,
* modified periodograms", IEEE Transactions on
* Audio and Electroacoustics, Vol AU-15, No 2,
* June, 1967.
*/
#include "gmt.h"
float *datac, *x, *y;
struct SPEC {
double xpow; /* PSD in X(t) */
double ypow; /* PSD in Y(t) */
double gain; /* Amplitude increase X->Y in Optimal Response Function */
double phase; /* Phase of Y w.r.t. X in Optimal Response Function */
double coh; /* (squared) Coherence of Y and X; SNR of Y = coh/(1-coh) */
double radmit; /* Real part of Admittance; used e.g., for gravity/topography */
} *spec;
double dt = 1.0, x_variance, y_variance, d_n_windows, y_pow;
int y_given = FALSE, write_wavelength = FALSE;
int n_spec, window = 0, window_2, n_data;
char namestem[80];
FILE *fp = NULL;
void alloc_arrays();
void read_data();
void compute_spectra();
void detrend_and_hanning();
void write_output();
void free_space();
main (argc, argv)
int argc;
char **argv;
{
int i, window_test, error = FALSE;
argc = gmt_begin (argc, argv);
sprintf(namestem, "spectrum\0");
for (i = 1; i < argc; i++) {
if (argv[i][0] == '-') {
switch (argv[i][1]) {
/* Common parameters */
case 'H':
case 'V':
case '\0':
error += get_common_args (argv[i], 0, 0, 0, 0);
break;
/* Supplemental parameters */
case 'C':
y_given = TRUE;
break;
case 'D':
dt = atof(&argv[i][2]);
break;
case 'N':
strcpy(namestem, &argv[i][2]);
if (!namestem[0]) error = TRUE;
break;
case 'S':
window = atoi(&argv[i][2]);
window_test = 2;
while (window_test < window) {
window_test += window_test;
}
break;
case 'W':
write_wavelength = TRUE;
break;
default:
error = TRUE;
gmt_default_error (argv[i][1]);
break;
}
}
else {
if ((fp = fopen(argv[i], "r")) == NULL) {
fprintf(stderr,"spectrum1d: Cannot open r %s\n", argv[i]);
error = TRUE;
}
}
}
if (argc == 1 || gmt_quick) { /* Display usage */
fprintf (stderr, "spectrum1d %s - compute auto- [and cross- ] spectra from one [or two] timeseries\n\n", GMT_VERSION);
fprintf(stderr,"usage: spectrum1d -S<segment_size> [-C] [-D<dt>] [-H] [-N<name_stem>] [-V] [-W]\n");
if (gmt_quick) exit (-1);
fprintf(stderr,"\t-S<segment_size> Use data subsets of <segment_size> elements.\n");
fprintf(stderr,"\t\t<segment_size> must be radix 2; std. err. = 1/sqrt(n_data/segment_size).\n");
fprintf(stderr,"\tOptions:\n");
fprintf(stderr,"\t-C 2 column X(t),Y(t) input; estimate Cross-spectra [Default 1 col, X power only].\n\n");
fprintf(stderr,"\t-D<dt> set delta_time sampling interval of data [Default = 1.0].\n");
explain_option ('H');
fprintf(stderr,"\t-N<name_stem> supply name stem for files [Default = 'spectrum'].\n");
fprintf(stderr,"\t\tOutput files will be named <name_stem>.xpower, etc.\n");
explain_option ('V');
fprintf(stderr,"\t-W write Wavelength of spectral estimate in col 1 [Default = frequency].\n");
exit(-1);
}
if (window == 0) {
fprintf (stderr, "%s: GMT SYNTAX ERROR -S option: segment size must be positive\n", gmt_program);
error++;
}
if (window_test != window) {
fprintf (stderr, "%s: GMT SYNTAX ERROR -S option: Segment size not radix 2. Try %d or %d\n", gmt_program,
(window_test/2), window_test);
error++;
}
if (dt <= 0.0) {
fprintf (stderr, "%s: GMT SYNTAX ERROR -D option: Sampling interval must be positive\n", gmt_program);
error++;
}
if (error) exit (-1);
alloc_arrays();
read_data();
compute_spectra();
write_output();
free_space();
gmt_end (argc, argv);
}
void alloc_arrays()
{
n_spec = window/2; /* This means we skip zero frequency; data are detrended */
window_2 = 2 * window; /* This is for complex array stuff */
spec = (struct SPEC *) memory (CNULL, n_spec, sizeof(struct SPEC), "spectrum1d");
datac = (float *) memory (CNULL, window_2, sizeof(float), "spectrum1d");
}
void read_data()
{
int i, n_alloc;
char buffer[512];
double xx, yy;
if (fp == NULL) fp = stdin;
n_alloc = GMT_CHUNK;
n_data = 0;
x = (float *) memory (CNULL, n_alloc, sizeof(float), "spectrum1d");
if (y_given) y = (float *)memory (CNULL, n_alloc, sizeof(float), "spectrum1d");
if (gmtdefs.io_header) for (i = 0; i < gmtdefs.n_header_recs; i++) fgets (buffer, 512, fp);
while ( (fgets (buffer, 512, fp) ) != NULL) {
if ((sscanf(buffer, "%lf %lf", &xx, &yy)) != (1 + y_given)) {
fprintf(stderr,"spectrum1d: Cannot read line %d\n", n_data+1);
continue;
}
x[n_data] = xx;
if (y_given) y[n_data] = yy;
n_data++;
if (n_data == n_alloc) {
n_alloc += GMT_CHUNK;
x = (float *) memory ((char *)x, n_alloc, sizeof(float), "spectrum1d");
if (y_given) y = (float *)memory ((char *)y, n_alloc, sizeof(float), "spectrum1d");
}
}
if (fp != stdin) fclose(fp);
x = (float *) memory ((char *)x, n_data, sizeof(float), "spectrum1d");
if (y_given) y = (float *)memory ((char *)y, n_data, sizeof(float), "spectrum1d");
if (gmtdefs.verbose) fprintf(stderr,"Read %d data points.\n", n_data);
}
void compute_spectra()
{
int n_windows, w, i, t_start, t_stop, t, f;
int narray, ndim = 1, ksign = -1, iform = 1;
float work = 0.0;
double dw, spec_scale, x_varp, y_varp, one_on_nw, co_quad;
double xreal, ximag, yreal, yimag, xpower, ypower, co_spec, quad_spec;
narray = window;
/* Scale factor for spectral estimates should be 1/4 of amount given in
Bendat & Piersol eqn 11-102 because I compute 2 * fft in my
one-sided code below. However, tests show that I need 1/8 of
their equation to match variances approximately: */
/* This used to read: spec_scale = 0.5 / (window_2 * dt); */
spec_scale = dt / (window_2);
d_n_windows = (double)n_data / (double)window;
n_windows = rint(2.0 * d_n_windows) - 1;
one_on_nw = 1.0 / n_windows;
dw = (double)(n_data - window) / (double)(n_windows - 1);
for (w = 0; w < n_windows; w++) {
t_start = floor (0.5 + w * dw);
t_stop = t_start + window;
if (y_given) {
for (t = t_start, i = 0; t < t_stop; t++, i+=2) {
datac[i] = x[t];
datac[i+1] = y[t];
}
}
else {
for (t = t_start, i = 0; t < t_stop; t++, i+=2) {
datac[i] = x[t];
datac[i+1] = 0.0;
}
}
detrend_and_hanning();
fourt_(datac, &narray, &ndim, &ksign, &iform, &work);
/* Get one-sided estimates: */
x_varp = spec_scale * (datac[0] * datac[0]);
if (y_given) {
y_varp = spec_scale * (datac[1] * datac[1]);
for (i = 0, f = 2; i < n_spec; i++, f+=2) {
xreal = (i == n_spec - 1) ? datac[f] : datac[f] + datac[window_2 - f];
ximag = (i == n_spec - 1) ? 0.0 : datac[f+1] - datac[window_2 - f + 1];
yreal = (i == n_spec - 1) ? datac[f+1] : datac[f+1] + datac[window_2 - f + 1];
yimag = (i == n_spec - 1) ? 0.0 : datac[window_2 - f] - datac[f];
xpower = spec_scale * (xreal * xreal + ximag * ximag);
ypower = spec_scale * (yreal * yreal + yimag * yimag);
co_spec = spec_scale * (xreal * yreal + ximag * yimag);
quad_spec = spec_scale * (ximag * yreal - yimag * xreal);
x_varp += xpower;
y_varp += ypower;
spec[i].xpow += xpower;
spec[i].ypow += ypower;
/* Temporarily store co-spec in gain: */
spec[i].gain += co_spec;
/* Temporarily store quad-spec in phase: */
spec[i].phase += quad_spec;
}
x_varp *= (dt/n_spec);
y_varp *= (dt/n_spec);
}
else {
for (i = 0, f = 2; i < n_spec; i++, f+=2) {
xreal = datac[f] + datac[window_2 - f];
ximag = datac[f+1] - datac[window_2 - f + 1];
xpower = spec_scale * (xreal * xreal + ximag * ximag);
x_varp += xpower;
spec[i].xpow += xpower;
}
x_varp *= (dt/n_spec);
}
if (gmtdefs.verbose) {
y_pow = (y_given) ? y_variance/y_varp : 0.0;
fprintf(stderr,"Window %d from %d to %d\n", w, t_start, t_stop);
fprintf(stderr,"X var: %.8lg X pow: %.8lg ratio: %.8lg Y var: %.8lg Y pow: %.8lg ratio: %.8lg\n",
x_variance, x_varp, (x_variance/x_varp), y_variance, y_varp, y_pow);
}
}
/* Now we can divide by n_windows for the ensemble average.
The cross spectral stuff needs to be computed: */
if (y_given ) {
for (i = 0; i < n_spec; i++) {
spec[i].xpow *= one_on_nw;
spec[i].ypow *= one_on_nw;
co_spec = spec[i].gain * one_on_nw;
quad_spec = spec[i].phase * one_on_nw;
spec[i].phase = d_atan2(quad_spec, co_spec);
co_quad = co_spec * co_spec + quad_spec * quad_spec;
spec[i].coh = co_quad / (spec[i].xpow * spec[i].ypow);
spec[i].gain = sqrt(co_quad) / spec[i].xpow;
spec[i].radmit = co_spec / spec[i].xpow;
}
}
else {
for (i = 0; i < n_spec; i++) {
spec[i].xpow *= one_on_nw;
}
}
}
void write_output()
{
int i;
double f, delta_f, eps_pow, eps, cop, nop;
char fname[120], format[50];
FILE *fpout;
sprintf (format, "%s\t%s\t%s\n\0", gmtdefs.d_format, gmtdefs.d_format, gmtdefs.d_format);
delta_f = 1.0 / (window * dt);
eps_pow = 1.0 / sqrt(d_n_windows); /* Multiplicative error bars for power spectra */
/* First write x power */
sprintf(fname, "%s.xpower\0", namestem);
if ( (fpout = fopen(fname, "w")) == NULL) {
fprintf(stderr,"spectrum1d: Cannot open w %s\n", fname);
exit(-1);
}
if (gmtdefs.verbose) fprintf(stderr,"spectrum1d: Writing %s\n", fname);
for (i = 0; i < n_spec; i++) {
f = (i + 1) * delta_f;
if (write_wavelength) f = 1.0 / f;
fprintf(fpout, format, f, spec[i].xpow, eps_pow * spec[i].xpow);
}
fclose(fpout);
if (y_given) {
/* Write y power */
sprintf(fname, "%s.ypower\0", namestem);
if ( (fpout = fopen(fname, "w")) == NULL) {
fprintf(stderr,"spectrum1d: Cannot open w %s\n", fname);
exit(-1);
}
if (gmtdefs.verbose) fprintf(stderr,"spectrum1d: Writing %s\n", fname);
for (i = 0; i < n_spec; i++) {
f = (i + 1) * delta_f;
if (write_wavelength) f = 1.0 / f;
fprintf(fpout, format, f, spec[i].ypow, eps_pow * spec[i].ypow);
}
fclose(fpout);
/* Write Coherent Output power */
sprintf(fname, "%s.cpower\0", namestem);
if ( (fpout = fopen(fname, "w")) == NULL) {
fprintf(stderr,"spectrum1d: Cannot open w %s\n", fname);
exit(-1);
}
if (gmtdefs.verbose) fprintf(stderr,"spectrum1d: Writing %s\n", fname);
for (i = 0; i < n_spec; i++) {
f = (i + 1) * delta_f;
if (write_wavelength) f = 1.0 / f;
cop = spec[i].ypow * spec[i].coh;
eps = eps_pow * sqrt( (2.0 - spec[i].coh) / spec[i].coh);
fprintf(fpout, format, f, cop, eps*cop);
}
fclose(fpout);
/* Write Noise Output power */
sprintf(fname, "%s.npower\0", namestem);
if ( (fpout = fopen(fname, "w")) == NULL) {
fprintf(stderr,"spectrum1d: Cannot open w %s\n", fname);
exit(-1);
}
if (gmtdefs.verbose) fprintf(stderr,"spectrum1d: Writing %s\n", fname);
for (i = 0; i < n_spec; i++) {
f = (i + 1) * delta_f;
if (write_wavelength) f = 1.0 / f;
nop = spec[i].ypow * (1.0 - spec[i].coh);
fprintf(fpout, format, f, nop, eps_pow*nop);
}
fclose(fpout);
/* Write Gain spectrum */
sprintf(fname, "%s.gain\0", namestem);
if ( (fpout = fopen(fname, "w")) == NULL) {
fprintf(stderr,"spectrum1d: Cannot open w %s\n", fname);
exit(-1);
}
if (gmtdefs.verbose) fprintf(stderr,"spectrum1d: Writing %s\n", fname);
for (i = 0; i < n_spec; i++) {
f = (i + 1) * delta_f;
if (write_wavelength) f = 1.0 / f;
eps = eps_pow * sqrt( (1.0 - spec[i].coh) / 2.0 * spec[i].coh);
fprintf(fpout, format, f, spec[i].gain, eps*spec[i].gain);
}
fclose(fpout);
/* Write Real Admittance spectrum */
sprintf(fname, "%s.admit\0", namestem);
if ( (fpout = fopen(fname, "w")) == NULL) {
fprintf(stderr,"spectrum1d: Cannot open w %s\n", fname);
exit(-1);
}
if (gmtdefs.verbose) fprintf(stderr,"spectrum1d: Writing %s\n", fname);
for (i = 0; i < n_spec; i++) {
f = (i + 1) * delta_f;
if (write_wavelength) f = 1.0 / f;
eps = eps_pow * sqrt( (1.0 - spec[i].coh) / 2.0 * spec[i].coh);
fprintf(fpout, format, f, spec[i].radmit, fabs (eps*spec[i].radmit));
}
fclose(fpout);
/* Write Phase spectrum */
sprintf(fname, "%s.phase\0", namestem);
if ( (fpout = fopen(fname, "w")) == NULL) {
fprintf(stderr,"spectrum1d: Cannot open w %s\n", fname);
exit(-1);
}
if (gmtdefs.verbose) fprintf(stderr,"spectrum1d: Writing %s\n", fname);
for (i = 0; i < n_spec; i++) {
f = (i + 1) * delta_f;
if (write_wavelength) f = 1.0 / f;
eps = eps_pow * sqrt( (1.0 - spec[i].coh) / 2.0 * spec[i].coh);
fprintf(fpout, format, f, spec[i].phase, eps);
}
fclose(fpout);
/* Write Coherency spectrum */
sprintf(fname, "%s.coh\0", namestem);
if ( (fpout = fopen(fname, "w")) == NULL) {
fprintf(stderr,"spectrum1d: Cannot open w %s\n", fname);
exit(-1);
}
if (gmtdefs.verbose) fprintf(stderr,"spectrum1d: Writing %s\n", fname);
for (i = 0; i < n_spec; i++) {
f = (i + 1) * delta_f;
if (write_wavelength) f = 1.0 / f;
eps = eps_pow * (1.0 - spec[i].coh) * sqrt(2.0 / spec[i].coh);
fprintf(fpout, format, f, spec[i].coh, eps * spec[i].coh);
}
fclose(fpout);
}
}
void free_space ()
{
free ((char *)spec);
free ((char *)datac);
free ((char *)x);
if (y_given) free ((char *)y);
}
void detrend_and_hanning()
{
int i, t;
double sumx, sumtx, sumy, sumty, sumt2, x_slope, x_mean, y_slope, y_mean;
double t_factor, h_period, h_scale, hc, hw, tt;
sumx = 0.0;
sumtx = 0.0;
sumy = 0.0;
sumty = 0.0;
sumt2 = 0.0;
x_variance = 0.0;
y_variance = 0.0;
t_factor = 2.0 / (window - 1);
h_period = M_PI / (double)window; /* For Hanning window */
h_scale = sqrt(8.0/3.0); /* For Hanning window */
if (y_given) {
for (i = 0, t = 0; i < window_2; i+=2, t++) {
tt = t * t_factor - 1.0;
sumt2 += (tt * tt);
sumx += datac[i];
sumtx += (tt * datac[i]);
sumy += datac[i+1];
sumty += (tt * datac[i+1]);
}
}
else {
for (i = 0, t = 0; i < window_2; i+=2, t++) {
tt = t * t_factor - 1.0;
sumt2 += (tt * tt);
sumx += datac[i];
sumtx += (tt * datac[i]);
}
}
x_slope = sumtx / sumt2;
x_mean = sumx / window;
if (y_given) {
y_slope = sumty / sumt2;
y_mean = sumy / window;
for (i = 0, t = 0; i < window_2; i+=2, t++) {
hc = cos(t * h_period);
hw = h_scale * (1.0 - hc * hc);
tt = t * t_factor - 1.0;
datac[i] -= (x_mean + tt * x_slope);
datac[i] *= hw;
x_variance += (datac[i] * datac[i]);
datac[i+1] -= (y_mean + tt * y_slope);
datac[i+1] *= hw;
y_variance += (datac[i+1] * datac[i+1]);
}
x_variance /= window;
y_variance /= window;
}
else {
for (i = 0, t = 0; i < window_2; i+=2, t++) {
hc = cos(t * h_period);
hw = h_scale * (1.0 - hc * hc);
tt = t * t_factor - 1.0;
datac[i] -= (x_mean + tt * x_slope);
datac[i] *= hw;
x_variance += (datac[i] * datac[i]);
}
x_variance /= window;
}
}