ngspice/src/frontend/com_fft.c

677 lines
19 KiB
C
Raw Normal View History

/**********
Copyright 2008 Holger Vogt. All rights reserved.
Author: 2008 Holger Vogt
**********/
/*
* Code to do fast fourier transform on data.
*/
2011-09-18 11:03:55 +02:00
#define GREEN /* select fast Green's fft */
#include "ngspice/ngspice.h"
#include "ngspice/ftedefs.h"
#include "ngspice/dvec.h"
#include "ngspice/sim.h"
#include "com_fft.h"
#include "variable.h"
2010-10-15 20:22:39 +02:00
#include "parse.h"
#include "../misc/misc_time.h"
#include "ngspice/fftext.h"
#ifndef GREEN
static void fftext(double*, double*, long int, long int, int);
#endif
void
com_fft(wordlist *wl)
{
ngcomplex_t **fdvec = NULL;
double **tdvec = NULL;
double *freq, *win = NULL, *time;
double delta_t, span;
int fpts, i, j, tlen, ngood;
2009-10-04 13:48:37 +02:00
struct dvec *f, *vlist, *lv = NULL, *vec;
struct pnode *pn, *names = NULL;
2011-09-18 11:03:55 +02:00
#ifdef GREEN
int mm;
#endif
double *reald = NULL, *imagd = NULL;
2010-02-28 18:52:09 +01:00
int size, sign, order;
double scale, sigma;
if (!plot_cur || !plot_cur->pl_scale) {
fprintf(cp_err, "Error: no vectors loaded.\n");
goto done;
}
2010-02-28 18:52:09 +01:00
if (!isreal(plot_cur->pl_scale) ||
((plot_cur->pl_scale)->v_type != SV_TIME)) {
fprintf(cp_err, "Error: fft needs real time scale\n");
goto done;
}
tlen = (plot_cur->pl_scale)->v_length;
time = (plot_cur->pl_scale)->v_realdata;
span = time[tlen-1] - time[0];
delta_t = span/(tlen - 1);
2010-02-28 18:52:09 +01:00
2011-09-18 11:03:55 +02:00
#ifdef GREEN
// size of input vector is power of two and larger than spice vector
size = 1;
mm = 0;
while (size < tlen) {
size <<= 1;
mm++;
}
#else
2010-02-28 18:52:09 +01:00
/* size of input vector is power of two and larger than spice vector */
size = 1;
while (size < tlen)
size *= 2;
2011-09-18 11:03:55 +02:00
#endif
2010-02-28 18:52:09 +01:00
/* output vector has length of size/2 */
fpts = size/2;
2010-02-28 18:52:09 +01:00
/* window functions - should have an average of one */
win = TMALLOC(double, tlen);
{
char window[BSIZE_SP];
double maxt = time[tlen-1];
if (!cp_getvar("specwindow", CP_STRING, window))
strcpy(window, "blackman");
if (eq(window, "none"))
for (i = 0; i < tlen; i++)
2010-02-28 18:52:09 +01:00
win[i] = 1.0;
else if (eq(window, "rectangular"))
for (i = 0; i < tlen; i++) {
if (maxt-time[i] > span)
win[i] = 0.0;
else
win[i] = 1.0;
}
else if (eq(window, "triangle") || eq(window, "bartlet") || eq(window, "bartlett"))
for (i = 0; i < tlen; i++) {
if (maxt-time[i] > span)
win[i] = 0.0;
else
win[i] = 2.0 - fabs(2+4*(time[i]-maxt)/span);
}
else if (eq(window, "hann") || eq(window, "hanning") || eq(window, "cosine"))
for (i = 0; i < tlen; i++) {
if (maxt-time[i] > span)
win[i] = 0.0;
else
win[i] = 1.0 - cos(2*M_PI*(time[i]-maxt)/span);
}
else if (eq(window, "hamming"))
for (i = 0; i < tlen; i++) {
if (maxt-time[i] > span)
win[i] = 0.0;
else
win[i] = 1.0 - 0.46/0.54*cos(2*M_PI*(time[i]-maxt)/span);
}
else if (eq(window, "blackman"))
for (i = 0; i < tlen; i++) {
if (maxt-time[i] > span) {
win[i] = 0;
} else {
win[i] = 1.0;
win[i] -= 0.50/0.42*cos(2*M_PI*(time[i]-maxt)/span);
win[i] += 0.08/0.42*cos(4*M_PI*(time[i]-maxt)/span);
}
}
else if (eq(window, "flattop"))
for (i = 0; i < tlen; i++) {
if (maxt-time[i] > span) {
win[i] = 0;
} else {
win[i] = 1.0;
win[i] -= 1.93*cos(2*M_PI*(time[i]-maxt)/span);
win[i] += 1.29*cos(4*M_PI*(time[i]-maxt)/span);
win[i] -= 0.388*cos(6*M_PI*(time[i]-maxt)/span);
win[i] += 0.032*cos(8*M_PI*(time[i]-maxt)/span);
}
}
else if (eq(window, "gaussian")) {
if (!cp_getvar("specwindoworder", CP_NUM, &order))
order = 2;
if (order < 2)
order = 2;
sigma = 1.0/order;
scale = 0.83/sigma;
for (i = 0; i < tlen; i++) {
if (maxt-time[i] > span)
win[i] = 0;
else
win[i] = scale*exp(-0.5 * pow((time[i]-maxt/2)/(sigma*maxt/2), 2));
}
} else {
fprintf(cp_err, "Warning: unknown window type %s\n", window);
goto done;
}
}
names = ft_getpnames(wl, TRUE);
vlist = NULL;
ngood = 0;
for (pn = names; pn; pn = pn->pn_next) {
vec = ft_evaluate(pn);
for (; vec; vec = vec->v_link2) {
if (vec->v_length != tlen) {
fprintf(cp_err, "Error: lengths of %s vectors don't match: %d, %d\n",
vec->v_name, vec->v_length, tlen);
continue;
}
if (!isreal(vec)) {
fprintf(cp_err, "Error: %s isn't real!\n", vec->v_name);
continue;
}
if (vec->v_type == SV_TIME) {
continue;
}
if (!vlist)
vlist = vec;
else
lv->v_link2 = vec;
lv = vec;
ngood++;
}
}
if (!ngood)
goto done;
2010-02-28 18:52:09 +01:00
plot_cur = plot_alloc("spectrum");
plot_cur->pl_next = plot_list;
plot_list = plot_cur;
plot_cur->pl_title = copy((plot_cur->pl_next)->pl_title);
plot_cur->pl_name = copy("Spectrum");
plot_cur->pl_date = copy(datestring());
freq = TMALLOC(double, fpts);
f = alloc(struct dvec);
ZERO(f, struct dvec);
f->v_name = copy("frequency");
f->v_type = SV_FREQUENCY;
f->v_flags = (VF_REAL | VF_PERMANENT | VF_PRINT);
f->v_length = fpts;
f->v_realdata = freq;
vec_new(f);
for (i = 0; i<fpts; i++)
freq[i] = i*1.0/span*tlen/size;
tdvec = TMALLOC(double *, ngood);
fdvec = TMALLOC(ngcomplex_t *, ngood);
for (i = 0, vec = vlist; i<ngood; i++) {
tdvec[i] = vec->v_realdata; /* real input data */
fdvec[i] = TMALLOC(ngcomplex_t, fpts); /* complex output data */
f = alloc(struct dvec);
ZERO(f, struct dvec);
f->v_name = vec_basename(vec);
f->v_type = SV_NOTYPE;
f->v_flags = (VF_COMPLEX | VF_PERMANENT);
f->v_length = fpts;
f->v_compdata = fdvec[i];
vec_new(f);
vec = vec->v_link2;
}
sign = 1;
2010-02-28 18:52:09 +01:00
printf("FFT: Time span: %g s, input length: %d, zero padding: %d\n", span, size, size-tlen);
printf("FFT: Freq. resolution: %g Hz, output length: %d\n", 1.0/span*tlen/size, fpts);
reald = TMALLOC(double, size);
imagd = TMALLOC(double, size);
for (i = 0; i<ngood; i++) {
for (j = 0; j < tlen; j++) {
2010-02-28 18:52:09 +01:00
reald[j] = tdvec[i][j]*win[j];
imagd[j] = 0.0;
}
for (j = tlen; j < size; j++) {
2010-02-28 18:52:09 +01:00
reald[j] = 0.0;
imagd[j] = 0.0;
}
2011-09-18 11:03:55 +02:00
#ifdef GREEN
// Green's FFT
2011-09-18 11:03:55 +02:00
fftInit(mm);
rffts(reald, mm, 1);
fftFree();
scale = size;
/* Re(x[0]), Re(x[N/2]), Re(x[1]), Im(x[1]), Re(x[2]), Im(x[2]), ... Re(x[N/2-1]), Im(x[N/2-1]). */
for (j = 0; j < fpts; j++) {
2011-09-18 11:03:55 +02:00
fdvec[i][j].cx_real = reald[2*j]/scale;
fdvec[i][j].cx_imag = reald[2*j+1]/scale;
}
fdvec[i][0].cx_imag = 0;
#else
2010-02-28 18:52:09 +01:00
fftext(reald, imagd, size, tlen, sign);
scale = 0.66;
for (j = 0; j < fpts; j++) {
2010-02-28 18:52:09 +01:00
fdvec[i][j].cx_real = reald[j]/scale;
fdvec[i][j].cx_imag = imagd[j]/scale;
}
2011-09-18 11:03:55 +02:00
#endif
}
done:
2008-11-23 10:37:13 +01:00
tfree(reald);
tfree(imagd);
2010-02-28 18:52:09 +01:00
tfree(tdvec);
2009-04-05 10:02:03 +02:00
tfree(fdvec);
2010-02-28 18:52:09 +01:00
tfree(win);
free_pnode(names);
}
2010-11-27 17:36:03 +01:00
void
com_psd(wordlist *wl)
{
ngcomplex_t **fdvec = NULL;
double **tdvec = NULL;
double *freq, *win = NULL, *time, *ave;
2010-11-27 17:36:03 +01:00
double delta_t, span, noipower;
2011-08-08 21:39:15 +02:00
int mm;
unsigned long size, ngood, fpts, i, j, tlen, jj, smooth, hsmooth;
2010-11-27 17:36:03 +01:00
char *s;
2011-06-11 19:07:38 +02:00
struct dvec *f, *vlist, *lv = NULL, *vec;
struct pnode *pn, *names = NULL;
2010-11-27 17:36:03 +01:00
double *reald = NULL, *imagd = NULL;
2011-08-08 21:39:15 +02:00
int sign, isreal;
2010-11-27 17:36:03 +01:00
double scaling, sum;
int order;
double scale, sigma;
if (!plot_cur || !plot_cur->pl_scale) {
fprintf(cp_err, "Error: no vectors loaded.\n");
goto done;
2010-11-27 17:36:03 +01:00
}
if (!isreal(plot_cur->pl_scale) ||
2010-11-27 17:36:03 +01:00
((plot_cur->pl_scale)->v_type != SV_TIME)) {
fprintf(cp_err, "Error: fft needs real time scale\n");
goto done;
2010-11-27 17:36:03 +01:00
}
2010-11-27 17:36:03 +01:00
tlen = (plot_cur->pl_scale)->v_length;
time = (plot_cur->pl_scale)->v_realdata;
span = time[tlen-1] - time[0];
delta_t = span/(tlen - 1);
2010-11-27 17:36:03 +01:00
// get filter length from parameter input
s = wl->wl_word;
2011-07-17 18:37:54 +02:00
ave = ft_numparse(&s, FALSE);
if (!ave || (*ave < 1.0)) {
fprintf(cp_out, "Number of averaged data points: %d\n", 1);
2010-11-27 17:36:03 +01:00
smooth = 1;
} else {
smooth = (int)(*ave);
2010-11-27 17:36:03 +01:00
}
wl = wl->wl_next;
2010-11-27 17:36:03 +01:00
// size of input vector is power of two and larger than spice vector
size = 1;
mm = 0;
while (size < tlen) {
size <<= 1;
mm++;
}
// output vector has length of size/2
fpts = size>>1;
2010-11-27 17:36:03 +01:00
// window function
2010-11-27 17:36:03 +01:00
win = TMALLOC(double, tlen);
{
char window[BSIZE_SP];
double maxt = time[tlen-1];
if (!cp_getvar("specwindow", CP_STRING, window))
strcpy(window, "blackman");
if (eq(window, "none"))
for (i = 0; i < tlen; i++)
2010-11-27 17:36:03 +01:00
win[i] = 1;
else if (eq(window, "rectangular"))
for (i = 0; i < tlen; i++) {
if (maxt-time[i] > span)
win[i] = 0;
else
win[i] = 1;
}
else if (eq(window, "hanning") || eq(window, "cosine"))
for (i = 0; i < tlen; i++) {
if (maxt-time[i] > span)
win[i] = 0;
else
win[i] = 1 - cos(2*M_PI*(time[i]-maxt)/span);
}
else if (eq(window, "hamming"))
for (i = 0; i < tlen; i++) {
if (maxt-time[i] > span)
win[i] = 0;
else
win[i] = 1 - 0.92/1.08*cos(2*M_PI*(time[i]-maxt)/span);
}
else if (eq(window, "triangle") || eq(window, "bartlet"))
for (i = 0; i < tlen; i++) {
if (maxt-time[i] > span)
win[i] = 0;
else
win[i] = 2 - fabs(2+4*(time[i]-maxt)/span);
}
else if (eq(window, "blackman")) {
int order;
if (!cp_getvar("specwindoworder", CP_NUM, &order))
order = 2;
if (order < 2)
order = 2; /* only order 2 supported here */
for (i = 0; i < tlen; i++) {
if (maxt-time[i] > span) {
win[i] = 0;
} else {
win[i] = 1;
win[i] -= 0.50/0.42*cos(2*M_PI*(time[i]-maxt)/span);
win[i] += 0.08/0.42*cos(4*M_PI*(time[i]-maxt)/span);
}
}
} else if (eq(window, "gaussian")) {
if (!cp_getvar("specwindoworder", CP_NUM, &order))
order = 2;
if (order < 2)
order = 2;
sigma = 1.0/order;
scale = 0.83/sigma;
for (i = 0; i < tlen; i++) {
if (maxt-time[i] > span)
win[i] = 0;
else
win[i] = scale*exp(-0.5 * pow((time[i]-maxt/2)/(sigma*maxt/2), 2));
}
/*
* int order;
* double scale;
* extern double erfc(double);
* if (!cp_getvar("specwindoworder", CP_NUM, &order))
* order = 2;
* if (order < 2)
* order = 2;
* scale = pow(2*M_PI/order, 0.5)*(0.5-erfc(pow(order, 0.5)));
* for (i = 0; i < tlen; i++) {
* if (maxt-time[i] > span) {
* win[i] = 0;
* } else {
* win[i] = exp(-0.5*order*(1-2*(maxt-time[i])/span)
* *(1-2*(maxt-time[i])/span))/scale;
* }
* }
*/
} else {
fprintf(cp_err, "Warning: unknown window type %s\n", window);
goto done;
}
2010-11-27 17:36:03 +01:00
}
names = ft_getpnames(wl, TRUE);
2010-11-27 17:36:03 +01:00
vlist = NULL;
ngood = 0;
for (pn = names; pn; pn = pn->pn_next) {
vec = ft_evaluate(pn);
for (; vec; vec = vec->v_link2) {
2011-08-08 21:39:15 +02:00
if (vec->v_length != (int)tlen) {
2011-10-31 11:53:51 +01:00
fprintf(cp_err, "Error: lengths of %s vectors don't match: %d, %lu\n",
2010-11-27 17:36:03 +01:00
vec->v_name, vec->v_length, tlen);
continue;
}
2010-11-27 17:36:03 +01:00
if (!isreal(vec)) {
fprintf(cp_err, "Error: %s isn't real!\n", vec->v_name);
2010-11-27 17:36:03 +01:00
continue;
}
2010-11-27 17:36:03 +01:00
if (vec->v_type == SV_TIME) {
continue;
}
2010-11-27 17:36:03 +01:00
if (!vlist)
vlist = vec;
else
lv->v_link2 = vec;
2010-11-27 17:36:03 +01:00
lv = vec;
ngood++;
}
}
if (!ngood)
goto done;
2010-11-27 17:36:03 +01:00
plot_cur = plot_alloc("spectrum");
plot_cur->pl_next = plot_list;
plot_list = plot_cur;
plot_cur->pl_title = copy((plot_cur->pl_next)->pl_title);
plot_cur->pl_name = copy("PSD");
plot_cur->pl_date = copy(datestring());
2010-11-27 17:36:03 +01:00
2011-09-18 11:03:55 +02:00
freq = TMALLOC(double, fpts + 1);
2010-11-27 17:36:03 +01:00
f = alloc(struct dvec);
ZERO(f, struct dvec);
f->v_name = copy("frequency");
f->v_type = SV_FREQUENCY;
f->v_flags = (VF_REAL | VF_PERMANENT | VF_PRINT);
f->v_length = fpts;
f->v_realdata = freq;
vec_new(f);
for (i = 0; i <= fpts; i++)
freq[i] = i*1./span*tlen/size;
2010-11-27 17:36:03 +01:00
tdvec = TMALLOC(double*, ngood);
fdvec = TMALLOC(ngcomplex_t*, ngood);
for (i = 0, vec = vlist; i<ngood; i++) {
tdvec[i] = vec->v_realdata; /* real input data */
fdvec[i] = TMALLOC(ngcomplex_t, fpts + 1); /* complex output data */
f = alloc(struct dvec);
ZERO(f, struct dvec);
f->v_name = vec_basename(vec);
f->v_type = SV_NOTYPE; //vec->v_type;
f->v_flags = (VF_COMPLEX | VF_PERMANENT);
f->v_length = fpts + 1;
f->v_compdata = fdvec[i];
vec_new(f);
vec = vec->v_link2;
2010-11-27 17:36:03 +01:00
}
2011-10-31 11:53:51 +01:00
printf("PSD: Time span: %g s, input length: %lu, zero padding: %lu\n", span, size, size-tlen);
printf("PSD: Freq. resolution: %g Hz, output length: %lu\n", 1.0/span*tlen/size, fpts);
2010-11-27 17:36:03 +01:00
sign = 1;
isreal = 1;
2011-08-08 21:39:15 +02:00
reald = TMALLOC(double, size);
imagd = TMALLOC(double, size);
// scale = 0.66;
2010-11-27 17:36:03 +01:00
for (i = 0; i<ngood; i++) {
2011-09-18 11:03:55 +02:00
double intres;
for (j = 0; j < tlen; j++) {
reald[j] = (tdvec[i][j]*win[j]);
imagd[j] = 0.;
}
for (j = tlen; j < size; j++) {
reald[j] = 0.;
imagd[j] = 0.;
}
// Green's FFT
2010-11-27 17:36:03 +01:00
fftInit(mm);
rffts(reald, mm, 1);
fftFree();
2011-09-18 11:03:55 +02:00
scaling = size;
2010-11-27 17:36:03 +01:00
/* Re(x[0]), Re(x[N/2]), Re(x[1]), Im(x[1]), Re(x[2]), Im(x[2]), ... Re(x[N/2-1]), Im(x[N/2-1]). */
2011-09-18 11:03:55 +02:00
intres = (double)size * (double)size;
noipower = fdvec[i][0].cx_real = reald[0]*reald[0]/intres;
fdvec[i][fpts].cx_real = reald[1]*reald[1]/intres;
noipower += fdvec[i][fpts-1].cx_real;
for (j = 1; j < fpts; j++) {
jj = j<<1;
fdvec[i][j].cx_real = 2.* (reald[jj]*reald[jj] + reald[jj + 1]*reald[jj + 1])/intres;
fdvec[i][j].cx_imag = 0;
noipower += fdvec[i][j].cx_real;
if (!finite(noipower))
break;
2010-11-27 17:36:03 +01:00
}
2011-09-18 11:03:55 +02:00
printf("Total noise power up to Nyquist frequency %5.3e Hz:\n%e V^2 (or A^2), \nnoise voltage or current %e V (or A)\n",
freq[fpts], noipower, sqrt(noipower));
2010-11-27 17:36:03 +01:00
/* smoothing with rectangular window of width "smooth",
plotting V/sqrt(Hz) or I/sqrt(Hz) */
2011-09-18 11:03:55 +02:00
if (smooth < 1)
continue;
2010-11-27 17:36:03 +01:00
hsmooth = smooth>>1;
for (j = 0; j < hsmooth; j++) {
sum = 0.;
for (jj = 0; jj < hsmooth + j; jj++)
sum += fdvec[i][jj].cx_real;
sum /= (hsmooth + j);
reald[j] = (sqrt(sum)/scaling);
2010-11-27 17:36:03 +01:00
}
for (j = hsmooth; j < fpts-hsmooth; j++) {
sum = 0.;
for (jj = 0; jj < smooth; jj++)
sum += fdvec[i][j-hsmooth+jj].cx_real;
sum /= smooth;
reald[j] = (sqrt(sum)/scaling);
2010-11-27 17:36:03 +01:00
}
for (j = fpts-hsmooth; j < fpts; j++) {
sum = 0.;
for (jj = 0; jj < smooth; jj++)
sum += fdvec[i][j-hsmooth+jj].cx_real;
sum /= (fpts - j + hsmooth - 1);
reald[j] = (sqrt(sum)/scaling);
}
for (j = 0; j < fpts; j++)
fdvec[i][j].cx_real = reald[j];
2010-11-27 17:36:03 +01:00
}
done:
2010-11-27 17:36:03 +01:00
free(reald);
free(imagd);
2010-11-27 17:36:03 +01:00
tfree(tdvec);
tfree(fdvec);
tfree(win);
free_pnode(names);
2010-11-27 17:36:03 +01:00
}
#ifndef GREEN
static void
fftext(double *x, double *y, long int n, long int nn, int dir)
{
/*
http://local.wasp.uwa.edu.au/~pbourke/other/dft/
download 22.05.08
Used with permission from the author Paul Bourke
*/
/*
This computes an in-place complex-to-complex FFT
x and y are the real and imaginary arrays
n is the number of points, has to be to the power of 2
nn is the number of points w/o zero padded values
dir = 1 gives forward transform
dir = -1 gives reverse transform
*/
long i, i1, j, k, i2, l, l1, l2;
double c1, c2, tx, ty, t1, t2, u1, u2, z;
int m = 0, mm = 1;
/* get the exponent to the base of 2 from the number of points */
while (mm < n) {
mm *= 2;
m++;
}
2010-11-27 17:36:03 +01:00
/* Do the bit reversal */
i2 = n >> 1;
j = 0;
for (i = 0; i < n-1; i++) {
if (i < j) {
tx = x[i];
ty = y[i];
x[i] = x[j];
y[i] = y[j];
x[j] = tx;
y[j] = ty;
}
k = i2;
while (k <= j) {
j -= k;
k >>= 1;
}
j += k;
}
/* Compute the FFT */
c1 = -1.0;
c2 = 0.0;
l2 = 1;
for (l = 0; l < m; l++) {
l1 = l2;
l2 <<= 1;
u1 = 1.0;
u2 = 0.0;
for (j = 0; j < l1; j++) {
for (i = j; i < n; i += l2) {
i1 = i + l1;
t1 = u1 * x[i1] - u2 * y[i1];
t2 = u1 * y[i1] + u2 * x[i1];
x[i1] = x[i] - t1;
y[i1] = y[i] - t2;
x[i] += t1;
y[i] += t2;
}
z = u1 * c1 - u2 * c2;
u2 = u1 * c2 + u2 * c1;
u1 = z;
}
c2 = sqrt((1.0 - c1) / 2.0);
if (dir == 1)
c2 = -c2;
c1 = sqrt((1.0 + c1) / 2.0);
}
/* Scaling for forward transform */
if (dir == 1) {
double scale = 1.0 / nn;
for (i = 0; i < n; i++) {
x[i] *= scale; /* don't consider zero padded values */
y[i] *= scale;
}
}
}
#endif /* GREEN */