Plotting frequency spectrum with c++ - c++

Please see the Edits in the answer below this question.
I have written a script to plot the frequency spectrum of a sinusoidal signal with c++. Here are the steps
Applying Hanning window
Apply FFT using fftw3 library
I have three graphs: Signal, Signal when is multiplied to Hanning function, and the frequency spectrum. The frequency spectrum looks wrong. It should have a peak at 50 Hz. Any suggestion would be appreciated. Here is the code:
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include <fftw3.h>
#include <iostream>
#include <cmath>
#include <fstream>
using namespace std;
int main()
{
int i;
double y;
int N=50;
double Fs=1000;//sampling frequency
double T=1/Fs;//sample time
double f=50;//frequency
double *in;
fftw_complex *out;
double t[N];//time vector
double ff[N];
fftw_plan plan_forward;
in = (double*) fftw_malloc(sizeof(double) * N);
out = (fftw_complex*) fftw_malloc(sizeof(fftw_complex) * N);
for (int i=0; i< N;i++)
{
t[i]=i*T;
ff[i]=1/t[i];
in[i] =0.7 *sin(2*M_PI*f*t[i]);// generate sine waveform
double multiplier = 0.5 * (1 - cos(2*M_PI*i/(N-1)));//Hanning Window
in[i] = multiplier * in[i];
}
plan_forward = fftw_plan_dft_r2c_1d ( N, in, out, FFTW_ESTIMATE );
fftw_execute ( plan_forward );
double v[N];
for (int i = 0; i < N; i++)
{
v[i]=20*log(sqrt(out[i][0]*out[i][0]+ out[i][1]*out[i][1])/N/2);//Here I have calculated the y axis of the spectrum in dB
}
fstream myfile;
myfile.open("example2.txt",fstream::out);
myfile << "plot '-' using 1:2" << std::endl;
for(i = 0; i < N; ++i)
{
myfile << ff[i]<< " " << v[i]<< std::endl;
}
myfile.close();
fftw_destroy_plan ( plan_forward );
fftw_free ( in );
fftw_free ( out );
return 0;
}
I have to add that I have plotted the graphs using gnuplot after inserting the results into example2.txt. So ff[i] vs v[i] should give me the frequency spectrum.
Here are the plots: Frequency Spectrum and Sinusoidal time Window respectively:

My Frequency intervals were completely wrong. According to http://www.ni.com/white-paper/3995/en/#toc1; the frequency range and resolution on the x-axis depend on sampling rate and N. The last point on the frequency axis should be Fs/2-Fs/N and the resolution dF=FS/N.So I have changed my script to: (since frequency resolution is Fs/N as you increase the number of smaples N (or decrease sampling frequency Fs) you get smaller frequency resolution and better results.)
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include <fftw3.h>
#include <iostream>
#include <cmath>
#include <fstream>
using namespace std;
int main()
{
int i;
double y;
int N=550;//Number of points acquired inside the window
double Fs=200;//sampling frequency
double dF=Fs/N;
double T=1/Fs;//sample time
double f=50;//frequency
double *in;
fftw_complex *out;
double t[N];//time vector
double ff[N];
fftw_plan plan_forward;
in = (double*) fftw_malloc(sizeof(double) * N);
out = (fftw_complex*) fftw_malloc(sizeof(fftw_complex) * N);
for (int i=0; i<= N;i++)
{
t[i]=i*T;
in[i] =0.7 *sin(2*M_PI*f*t[i]);// generate sine waveform
double multiplier = 0.5 * (1 - cos(2*M_PI*i/(N-1)));//Hanning Window
in[i] = multiplier * in[i];
}
for (int i=0; i<= ((N/2)-1);i++)
{ff[i]=Fs*i/N;
}
plan_forward = fftw_plan_dft_r2c_1d ( N, in, out, FFTW_ESTIMATE );
fftw_execute ( plan_forward );
double v[N];
for (int i = 0; i<= ((N/2)-1); i++)
{
v[i]=(20*log(sqrt(out[i][0]*out[i][0]+ out[i][1]*out[i][1])))/N; //Here I have calculated the y axis of the spectrum in dB
}
fstream myfile;
myfile.open("example2.txt",fstream::out);
myfile << "plot '-' using 1:2" << std::endl;
for(i = 0;i< ((N/2)-1); i++)
{
myfile << ff[i]<< " " << v[i]<< std::endl;
}
myfile.close();
fftw_destroy_plan ( plan_forward );
fftw_free ( in );
fftw_free ( out );
return 0;
}

I think you may not have enough samples, particularly, reference this Electronics.StackExhcange post: https://electronics.stackexchange.com/q/12407/84272.
You're sampling for 50 samples, so 25 FFT bins. You're sampling at 1000 Hz, so 1000 / 2 / 25 == 250 Hz per FFT bins. Your bin resolution is too low.
I think you need to lower the sampling frequency or increase the number of samples.

Since your question in on SO, your code could use some indentation and style improvement to make it easier to read.
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include <fftw3.h>
#include <iostream>
#include <cmath>
#include <fstream>
using namespace std;
int main(){
// use meaningful names for all the variables
int i;
double y;
int N = 550; // number of points acquired inside the window
double Fs = 200; // sampling frequency
double dF = Fs / N;
double T = 1 / Fs; // sample time
double f = 50; // frequency
double *in;
fftw_complex *out;
double t[N]; // time vector
double ff[N];
fftw_plan plan_forward;
in = (double*) fftw_malloc(sizeof(double) * N);
out = (fftw_complex*) fftw_malloc(sizeof(fftw_complex) * N);
for (int i = 0; i <= N; i++){
t[i]=i*T;
in[i] = 0.7 * sin(2 * M_PI * f * t[i]); // generate sine waveform
double multiplier = 0.5 * (1 - cos(2 * M_PI * i / (N-1))); // Hanning Window
in[i] = multiplier * in[i];
}
for(int i = 0; i <= ((N/2)-1); i++){
ff[i] = (Fs * i) / N;
}
plan_forward = fftw_plan_dft_r2c_1d(N, in, out, FFTW_ESTIMATE);
fftw_execute(plan_forward);
double v[N];
// Here I have calculated the y axis of the spectrum in dB
for(int i = 0; i <= ((N/2)-1); i++){
v[i] = (20 * log(sqrt(out[i][0] * out[i][0] + out[i][1] * out[i][1]))) / N;
}
fstream myfile;
myfile.open("example2.txt", fstream::out);
myfile << "plot '-' using 1:2" << std::endl;
for(i = 0; i < ((N/2)-1); i++){
myfile << ff[i] << " " << v[i] << std::endl;
}
myfile.close();
fftw_destroy_plan(plan_forward);
fftw_free(in);
fftw_free(out);
return 0;
}
Your code can use more comments, especially before loops or function calls to specify their input value (purpose) and/or returning value (result).

Related

FFTW returns different results in some loops

I'm new to use C++.
I have tried to implement FFT using Eigen and fftw3 (version 3.3.10).
The purpose is to read measurement data from CSV file and analyze FFT.
However, I realized that fftw returns different results some time (sometimes return results correctly, sometimes wrong results...) in some loops if do some analysis again and again.
The problem might be just a memory leaking or casting variable problem as I'm new to use C++.
I'm very appreciated if you give me any advice or tips.
Thanks in advance!
#define EIGEN_FFTW_DEFAULT
#include <iostream>
#include <string>
#include <vector>
#include <cmath>
#include <fstream>
#include <sstream>
#include "Eigen/Dense"
#include <fftw3.h>
using namespace Eigen;
// define functions
template <typename T>
T readCSV(const std::string &path);
int nextpow2(int n);
VectorXd offsetData(VectorXd v);
void fftw_test(VectorXd x);
// calculate exponent of next higher power of 2
int nextpow2(int n)
{
if (n < 0) // n must be int
return 0;
if (n == 1) // n
return 1;
return (int)floor(log2(n - 1)) + 1.0;
};
// Read Measurement Data
template <typename T>
T readCSV(const std::string &path)
{
// https://stackoverflow.com/questions/34247057/how-to-read-csv-file-and-assign-to-eigen-matrix
std::ifstream file;
std::string line;
std::string cell;
std::vector<double> row;
uint rows = 0;
file.open(path);
std::cout << "Opend file: " << path << std::endl;
std::getline(file, line); // skip the first header line
while (std::getline(file, line))
{
std::stringstream lineStream(line);
while (std::getline(lineStream, cell, ','))
{
row.push_back(std::stod(cell)); // insert value as double
}
++rows;
}
return Map<const Matrix<typename T::Scalar, T ::RowsAtCompileTime, T::ColsAtCompileTime, RowMajor> >(row.data(), rows, row.size() / rows);
};
void fftw_test(VectorXd x)
{
// Convert data unit
x = x * 980.665 * 10; // Unit conversion:[G] to [cm/sec^2] to [mm/sec^2]
int ns = x.size(); // number of samples
int nfft = std::pow(2, nextpow2(ns)); // number of fft
// Zero padding to array
VectorXd xpad;
int npad = nfft - ns;
if (npad > 0)
{
xpad = VectorXd(nfft);
for (int i = 0; i < ns; ++i)
{
xpad(i) = x(i);
}
}
else
{
xpad = x;
}
// /* prepare a cosine wave */
// for (i = 0; i < N; i++)
// {
// in[i][0] = cos(3 * 2 * M_PI * i / N);
// in[i][1] = 0;
// }
int N = nfft;
fftw_complex *in, *out, *in2;
in = (fftw_complex *)fftw_malloc(sizeof(fftw_complex) * N);
out = (fftw_complex *)fftw_malloc(sizeof(fftw_complex) * N);
in2 = (fftw_complex *)fftw_malloc(sizeof(fftw_complex) * N);
fftw_plan p, q;
for (int i = 0; i < N; i++)
{
in[i][0] = (double)xpad(i);
in[i][1] = 0;
}
p = fftw_plan_dft_1d(N, in, out, FFTW_FORWARD, FFTW_ESTIMATE | FFTW_PRESERVE_INPUT);
fftw_execute(p);
for (int i = 0; i < 10; i++)
{
printf("in: %3d %+9.5f %+9.5f I\n", i, in[i][0], in[i][1]);
}
for (int i = 0; i < 10; i++)
{
printf("freq: %3d %+9.5f %+9.5f I\n", i, out[i][0], out[i][1]);
}
fftw_destroy_plan(p);
fftw_free(in);
fftw_free(out);
fftw_free(in2);
fftw_cleanup();
};
VectorXd offsetData(VectorXd v)
{
// Offset by mean values
int ns = v.size(); // number of samples
VectorXd ones = MatrixXd::Ones(ns, 1);
v = v - v.mean() * ones;
return v;
};
int main()
{
// Read measured data from csv file
MatrixXd measuredData = readCSV<MatrixXd>("./sampleCsv/20220208-134655_A351AU.csv");
// Extract a vertical acceleration column
VectorXd Acc = measuredData.col(4);
VectorXd Acc_offset = offsetData(Acc / 1000);
for (int i = 0; i < 100; ++i)
{
// fftw bug test
printf("loop: %ith \n", i);
fftw_test(Acc_offset);
};
return 0;
}
The sample CSV file is here.
https://drive.google.com/file/d/1DQO2eeMX7AfxjnuW8DDJMOitxHNuIDHA/view?usp=sharing
The correct results should be below.
freq: 0 -0.00000 +0.00000 I
freq: 1 +320.64441 -83.56961 I
freq: 2 -113.66004 -195.80680 I
freq: 3 -28.57778 -13.57046 I
freq: 4 -47.71908 +185.43538 I
freq: 5 +381.01770 +92.18739 I
freq: 6 +430.73267 -348.16464 I
freq: 7 -111.55714 -796.10333 I
freq: 8 -810.79331 -273.42916 I
freq: 9 -624.83461 +607.38775 I

Why is Armadillo so slow compared to a C-style array in a simple row-wise computationnal task

I'm currently computing a small quantity for each value of a big matrix (millions of rows, number of columns < 1000) while considering each row independently.
More precisely, for each value M(i,j) in each row i, column j of this matrix, the quantity is simply [ M(i,j) - mean(i,s) ] / std(i,s) where s is the subset s in M(i,:) - j
in other words, s is the subset of all values of row i without value j.
I compared two implementations, one in C-style array and one in Armadillo, and Armadillo is roughly twice slower in termes of execution time. I would expect a similar or slighty slower execution time, but plain C arrays seem to dramatically improve the performance.
Is there any particular reason or somthing that I missed somewhere? Here is a example compiled with: -O2 -lstdc++ -DARMA_DONT_USE_WRAPPER -lopenblas -llapack -lm. Also tried to use ARMA_NO_DEBUG without success.
#include <string>
#include <vector>
#include <iostream>
#include <fstream>
#include <algorithm>
#include <armadillo>
#include <chrono>
using namespace std::chrono;
/***************************
* main()
***************************/
int main( int argc, char *argv[] )
{
unsigned nrows = 2000000; //number of rows
unsigned ncols = 100; //number of cols
const arma::mat huge_mat = arma::randn(nrows, ncols); //create huge matrix
const arma::uvec vec = arma::linspace<arma::uvec>( 0, huge_mat.n_cols-1, huge_mat.n_cols); //create a vector of [0,...,n]
arma::rowvec inds = arma::zeros<arma::rowvec>( huge_mat.n_cols-1 ); //-1 since we remove only one value at each step.
arma::colvec simuT = arma::zeros<arma::colvec>( ncols ); //let's store the results in this simuT vector.
high_resolution_clock::time_point t1 = high_resolution_clock::now();
//compute some normalization over each value of line of this huge matrix:
for(unsigned i=0; i < nrows; i++) {
const arma::rowvec current_line = huge_mat.row(i); //extract current line
//for each observation in current_line:
for(unsigned j=0; j < ncols; j++) {
//Take care of side effects first:
if( j == 0 )
inds = current_line(arma::span(1, ncols-1));
else
if( j == 1 ) {
inds(0) = current_line(0);
inds(arma::span(1, ncols-2)) = current_line( arma::span(2, ncols-1) );
} else
inds(arma::span(0, j-1)) = current_line( arma::span(0, j-1) );
//Let's do some computation: huge_mat(i,j) - mean[huge_mat(i,:)] / std([huge_mat(i,:)]) //can compute the mean and std first... for each line.
simuT(j) = (current_line(j) - arma::mean(inds)) / ( std::sqrt( 1+1/((double) ncols-1) ) * arma::stddev(inds) );
}
}
high_resolution_clock::time_point t2 = high_resolution_clock::now();
auto duration = duration_cast<seconds>( t2 - t1 ).count();
std::cout << "ARMADILLO: " << duration << " secs\n";
//------------------PLAIN C Array
double *Mat_full;
double *output;
unsigned int i,j,k;
double mean=0, stdd=0;
double sq_diff_sum = 0, sum=0;
double diff = 0;
Mat_full = (double *) malloc(ncols * nrows * sizeof(double));
output = (double *) malloc(nrows * ncols * sizeof(double));
std::vector< std::vector<double> > V(huge_mat.n_rows);
//Some UGLY copy from arma::mat to double* using a vector:
for (size_t i = 0; i < huge_mat.n_rows; ++i)
V[i] = arma::conv_to< std::vector<double> >::from(huge_mat.row(i));
//then dump to Mat_full array:
for (i=0; i < V.size(); i++)
for (j=0; j < V[i].size(); j++)
Mat_full[i + huge_mat.n_rows * j] = V[i][j];
t1 = high_resolution_clock::now();
for(i=0; i < nrows; i++)
for(j=0; j < ncols; j++)
{
//compute mean of subset-------------------
sum = 0;
for(k = 0; k < ncols; k++)
if(k!=j)
{
sum = sum + Mat_full[i+k*nrows];
}
mean = sum / (ncols-1);
//compute standard deviation of subset-----
sq_diff_sum = 0;
for(k = 0; k < ncols; k++)
if(k!=j)
{
diff = Mat_full[i+k*nrows] - mean;
sq_diff_sum += diff * diff;
}
stdd = sqrt(sq_diff_sum / (ncols-2));
//export to plain C array:
output[i*ncols+j] = (Mat_full[i+j*nrows] - mean) / (sqrt(1+1/(((double) ncols)-1))*stdd);
}
t2 = high_resolution_clock::now();
duration = duration_cast<seconds>( t2 - t1 ).count();
std::cout << "C ARRAY: " << duration << " secs\n";
}
In particular the calls to arma::mean and arma::stddev seem to perform poorly when comparing execution times. I did not perform any in-depth analyse of the size-effect over performance, but it seems that for small values of nrows the plain C tends to be (very much) faster. For a simple test using this
setup i got:
ARMADILLO: 111 secs
C ARRAY: 79 secs
in execution time.
EDIT
Here is modification where we work column-wise instead of row-wise and treat each column independently, as suggested by #rubenvb and #mtall. The resulting execution time slightly is decreased (ARMADILLO: 104 secs now), thus showing some improvments over working row-wise:
#include <string>
#include <vector>
#include <iostream>
#include <fstream>
#include <algorithm>
#include <armadillo>
#include <chrono>
using namespace std::chrono;
/***************************
* main()
***************************/
int main( int argc, char *argv[] )
{
unsigned nrows = 100; //number of rows
unsigned ncols = 2000000; //number of cols
const arma::mat huge_mat = arma::randn(nrows, ncols); //create huge matrix
const arma::uvec vec = arma::linspace<arma::uvec>( 0, huge_mat.n_rows-1, huge_mat.n_rows); //create a vector of [0,...,n]
arma::colvec inds = arma::zeros<arma::colvec>( huge_mat.n_rows-1 ); //-1 since we remove only one value at each step.
arma::rowvec simuT = arma::zeros<arma::rowvec>( nrows ); //let's store the results in this simuT vector.
high_resolution_clock::time_point t1 = high_resolution_clock::now();
//compute some normalization over each value of line of this huge matrix:
for(unsigned i=0; i < ncols; i++) {
const arma::colvec current_line = huge_mat.col(i); //extract current line
//for each observation in current_line:
for(unsigned j=0; j < nrows; j++) {
//Take care of side effects first:
if( j == 0 )
inds = current_line(arma::span(1, nrows-1));
else
if( j == 1 ) {
inds(0) = current_line(0);
inds(arma::span(1, nrows-2)) = current_line( arma::span(2, nrows-1) );
} else
inds(arma::span(0, j-1)) = current_line( arma::span(0, j-1) );
//Let's do some computation: huge_mat(i,j) - mean[huge_mat(i,:)] / std([huge_mat(i,:)]) //can compute the mean and std first... for each line.
simuT(j) = (current_line(j) - arma::mean(inds)) / ( std::sqrt( 1+1/((double) nrows-1) ) * arma::stddev(inds) );
}
}
high_resolution_clock::time_point t2 = high_resolution_clock::now();
auto duration = duration_cast<seconds>( t2 - t1 ).count();
std::cout << "ARMADILLO: " << duration << " secs\n";
}
The reason is that Armadillo uses column-major ordering in mat, while your C array uses row-major ordering. This is kind of a big deal because your processor can use instruction vectorization to process multiple elements at once, where this requires contiguous memory chunks.
To verify whether this is the cause, do the same calculation but for columns instead of rows, and check the difference.

calculate the precise, non integer frequencies from time series in (FFTW)

I want to calculate the frequency of time series precisely with at least 3 decimal value.
This is a simple example that calculates the frequency of integer values.
#include <fftw3.h>
#include <cstdio>
#include <cmath>
#include <iostream>
#include <fstream>
#define REAL 0
#define IMAG 1
#define NUM_POINTS 1024
void acquire_signal(double *signal, double *theta) {
/* Generate two sine waves of different frequencies and
* amplitudes.
*/
int i;
for (i = 0; i < NUM_POINTS; ++i) {
theta[i] = (double)i / (double)NUM_POINTS;
signal[i] = 1.0*sin(50.0 * 2.0 * M_PI * theta[i]) +
0.5*sin(80.0 * 2.0 * M_PI * theta[i]);
}
}
int main() {
unsigned flags{0};
double *theta = new double[NUM_POINTS];
double *signal = new double[NUM_POINTS];
fftw_complex result[NUM_POINTS/2+1];
fftw_plan plan = fftw_plan_dft_r2c_1d(NUM_POINTS,
signal,
result,
flags);
acquire_signal(signal,theta);
fftw_execute(plan);
//save signal and result
std::ofstream f1,f2;
f1.open ("signal.txt");
for (int i=0; i<NUM_POINTS; i++){
f1 <<theta[i]<<" "<<signal[i]<<"\n";
}
f1.close();
f2.open("result.txt");
for (int i=0; i<NUM_POINTS/2; i++){
double yf = 2.0/(double)(NUM_POINTS)* sqrt(result[i][REAL]*result[i][REAL]+ result[i][IMAG]* result[i][IMAG]);
f2<< (double)i << " "<<yf <<"\n";
}
f2.close();
fftw_destroy_plan(plan);
delete[] signal,theta;
return 0;
}
But how should I change the code if I have
signal = 1.0*sin(50.350 * 2.0 * M_PI * theta[i]) +
0.5*sin(80.455 * 2.0 * M_PI * theta[i]);
Is it appropriate to change the units of time and frequency?
for example time in 1000*s and frequency in kHz?
Just changing the numbers in sin will shift your lines from 50 and 80 to 50.350 and 80.455 Hz, and assuming you have 1024 lines by 1024 Hz. But you still have 1Hz resolution. You need more lines (x1000) by the same sampling frequency to get bigger resolution.
For example if you want 1/4 Hz resolution you need 4x more samples so by 1024 Hz sample rate you need fs * 4 samples:
...
#define NUM_POINTS (1024 * 4)
double fs = 1024; // Sample rate in Hz
void acquire_signal(double *signal, double *theta) {
/* Generate two sine waves of different frequencies and
* amplitudes.
*/
int i;
for (i = 0; i < NUM_POINTS; ++i) {
theta[i] = (double)i / (double)fs;
signal[i] = 1.0*sin(50.0 * 2.0 * M_PI * theta[i]) +
0.5*sin(80.0 * 2.0 * M_PI * theta[i]);
}
}
....
for (int i=0; i< (NUM_POINTS/2 + 1) ; i++){
double yf = 2.0/(double)(NUM_POINTS)* sqrt(result[i][REAL]*result[i][REAL]+ result[i][IMAG]* result[i][IMAG]);
f2 << (double)i * fs / ( NUM_POINTS ) << " "<<yf <<"\n";
}
0 2.90715e-16
0.25 1.19539e-16
0.5 2.15565e-16
0.75 2.88629e-16
1 3.05084e-16
1.25 3.864e-16
...
49.75 9.47968e-16
50 1
50.25 1.12861e-15
50.5 4.95946e-16
50.75 6.9016e-16
...

Vectors and matrices in C++ for generating a spectrogram

This is my first attempt to generate a spectrogram of a sinusoidal signal with C++.
To generate the spectrogram:
I divided the real sinusoidal signal into B blocks
Applied Hanning window on each block (I assumed there is no overlap). This should give me the inputs for the fft, in[j][k] where k is the block number
Apply fft on in[j][k] for each block and store it.
Here is the script:
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include <fftw3.h>
#include <iostream>
#include <cmath>
#include <fstream>
using namespace std;
int main(){
int i;
int N = 500; // sampled
int Windowsize = 100;
double Fs = 200; // sampling frequency
double T = 1 / Fs; // sample time
double f = 50; // frequency
double *in;
fftw_complex *out;
double t[N]; // time vector
fftw_plan plan_forward;
std::vector<double> signal(N);
int B = N / Windowsize; //number of blocks
in = (double*)fftw_malloc(sizeof(double) * N);
out = (fftw_complex*) fftw_malloc(sizeof(fftw_complex) * N);
//Generating the signal
for(int i = 0; i < = N; i++){
t[i] = i * T;
signal[i] = 0.7 * sin(2 * M_PI * f * t[i]);// generate sine waveform
}
//Applying the Hanning window function on each block B
for(int k = 0; i <= B; k++){
for(int j = 0; j <= Windowsize; j++){
double multiplier = 0.5 * (1 - cos(2 * M_PI * j / (N-1))); // Hanning Window
in[j][k] = multiplier * signal[j];
}
plan_forward = fftw_plan_dft_r2c_1d (Windowsize, in, out, FFTW_ESTIMATE );
fftw_execute(plan_forward);
v[j][k]=(20 * log(sqrt(out[i][0] * out[i][0] + out[i][1] * out[i][1]))) / N;
}
fftw_destroy_plan(plan_forward);
fftw_free(in);
fftw_free(out);
return 0;
}
So, the question is: What is the correct way to declare in[j][k] and v[j][k] variables.
Update:I have declared my v [j] [k] as a matrix : double v [5][249]; according to this site :http://www.cplusplus.com/doc/tutorial/arrays/ so now my script looks like:
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include <fftw3.h>
#include <iostream>
#include <cmath>
#include <fstream>
using namespace std;
int main()
{
int i;
double y;
int N=500;//Number of pints acquired inside the window
double Fs=200;//sampling frequency
int windowsize=100;
double dF=Fs/N;
double T=1/Fs;//sample time
double f=50;//frequency
double *in;
fftw_complex *out;
double t[N];//time vector
double tt[5];
double ff[N];
fftw_plan plan_forward;
double v [5][249];
in = (double*) fftw_malloc(sizeof(double) * N);
out = (fftw_complex*) fftw_malloc(sizeof(fftw_complex) * N);
plan_forward = fftw_plan_dft_r2c_1d ( N, in, out, FFTW_ESTIMATE );
for (int i=0; i<= N;i++)
{
t[i]=i*T;
in[i] =0.7 *sin(2*M_PI*f*t[i]);// generate sine waveform
}
for (int k=0; k< 5;k++){
for (int i = 0; i<windowsize; i++){
double multiplier = 0.5 * (1 - cos(2*M_PI*i/(windowsize-1)));//Hanning Window
in[i] = multiplier * in[i+k*windowsize];
fftw_execute ( plan_forward );
for (int i = 0; i<= (N/2); i++)
{
v[k][i]=(20*log10(sqrt(out[i][0]*out[i][0]+ out[i][1]*out[i] [1])));//Here I have calculated the y axis of the spectrum in dB
}
}
}
for (int k=0; k< 5;k++)//Center time for each block
{
tt[k]=(2*k+1)*T*(windowsize/2);
}
fstream myfile;
myfile.open("example2.txt",fstream::out);
myfile << "plot '-' using 1:2" << std::endl;
for (int k=0; k< 5;k++){
for (int i = 0; i<= ((N/2)-1); i++)
{
myfile << v[k][i]<< " " << tt[k]<< std::endl;
}
}
myfile.close();
fftw_destroy_plan ( plan_forward );
fftw_free ( in );
fftw_free ( out );
return 0;
}
I do not get errors anymore but the spectrogram plot is not right.
As indicated in FFTW's documentation, the size of the output (out in your case) when using fftw_plan_dft_r2c_1d is not the same as the size of the input. More specifically for an input of N real samples, the output consists of N/2+1 complex values. You may then allocate out with:
out = (fftw_complex*) fftw_malloc(sizeof(fftw_complex) * (N/2 + 1));
For the spectrogram output you will then similarly have (N/2+1) magnitudes for each of the B blocks, resulting in the 2D array:
double** v = new double*[B];
for (int i = 0; i < B; i++){
v[i] = new double[(N/2+1)];
}
Also, note that you may reuse the input buffer in for each iteration (filling it with data for a new block). However since you have chosen to compute an N-point FFT and will be storing smaller blocks of Windowsize samples (in this case N=500 and Windowsize=100), make sure to initialize the remaining samples with zeros:
in = (double*)fftw_malloc(sizeof(double) * N);
for (int i = 0; i < N; i++){
in[i] = 0;
}
Note that in addition to the declaration and allocation of the in and v variables, the code you posted suffers from a few additional issues:
When computing the Hanning window, you should divide by the Windowsize-1 not N-1 (since in your case N correspond to the FFT size).
You are taking the FFT of the same block of signal over and over again since you are always indexing with j in the [0,Windowsize] range. You would most likely want to add an offset each time you process a different block.
Since the FFT size does not change, you only need to create the plan once. At the very least if you are going to create your plan at every iteration, you should similarly destroy it (with fftw_destroy_plan) at every iteration.
And a few additional points which may require some thoughts:
Scaling the log-scaled magnitudes by dividing by N might not do what you think. You are much more likely to want to scale the linear-scale magnitudes (ie. divide the magnitude before taking the logarithm). Note that this will result in a constant offset of the spectrum curve, which for many application is not that significant. If the scaling is important for your application, you may have a look at another answer of mine for more details.
The common formula 20*log10(x) typically used to convert linear scale to decibels uses a base-10 logarithm instead of the natural log (base e~2.7182) function which you've used. This would result in a multiplicative scaling (stretching), which may or may not be significant depending on your application.
To summarize, the following code might be more in line with what you are trying to do:
// Allocate & initialize buffers
in = (double*)fftw_malloc(sizeof(double) * N);
for (int i = 0; i < N; i++){
in[i] = 0;
}
out = (fftw_complex*) fftw_malloc(sizeof(fftw_complex) * (N/2 + 1));
v = new (double*)[B];
for (int i = 0; i < B; i++){
v[i] = new double[(N/2+1)];
}
// Generate the signal
...
// Create the plan once
plan_forward = fftw_plan_dft_r2c_1d (Windowsize, in, out, FFTW_ESTIMATE);
// Applying the Hanning window function on each block B
for(int k = 0; k < B; k++){
for(int j = 0; j < Windowsize; j++){
// Hanning Window
double multiplier = 0.5 * (1 - cos(2 * M_PI * j / (Windowsize-1)));
in[j] = multiplier * signal[j+k*Windowsize];
}
fftw_execute(plan_forward);
for (int j = 0; j <= N/2; j++){
// Factor of 2 is to account for the fact that we are only getting half
// the spectrum (the other half is not return by a R2C plan due to symmetry)
v[k][j] = 2*(out[j][0] * out[j][0] + out[j][1] * out[j][1])/(N*N);
}
// DC component and at Nyquist frequency do not have a corresponding symmetric
// value, so should not have been doubled up above. Correct those special cases.
v[k][0] *= 0.5;
v[k][N/2] *= 0.5;
// Convert to decibels
for (int j = 0; j <= N/2; j++){
// 20*log10(sqrt(x)) is equivalent to 10*log10(x)
// also use some small epsilon (e.g. 1e-5) to avoid taking the log of 0
v[k][j] = 10 * log10(v[k][j] + epsilon);
}
}
// Clean up
fftw_destroy_plan(plan_forward);
fftw_free(in);
fftw_free(out);
// Delete this last one after you've done something useful with the spectrogram
for (int i = 0; i < B; i++){
delete[] v[i];
}
delete[] v;
Looks like you're missing the initial declaration for 'v' altogether, and 'in' is not declared properly.
See this page for a related question about creating 2D arrays in C++. As I understand, fftw_malloc() is basically new() or malloc() but aligns the variable properly for the FFTW algorithm.
Since you're not supplying 'v' to the anything related to FFTW, you could use standard malloc() for that.

Plotting the spectrogram

Using the answer to this link:Spectrogram C++ library I have written a code to calculate the spectrogram of a sinusoidal signal:
1-Created a sinusoidal signal.
2- I applied the Hann Window.
3- used FFTW .
4- Calculated log magnitude of frequency coefficients.
Here is the script:
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include <fftw3.h>
#include <iostream>
#include <cmath>
using namespace std;
int main(void)
{
int i;
double y;
int N=256;
double Fs=30000;//sampling frequency
double T=1/Fs;//sample time
double f=5000;//frequency
double *in;
fftw_complex *out;
double t[N-1];//time vector
fftw_plan plan_forward;
in = (double*) fftw_malloc(sizeof(double) * N);
out = (fftw_complex*) fftw_malloc(sizeof(fftw_complex) * N);
for (int i=0; i< N;i++)
{
t[i]=i*T;
in[i] =0.7 *sin(2*M_PI*f*t[i]);// generate sine waveform
double multiplier = 0.5 * (1 - cos(2*M_PI*i/(N-1)));//Hanning Window
in[i] = multiplier * in[i];
}
plan_forward = fftw_plan_dft_r2c_1d ( N, in, out, FFTW_ESTIMATE );
printf ( "\n" );
printf ( " Input Data:\n" );
printf ( "\n" );
for ( i = 0; i < N; i++ )
{
printf ( " %4d %12f\n", i, in[i] );
}
fftw_execute ( plan_forward );
printf ( "\n" );
printf ( " log magnitude of frequency domain components :\n" );
printf ( "\n" );
for ( i = 0; i < N; i++ )
{
cout << log(sqrt(out[i][0]*out[i][0]+ out[i][1]*out[i][1])) ;
}
fftw_destroy_plan ( plan_forward );
fftw_free ( in );
fftw_free ( out );
return 0;
}
The question is how should I proceed from here? Which library should I use to plot the spectrogram? Any suggestions? Thanks.
If you want to stay with C++ and be reasonable cross-platform, you might want to consider Qt as UI and either Qwt or QCustomPlot ar scientific plots widget
Links
http://sourceforge.net/projects/qwt/
http://www.qcustomplot.com/