#c #signal-processing #fftw
#c #обработка сигналов #fftw
Вопрос:
Я использую функцию преобразования Гильберта из источника FFTW. Потому что я собираюсь использовать его в своем DAQ с режимом потоковой передачи данных. Функция работает нормально, но скорость вычисления низкая, что приведет к переполнению FIFO. Я слышал, что перемещение fftw_plan()
снаружи из hilbert()
для повторного plan
использования может быть полезным, однако, это ошибка, как только я это сделал, сказав Exception thrown at 0x0000000070769240 (libfftw3-3.dll) in CppFFTW.exe: 0xC0000005: Access violation reading location 0x0000000000000000.
на fftw_destroy_plan(plan);
. У кого-нибудь есть подобный опыт или даже лучшее решение для ускорения hilbert()
вычислений?
Вот что я пробовал (отредактировано 12/26 2020 года):
#include <iostream>
#include <fftw3.h>
#include <time.h>
using namespace std;
//macros for real and imaginary parts
#define REAL 0
#define IMAG 1
//length of complex array
#define N 8
fftw_complex* out;
fftw_plan plan;
void hilbert(const double* in, fftw_complex* out)
{
// copy the data to the complex array
for (int i = 0; i < N; i) {
out[i][REAL] = in[i];
out[i][IMAG] = 0;
}
// creat a DFT plan and execute it
//fftw_plan plan = fftw_plan_dft_1d(N, out, out, FFTW_FORWARD, FFTW_ESTIMATE);
fftw_execute(plan);
// destroy a plan to prevent memory leak
//fftw_destroy_plan(plan);
int hN = N >> 1; // half of the length (N/2)
int numRem = hN; // the number of remaining elements
// multiply the appropriate value by 2
//(those should multiplied by 1 are left intact because they wouldn't change)
for (int i = 1; i < hN; i) {
out[i][REAL] *= 2;
out[i][IMAG] *= 2;
}
// if the length is even, the number of the remaining elements decrease by 1
if (N % 2 == 0)
numRem--;
else if (N > 1) {
out[hN][REAL] *= 2;
out[hN][IMAG] *= 2;
}
// set the remaining value to 0
// (multiplying by 0 gives 0, so we don't care about the multiplicands)
memset(amp;out[hN 1][REAL], 0, numRem * sizeof(fftw_complex));
// creat a IDFT plan and execute it
//plan = fftw_plan_dft_1d(N, out, out, FFTW_BACKWARD, FFTW_ESTIMATE);
fftw_execute(plan);
// do some cleaning
//fftw_destroy_plan(plan);
//fftw_cleanup();
// scale the IDFT output
for (int i = 0; i < N; i) {
out[i][REAL] /= N;
out[i][IMAG] /= N;
}
}
/* Displays complex numbers in the form a /- bi. */
void displayComplex(fftw_complex* y)
{
for (int i = 0; i < N; i ) {
if (y[i][IMAG] < 0)
cout << y[i][REAL] << "-" << abs(y[i][IMAG]) << "i" << endl;
else
cout << y[i][REAL] << " " << y[i][IMAG] << "i" << endl;
}
}
/* Test */
int main()
{
fftw_plan plan = fftw_plan_dft_1d(N, out, out, FFTW_FORWARD, FFTW_ESTIMATE);
plan = fftw_plan_dft_1d(N, out, out, FFTW_BACKWARD, FFTW_ESTIMATE);
// input array
double x[N];
// output array
fftw_complex y[N];
// fill the first of some numbers
for (int i = 0; i < N; i) {
x[i] = i 1; // i.e.{1 2 3 4 5 6 7 8}
}
//clock_t begin = clock();
// compute the FFT of x and store the result in y.
hilbert(x, y);
// display the result
cout << "Hilbert =" << endl;
displayComplex(y);
fftw_destroy_plan(plan);
fftw_destroy_plan(plan);
}
Вот исходный код:
#include <iostream>
#include <fftw3.h>
using namespace std;
//macros for real and imaginary parts
#define REAL 0
#define IMAG 1
//length of complex array
#define N 8
void hilbert(const double* in, fftw_complex* out)
{
// copy the data to the complex array
for (int i = 0; i < N; i) {
out[i][REAL] = in[i];
out[i][IMAG] = 0;
}
// creat a DFT plan and execute it
fftw_plan plan = fftw_plan_dft_1d(N, out, out, FFTW_FORWARD, FFTW_ESTIMATE);
fftw_execute(plan);
// destroy a plan to prevent memory leak
fftw_destroy_plan(plan);
int hN = N >> 1; // half of the length (N/2)
int numRem = hN; // the number of remaining elements
// multiply the appropriate value by 2
//(those should multiplied by 1 are left intact because they wouldn't change)
for (int i = 1; i < hN; i) {
out[i][REAL] *= 2;
out[i][IMAG] *= 2;
}
// if the length is even, the number of the remaining elements decrease by 1
if (N % 2 == 0)
numRem--;
else if (N > 1) {
out[hN][REAL] *= 2;
out[hN][IMAG] *= 2;
}
// set the remaining value to 0
// (multiplying by 0 gives 0, so we don't care about the multiplicands)
memset(amp;out[hN 1][REAL], 0, numRem * sizeof(fftw_complex));
// creat a IDFT plan and execute it
plan = fftw_plan_dft_1d(N, out, out, FFTW_BACKWARD, FFTW_ESTIMATE);
fftw_execute(plan);
// do some cleaning
fftw_destroy_plan(plan);
fftw_cleanup();
// scale the IDFT output
for (int i = 0; i < N; i) {
out[i][REAL] /= N;
out[i][IMAG] /= N;
}
}
/* Displays complex numbers in the form a /- bi. */
void displayComplex(fftw_complex* y)
{
for (int i = 0; i < N; i ) {
if (y[i][IMAG] < 0)
cout << y[i][REAL] << "-" << abs(y[i][IMAG]) << "i" << endl;
else
cout << y[i][REAL] << " " << y[i][IMAG] << "i" << endl;
}
}
/* Test */
int main()
{
// input array
double x[N];
// output array
fftw_complex y[N];
// fill the first of some numbers
for (int i = 0; i < N; i) {
x[i] = i 1; // i.e.{1 2 3 4 5 6 7 8}
}
// compute the FFT of x and store the result in y.
hilbert(x, y);
// display the result
cout << "Hilbert =" << endl;
displayComplex(y);
}
Точный результат
Hilbert =
1 3.82843i
2-1i
3-1i
4-1.82843i
5-1.82843i
6-1i
7-1i
8 3.82843i
Комментарии:
1. Может быть, вы могли бы ответить на этот вопрос, переориентировав вопрос на эту часть в своем заявлении: «однако, как только я это сделал, он глючит «.. В документах к FFTW точно указано, чтобы повторно использовать fftw_plan . Итак, что вы пробовали и какой эффект «глючит», который вы видите?
2. Отредактировано, причина, по которой я сказал это, заключается в том, что я не уверен, верен ли мой метод.
3. Зачем переопределять чистую структуру? Вместо
out[i][REAL]
этого сделайте:out[i].re
. Я даже не уверен, что они эквивалентны [ваш может быть UB], и.re
это то, как определяется структура и что ожидает библиотека. Аналогично дляout[i].im
4. Полезно ли это для изменения нескольких алфавитов …?
5.
out
это указатель , поэтому доступ к нему черезout[i][anything]
неправильный . Я удивлен, что он скомпилирован, потому что нет способа сообщить компилятору о количестве строк. Это UB, и вы получаете доступ за пределы конца массива. Когда вы это делаете:out[i][1]
вы не устанавливаете.im
частьout[i]
. Вы разгромилиout[i 1]
. В конце концов, это приводит к тому, что вы просматриваете конец вашего массива [stack] — это UB [неопределенное поведение].
Ответ №1:
Для ускорения вы определенно хотите повторно использовать планы FFTW, если это возможно. При повторном использовании плана в нескольких вызовах hilbert удалите fftw_destroy_plan(plan)
строку внутри hilbert, иначе план не будет действителен для использования в следующем вызове. Вместо этого уничтожьте план в конце программы.
Редактировать: ранее я пропустил, что вы используете два плана: один для прямого преобразования, другой для обратного. В hilbert()
, удалите все вызовы fftw_plan_dft_1d
, fftw_destroy_plan
, и fftw_cleanup
; единственный вызов FFTW должен быть fftw_execute
. Вместо этого создайте оба плана заранее один раз в начале программы и уничтожьте их в конце программы.
Комментарии:
1. Спасибо! Вы имеете в виду, что я должен удалить 2 строки
fftw_destroy_plan(plan)
?2. Упс, я пропустил ранее, что вы используете два плана FFTW. Вы правы, обе строки уничтожения должны быть удалены. Пожалуйста, посмотрите правку в моем сообщении.
3. Вы имеете в виду
plan = fftw_plan_dft_1d(N, out, out, FFTW_BACKWARD, FFTW_ESTIMATE);
тоже? Для лучшего понимания не могли бы вы дать мне отредактированный сценарий, чтобы показать, какую часть нужно удалить, а какую часть нужно переместить за пределыhilbert()
, а какую часть все еще нужно оставить в функции? Большое спасибо!4. Пожалуйста, взгляните на мой пост, я попробовал, как вы сказали, но подобная ошибка все равно произошла, не могли бы вы указать, где я ошибся? Спасибо!
5. Глядя на ваш обновленный код, команды создания плана и уничтожения больше не
hilbert
используются; это хорошо. Чего не хватает, так это того, что теперь эти планы должны быть созданы в начале илиmain
. Конечно, попытка выполнить план без его инициализации не сработает. Чтобы создать планы, объявите двеfftw_plan
переменные как глобальные (два плана: один для прямого, другой для обратного) и используйтеfftw_plan_dft_1d
для инициализации каждого из них. Затем в концеmain
вызовитеfftw_destroy_plan
каждый из них для очистки.
Ответ №2:
После нескольких попыток вот успешное hilbert()
улучшение FFTW. Переместил два fftw_plan
и позволил им инициализироваться в main()
, плюс, fftw_destroy_plan
они тоже были перемещены.
#include <iostream>
#include <fftw3.h>
#include <time.h>
using namespace std;
//macros for real and imaginary parts
#define REAL 0
#define IMAG 1
//length of complex array
#define N 8
fftw_complex y[N];
void hilbert(const double* in, fftw_complex* out, fftw_plan plan_forward, fftw_plan plan_backward)
{
// copy the data to the complex array
for (int i = 0; i < N; i) {
out[i][REAL] = in[i];
out[i][IMAG] = 0;
}
// creat a DFT plan and execute it
//fftw_plan plan = fftw_plan_dft_1d(N, out, out, FFTW_FORWARD, FFTW_ESTIMATE);
fftw_execute(plan_forward);
// destroy a plan to prevent memory leak
//fftw_destroy_plan(plan_forward);
int hN = N >> 1; // half of the length (N/2)
int numRem = hN; // the number of remaining elements
// multiply the appropriate value by 2
//(those should multiplied by 1 are left intact because they wouldn't change)
for (int i = 1; i < hN; i) {
out[i][REAL] *= 2;
out[i][IMAG] *= 2;
}
// if the length is even, the number of the remaining elements decrease by 1
if (N % 2 == 0)
numRem--;
else if (N > 1) {
out[hN][REAL] *= 2;
out[hN][IMAG] *= 2;
}
// set the remaining value to 0
// (multiplying by 0 gives 0, so we don't care about the multiplicands)
memset(amp;out[hN 1][REAL], 0, numRem * sizeof(fftw_complex));
// creat a IDFT plan and execute it
//plan = fftw_plan_dft_1d(N, out, out, FFTW_BACKWARD, FFTW_ESTIMATE);
fftw_execute(plan_backward);
// do some cleaning
//fftw_destroy_plan(plan_backward);
//fftw_cleanup();
// scale the IDFT output
for (int i = 0; i < N; i) {
out[i][REAL] /= N;
out[i][IMAG] /= N;
}
}
/* Displays complex numbers in the form a /- bi. */
void displayComplex(fftw_complex* y)
{
for (int i = 0; i < N; i ) {
if (y[i][IMAG] < 0)
cout << y[i][REAL] << "-" << abs(y[i][IMAG]) << "i" << endl;
else
cout << y[i][REAL] << " " << y[i][IMAG] << "i" << endl;
}
}
/* Test */
int main()
{
// input array
double x[N];
// output array
//fftw_complex y[N];
fftw_plan plan_forward = fftw_plan_dft_1d(N, y, y, FFTW_FORWARD, FFTW_ESTIMATE);
fftw_plan plan_backward = fftw_plan_dft_1d(N, y, y, FFTW_BACKWARD, FFTW_ESTIMATE);
// fill the first of some numbers
for (int i = 0; i < N; i) {
x[i] = i 1; // i.e.{1 2 3 4 5 6 7 8}
}
// compute the FFT of x and store the result in y.
hilbert(x, y, plan_forward, plan_backward);
// display the result
cout << "Hilbert =" << endl;
displayComplex(y);
fftw_destroy_plan(plan_forward);
fftw_destroy_plan(plan_backward);
}
Ответ №3:
Предваряется моими лучшими комментариями…
Хорошо… После нескольких неудачных запусков…
В вашей второй версии были некоторые проблемы.
Примечательно, что вы не выделяли out
.
Кроме того, на всякий случай я считаю, что вам нужны две fftw_plan
структуры, одна для прямого и одна для обратного.
Они должны быть выделены / инициализированы один раз main
.
И удалите все вызовы выделения / уничтожения hilbert
. Таким образом, его можно вызывать несколько раз, просто изменяя размещенные данные out
.
Код очистки может перейти к нижней части main
.
Я создал очищенную и рабочую версию вашей второй версии. Я показал разницу между старым и новым кодом с cpp
помощью условных обозначений:
#if 0
// old code ...
#else
// new code ...
#endif
Итак, вот оно:
#include <iostream>
#include <cstring>
#include <fftw3.h>
#include <time.h>
using namespace std;
//macros for real and imaginary parts
#define REAL 0
#define IMAG 1
//length of complex array
#define N 8
fftw_plan plan_fwd;
fftw_plan plan_bwd;
fftw_complex *out;
void
hilbert(const double *in, fftw_complex *out)
{
// copy the data to the complex array
for (int i = 0; i < N; i) {
out[i][REAL] = in[i];
out[i][IMAG] = 0;
}
// creat a DFT plan and execute it
// fftw_plan plan = fftw_plan_dft_1d(N, out, out, FFTW_FORWARD, FFTW_ESTIMATE); // This line has been moved to the main function
fftw_execute(plan_fwd);
// destroy a plan to prevent memory leak
#if 0
fftw_destroy_plan(plan_fwd);
#endif
int hN = N >> 1; // half of the length (N/2)
int numRem = hN; // the number of remaining elements
// multiply the appropriate value by 2
// (those should multiplied by 1 are left intact because they wouldn't change)
for (int i = 1; i < hN; i) {
out[i][REAL] *= 2;
out[i][IMAG] *= 2;
}
// if the length is even, the number of the remaining elements decrease by 1
if (N % 2 == 0)
numRem--;
else if (N > 1) {
out[hN][REAL] *= 2;
out[hN][IMAG] *= 2;
}
// set the remaining value to 0
// (multiplying by 0 gives 0, so we don't care about the multiplicands)
memset(amp;out[hN 1][REAL], 0, numRem * sizeof(fftw_complex));
// creat a IDFT plan and execute it
#if 0
plan_bwd = fftw_plan_dft_1d(N, out, out, FFTW_BACKWARD, FFTW_ESTIMATE);
#endif
fftw_execute(plan_bwd);
// do some cleaning
#if 0
fftw_destroy_plan(plan_bwd);
fftw_cleanup();
#endif
// scale the IDFT output
for (int i = 0; i < N; i) {
out[i][REAL] /= N;
out[i][IMAG] /= N;
}
}
/* Displays complex numbers in the form a /- bi. */
void
displayComplex(fftw_complex * y)
{
for (int i = 0; i < N; i ) {
if (y[i][IMAG] < 0)
cout << y[i][REAL] << "-" << abs(y[i][IMAG]) << "i" << endl;
else
cout << y[i][REAL] << " " << y[i][IMAG] << "i" << endl;
}
}
/* Test */
int
main()
{
#if 1
out = (fftw_complex *) malloc(sizeof(fftw_complex) * N);
#endif
plan_fwd = fftw_plan_dft_1d(N, out, out, FFTW_FORWARD, FFTW_ESTIMATE);
plan_bwd = fftw_plan_dft_1d(N, out, out, FFTW_BACKWARD, FFTW_ESTIMATE);
// input array
double x[N];
// output array
fftw_complex y[N];
// fill the first of some numbers
for (int i = 0; i < N; i) {
x[i] = i 1; // i.e.{1 2 3 4 5 6 7 8}
}
clock_t begin = clock();
// compute the FFT of x and store the result in y.
hilbert(x, y);
// display the result
cout << "Hilbert =" << endl;
displayComplex(y);
clock_t end = clock();
double time_spent = (double) (end - begin) / CLOCKS_PER_SEC;
printf("%f", time_spent);
fftw_destroy_plan(plan_fwd);
fftw_destroy_plan(plan_bwd);
fftw_cleanup();
}
Вот вывод программы:
Hilbert =
0.125 0i
0.5 0i
0.75 0i
1 0i
0.625 0i
0 0i
0 0i
0 0i
0.000171
Комментарии:
1. Спасибо, Крейг, но выходной ответ был неправильным
2. Извините за поздний обзор, я думаю, что то, что вы представили, почти соответствовало концепции, однако выходной ответ был неправильным. Но я думаю, что мы можем продолжать улучшать базу вашего кода. Кстати, я не тот парень, который дал отрицательный результат 😉
3. Я обновил фактическое выходное значение в части вопроса, пожалуйста, взгляните. Спасибо!