📄 fft.cpp
字号:
// fft -- a program to model a Fast Fourier Transform (FFT).
//
// Copyright (C) 2001 John Dalton
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
// Include required libraries
#include <vector>
#include <math.h>
#include <iostream>
#include <complex>
#include <stdlib.h>
//
// factor
//
// Return a factor of a number.
//
// Inputs: x = the number to be factored.
// Returns: A factor of 'x'. If 'x' is a
// prime, eith 'x' or the number
// one will be returned.
//
long factor(long x)
{
long i;
for(i=2; i<x; i++) {
if(x%i==0) {
return(i);
}
}
return(1);
}
//Paramatise the type of each element
//to allow it to be changed easily
#define element_type double_complex
//This routine does one stage of an FFT.
//
// fft_stage
//
// Do a set of FFTs. The length of the FFT may be
// smaller than the length of the input sequence.
// In this case, multiple FFTs will be performed.
// If the length of the FFT is a composite number,
// it is recusively decomposed into smaller FFTs
// whose lengths are prime numbers. The elements
// of each FFT do not need to be consecutive.
//
// Inputs: in = A sequence of numbers to be transformed.
// fft_length = The number of points in each FFT
// grouping = The separation between each element
// within an FFT.
// Returns: The transformed sequence.
//
vector<element_type> fft_stage(vector<element_type> in, int fft_length, int grouping)
{
int x, y;
vector<element_type> out(in.size());
int f;
// cout<<"***FFT"<<fft_length<<" Grouping:"<<grouping<<" Array Length:"<<in.size()<<"\n";
// A DFT of length does nothing, so just
// return the input
if(fft_length <=1) {
out = in;
return(out);
}
// Factorise the number of points in the DFT (if possible)
// so we can break it into two smaller DFTs
f = factor(fft_length);
// If the number of points in the DFT is a composite
// number (ie. can be factorised), divide it into
// two smaller DFTs. Recurse until the DFT has been
// decomposed into DTFs of prime length.
if(f!=fft_length &&f!=1) {
int P = f;
int Q = fft_length / f;
vector<element_type> intermediate(in.size());
//Do a DFT along one dimension
intermediate = fft_stage(in, P, grouping*Q);
//Multiply by twiddle factors
for(int xx=0; xx<intermediate.size(); xx+=P*Q) {
for(int a=0; a<P; a++) {
for(int q=0; q<Q; q++) {
intermediate[xx+a*Q+q] = intermediate[xx+a*Q+q] * exp(-double_complex(0,1)*2*M_PI/(P*Q)*a*q);
}
}
}
//Do a DFT along a second, orthogonal, dimension
out = fft_stage(intermediate, Q, grouping);
return(out);
}
// Here if the length for the DFT was prime.
for(x=0; x<in.size(); x+=fft_length*grouping) {
for(y=x; y<x+grouping; y++) {
//If the length of the DFT is two, do a butterfly
//operation.
if(fft_length==2) {
out[y ] = in[y] + in[y+grouping];
out[y+grouping] = in[y] - in[y+grouping];
} else { //do an exhaustive DFT (for the time being)
//For other prime length DTFs (3,5,7,11,...) do a
//full DFT. I'm still looking for a more efficient
//algorithm to use here. Any suggestions?
for(int k=0; k<fft_length; k++) {
out[y+k*grouping] = 0;
for(int n=0; n<fft_length; n++){
out[y+k*grouping] += in[y+n*grouping]*exp(-double_complex(0,1)*n*k*2*M_PI/fft_length);
}
}
}
}
}
return(out);
}
//
// fft
//
// Do an FFT. There are no restrictions on the
// length of the FFT, but a power of two will
// yield the most efficient calculation.
// This is the routine you should call to do
// an FFT (not fft_stage).
//
// Inputs: in = A sequence of numbers to be transformed.
// Returns: The transformed sequence.
//
vector<element_type> fft(vector<element_type> in)
{
return( fft_stage(in, in.size(), 1) );
}
//
// golden_dft_model
//
// Do a Discrete Fourier Transform (DFT).
// This is the model against which results
// of the FFT will be compared.
//
// Inputs: in = A sequence of numbers to be transformed.
// Returns: The transformed sequence.
//
vector<element_type> golden_dft_model(vector<element_type> in)
{
vector<element_type> out(in.size());
for(int k=0; k<out.size(); k++) {
out[k] = 0;
for(int n=0; n<in.size(); n++) {
out[k] += in[n]*exp(-double_complex(0,1)*2*M_PI*n*k/in.size());
}
}
return(out);
}
//Reproduce the order in which the outputs of the FFT appear
//
// order
//
// This function reproduces the order of the
// outputs of the FFT. For an FFT of length a
// power of two, the order will be bit reversed.
// For other lengths, the ordering will be slightly
// more complicated.
//
// Inputs: in = A sequence of numbers 1,2,3,...,N-1.
// length = The length of the FFT whose output
// ordering is to be reproduced.
// grouping = The separation between each element
// of the FFT.
// Returns: A sequence whose order corresponds to the
// order of the FFT's outputs.
//
vector<int> order(vector<int> in, int length, int grouping)
{
vector<int> out(in.size());
int f;
if(length <=1) {
out = in;
return(out);
}
f = factor(length);
if(f!=length &&f!=1) {
int P = f;
int Q = length / f;
vector<int> intermediate(in.size());
// intermediate = order(in, P, grouping*Q);
//out = order(intermediate, Q, grouping);
intermediate = order(in, Q, grouping*P);
out = order(intermediate, P, grouping);
return(out);
}
for(int x=0; x<in.size(); x+=length*grouping) {
for(int y=0; y<grouping; y++) {
for(int k=0; k<length; k++) {
out[x+y+k*grouping] = in[x+y*length+k];
}
}
}
return(out);
}
//
// near
//
// Check if two complex numbers are equal,
// to within some approximation.
//
// Inputs: a = The first complex number.
// b = The second complex number.
// Returns: One if the two numbers are equal, zero otherwise.
//
int near(element_type a, element_type b)
{
const double epsilon = 1e-12;
if( fabs(a.real()-b.real()) <= epsilon && fabs(a.imag()-b.imag()) <= epsilon ) {
return(1);
}
return(0);
}
//
// test_fft
//
// Do an FFT and compare the results with the
// output of the corresponging DFT. If the
// results are not the same, print an
// error message.
//
// Inputs: in = The complex sequence to be transformed.
// Outputs: failed = Non-zero if the test failed, zero if it suceeded.
// Returns: The results of the FFT.
//
vector<element_type> test_fft(vector<element_type> in, int *failed)
{
vector<element_type> out(in.size()), reference(in.size());
vector<int> index(in.size());
out = fft(in);
reference = golden_dft_model(in);
for(int i=0; i<index.size(); i++) {
index[i] = i;
}
index = order(index, index.size(), 1);
//for(int i=0; i<index.size(); i++) {
// cout << index[i] << ((i==index.size()-1)?'\n':',');
//}
for(int i=0; i<index.size(); i++) {
if( !near(reference[i], out[index[i]]) ) {
cout << "FFT Test Failed!\n";
cout << i << ": " << reference[i] << " != " << out[index[i]] << "\n";
for(int j=0; j<in.size(); j++) {
cout << j << ": " << in[j] << " " << reference[j] << " " << out[index[j]] << "\n";
}
*failed = 1;
return(out);
}
}
*failed = 0;
return(out);
}
//
// main
//
// Run a series of tests to verify an FFT.
//
// Inputs: None.
// Outputs: None.
// Returns: Zero if the tests suceed. Non-zero otherwise.
//
main()
{
int i;
int length;
int failed;
//const double freq = 3.0;
// Do a series of tests using walking tones on a
// series of different length FFTs. Assuming linearity,
// these tests should prove the FFT correct.
cout<< "Tone Test.\n";
cout.flush();
for(length=1; length<=32; length++) {
vector<element_type> in(length), out(length);
for(double freq=0; freq<length; freq++){
for(i=0; i<length; i++) {
// in[i] = i;
in[i] = exp(double_complex(0,1)*2.0*M_PI/length*freq*i);
}
for(i=0; i<length; i++) {
// cout << in[i] << ((i==length-1)?'\n':',');
}
out = test_fft(in, &failed);
if(failed) {
exit(1);
}
for(i=0; i<length; i++) {
//cout << "(" << (int)(out[i].real()*100.0)/100 << "," << (int)(out[i].imag()*100.0)/100 << ")" << ((i==length-1)?'\n':',');
}
}
}
// Do a series of tests with random data and
// random length FFTs. These test double check
// that nothing is wrong.
cout<< "Monte Carlo Test.\n";
cout.flush();
int max_length = 100;
for(i=0; i<100000; i++) {
int length = rand()%max_length;
vector<element_type> in(length);
// cout<< length << " ";
//cout.flush();
for(int i=0; i<in.size(); i++) {
in[i] = 1.0-2.0*(double)rand()/(double)RAND_MAX;
}
test_fft(in, &failed);
if(failed) {
exit(1);
}
}
cout << "PASSED ALL TESTS.\n";
return(0);
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -