Векторы и матрицы в C++ для генерации спектрограммы

Это моя первая попытка сгенерировать спектрограмму синусоидального сигнала с помощью C++. Чтобы сгенерировать спектрограмму:

  1. Я разделил реальный синусоидальный сигнал на B блоков

  2. Применил окно Ханнинга к каждому блоку (я предположил, что перекрытия нет). Это должно дать мне входные данные для fft, in[j][k], где k — номер блока.

  3. Примените fft к in[j][k] для каждого блока и сохраните его.

Вот сценарий:

#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;
    }

Итак, вопрос: как правильно объявить переменные in[j][k] и v[j][k].

Обновление: я объявил свой v [j] [k] матрицей: double v [5][249]; в соответствии с этим сайтом: http://www.cplusplus.com/doc/tutorial/arrays/ теперь мой скрипт выглядит так:

#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;
  }

Я больше не получаю ошибок, но график спектрограммы неверен.


person Jack    schedule 01.09.2015    source источник
comment
Делайте правильный отступ в коде и удаляйте лишние пустые строки, чтобы сделать его более читабельным.   -  person crashmstr    schedule 01.09.2015
comment
Опасность, опасность! Результатом fftw_malloc будет 1D, а не 2D-массив, поэтому использование его таким образом in[j][k] закончится плохо. Вы, вероятно, захотите какой-нибудь вариант in[k * Windowsize + j]   -  person user4581301    schedule 01.09.2015


Ответы (2)


Как указано в FFTW. документации, размер вывода (out в вашем случае) при использовании fftw_plan_dft_r2c_1d не совпадает с размером ввода. В частности, для ввода N реальных выборок вывод состоит из N/2+1 комплексных значений. Затем вы можете выделить out с помощью:

out = (fftw_complex*) fftw_malloc(sizeof(fftw_complex) * (N/2 + 1));

Для вывода спектрограммы у вас будет аналогичным образом (N/2+1) величин для каждого из B блоков, в результате чего получится двумерный массив:

double** v = new double*[B];
for (int i = 0; i < B; i++){
  v[i] = new double[(N/2+1)];
}

Также обратите внимание, что вы можете повторно использовать входной буфер in для каждой итерации (заполняя его данными для нового блока). Однако, поскольку вы решили вычислить N-точечное БПФ и будете хранить меньшие блоки из Windowsize отсчетов (в данном случае N=500 и Windowsize=100), обязательно инициализируйте оставшиеся отсчеты нулями:

in = (double*)fftw_malloc(sizeof(double) * N);
for (int i = 0; i < N; i++){
  in[i] = 0;
}

Обратите внимание, что в дополнение к объявлению и размещению переменных in и v в опубликованном вами коде есть несколько дополнительных проблем:

  • При вычислении окна Ханнинга следует делить на Windowsize-1, а не N-1 (поскольку в вашем случае N соответствуют размеру БПФ).
  • Вы снова и снова выполняете БПФ одного и того же блока signal, поскольку вы всегда индексируете j в диапазоне [0,Windowsize]. Скорее всего, вы захотите добавлять смещение каждый раз, когда обрабатываете другой блок.
  • Поскольку размер БПФ не меняется, вам нужно создать план только один раз. По крайней мере, если вы собираетесь создавать свой план на каждой итерации, вы должны аналогичным образом уничтожать его (с помощью fftw_destroy_plan) на каждой итерации.

И несколько дополнительных моментов, которые могут потребовать некоторых размышлений:

  • Масштабирование логарифмических величин путем деления на N может не дать того, что вы думаете. Скорее всего, вы захотите масштабировать линейные величины (т. е. разделить величину перед логарифмированием). Обратите внимание, что это приведет к постоянному смещению кривой спектра, что для многих приложений не так важно. Если масштабирование важно для вашего приложения, вы можете посмотреть другой мой ответ для получения более подробной информации.
  • Общая формула 20*log10(x), обычно используемая для преобразования линейной шкалы в децибелы, использует десятичный логарифм вместо естественная функция log (база e~2.7182), которую вы использовали. Это приведет к мультипликативному масштабированию (растяжению), которое может быть или не быть значительным в зависимости от вашего приложения.

Подводя итог, следующий код может больше соответствовать тому, что вы пытаетесь сделать:

// 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;
person SleuthEye    schedule 02.09.2015
comment
Спасибо. Я изменил свой код на ваш, но получаю: ошибка: ‘v’ не был объявлен в этой области видимости v = new double*[B]; - person Jack; 02.09.2015
comment
Таким образом, вы смотрите на v как на массив, когда определяете его как v = new (double*)[B]; так можно ли использовать v[k][j] ? - person Jack; 02.09.2015
comment
v - это 2D-массив (double**, только что отредактировал сообщение, чтобы включить это). - person SleuthEye; 02.09.2015
comment
Спасибо, и не должно быть круглых скобок для нового (двойного *) [B]; примечание: попробуйте удалить круглые скобки вокруг идентификатора типа, что я получил ... Теперь я получаю Ошибка сегментации (сброс ядра) - person Jack; 02.09.2015
comment
,,Есть ли у вас какие-либо советы, как избавиться от ошибки Segmentation fault (core dumped)? Не проще ли просто подписаться на cplusplus.com/doc/tutorial/arrays и объявить матрицу v как недействительную процедуру (int myarray[][B][N])? - person Jack; 02.09.2015
comment
Вы можете объявить его как myarray[5][500], но не как myarray[B][N] в стандартном С++ (хотя некоторые компиляторы могут разрешать синтаксис массива динамического размера в качестве расширения). Ошибка сегментации могла быть вызвана неправильной переменной, используемой для условия остановки в файле for (int k=0; i<B; k++). В общем случае отладчик может помочь избавиться от ошибок сегментации. - person SleuthEye; 02.09.2015

Похоже, вы вообще пропустили начальное объявление для «v», а «in» не объявлено должным образом.

См. эту страницу для связанного вопрос о создании 2D-массивов в C++. Насколько я понимаю, fftw_malloc() в основном является new() или malloc(), но правильно выравнивает переменную для алгоритма FFTW.

Поскольку вы не предоставляете 'v' чему-либо, связанному с FFTW, вы можете использовать для этого стандартный malloc().

person bjornruffians    schedule 01.09.2015