C++:尝试使用std::rotate和fftw_complex数据时产生错误:“数组必须用大括号括起的初始值设定项初始化”

k5hmc34c  于 2023-03-20  发布在  其他
关注(0)|答案(1)|浏览(189)

我试图理解如何利用fft来处理我用软件定义无线电捕获的数据。我发现了C++的fftw 3库,并试图查找一些文档或教程,发现没有一吨去。
不过,我确实找到了这个tutorial。执行fft/ifft/etc的代码对我来说编译和工作都很好,但当我试图实现fftShift函数时,我得到了以下编译器错误:

In file included from /usr/include/c++/9/algorithm:62,
                 from CppFFTW.cpp:13:
/usr/include/c++/9/bits/stl_algo.h: In instantiation of ‘_RandomAccessIterator std::_V2::__rotate(_RandomAccessIterator, _RandomAccessIterator, _RandomAccessIterator, std::random_access_iterator_tag) [with _RandomAccessIterator = double (*)[2]]’:
/usr/include/c++/9/bits/stl_algo.h:1449:27:   required from ‘_FIter std::_V2::rotate(_FIter, _FIter, _FIter) [with _FIter = double (*)[2]]’
CppFFTW.cpp:76:54:   required from here
/usr/include/c++/9/bits/stl_algo.h:1371:16: error: array must be initialized with a brace-enclosed initializer
 1371 |     _ValueType __t = _GLIBCXX_MOVE(*__p);
      |                ^~~
/usr/include/c++/9/bits/stl_algo.h:1373:22: error: invalid array assignment
 1373 |     *(__p + __n - 1) = _GLIBCXX_MOVE(__t);
      |                      ^
/usr/include/c++/9/bits/stl_algo.h:1394:16: error: array must be initialized with a brace-enclosed initializer
 1394 |     _ValueType __t = _GLIBCXX_MOVE(*(__p + __n - 1));
      |                ^~~
/usr/include/c++/9/bits/stl_algo.h:1396:10: error: invalid array assignment
 1396 |     *__p = _GLIBCXX_MOVE(__t);
      |          ^

下面是我用来编译代码的代码行:g++ CppFFTW.cpp -lfftw3 -lm
g++--版本的输出:“g++(Ubuntu 9.4.0- 10版本)9.4.0”
这个错误消息对我来说没有多大意义,但我认为它意味着算法的std::rotate函数由于某种原因不再适合fftw_complex数据类型。
这给我留下了几个问题
1.为什么这个方法在教程视频中有效,但对我无效?
1.有哪些方法可以帮助您理解此错误消息的含义?
1.如何实现一个有效的fftShift函数?
1.我是否应该使用不同的编译器版本?
fftShift应该做的是将零分量移到中心,如本例所示
x一个一个一个一个x一个一个二个x
以下是fftShift定义的定义:编辑:原始(已损坏)fftShift

// FFT shift for complex data 
void fftShift(fftw_complex *data)
{
    // even number of elements
    if (N % 2 == 0) {
        std::rotate(&data[0], &data[N >> 1], &data[N]);
    }
    // odd number of elements
    else {
        std::rotate(&data[0], &data[(N >> 1) + 1], &data[N]);
    }
}

下面是代码的其余部分(与问题无关的函数/调用被省略):编辑:根据Ted的贡献,添加了#include并修复了fftShift函数。

* 
 Example code for how to utilize the FFTW libs
 Adapted from damian-dz C++ Tutorial
 https://github.com/damian-dz/CppFFTW/tree/master/CppFFTW
 (https://www.youtube.com/watch?v=geYbCA137PU&t=0s)
 To compile, run: g++ CppFFTW.cpp -lfftw3 -lm
 -lfftw3 -lm links the code to the fftw3 library
*/

#include <fftw3.h>
#include <iostream>
#include <cmath>
#include <algorithm>
#include <complex> // Added post feedback from Ted

// macros for real & imaginary parts
#define REAL 0
#define IMAG 1

// length of the complex arrays
#define N 8

/* Computres the 1-D Fast Fourier Transform. */
void fft(fftw_complex *in, fftw_complex *out)
{
    // create a DFT plan
    fftw_plan plan = fftw_plan_dft_1d(N, in, out, FFTW_FORWARD, FFTW_ESTIMATE);
    // execute the plan
    fftw_execute(plan);
    // clean up
    fftw_destroy_plan(plan);
    fftw_cleanup();
}

/* Displays complex numbers in the form a +/- bi */
void displayComplex(fftw_complex *y)
{
    for (int idx = 0; idx < N; ++idx) {
        if (y[idx][IMAG] < 0) {
            std::cout << y[idx][REAL] << " - " << abs(y[idx][IMAG]) << "i" << std::endl;
        } else {
            std::cout << y[idx][REAL] << " + " << abs(y[idx][IMAG]) << "i" << std::endl;
        }
    }
}

// Edit: Adjusted fftShift function per Ted's feedback
void fftShift(std::complex<double>* data) {
    static_assert(sizeof(fftw_complex) == sizeof(std::complex<double>));

    // even number of elements
    if(N % 2 == 0) {
        std::rotate(&data[0], &data[N >> 1], &data[N]);
    }
    // odd number of elements
    else {
        std::rotate(&data[0], &data[(N >> 1) + 1], &data[N]);
    }
}
// Edit: Accompanying fftShift to re-cast data
void fftShift(fftw_complex* data) {
    fftShift(reinterpret_cast<std::complex<double>*>(data));
}

// Original (broken) FFT shift for complex data 
void fftShift(fftw_complex *data)
{
    // even number of elements
    if (N % 2 == 0) {
        std::rotate(&data[0], &data[N >> 1], &data[N]);
    }
    // odd number of elements
    else {
        std::rotate(&data[0], &data[(N >> 1) + 1], &data[N]);
    }
}

/* Test */
int main()
{
    // input array
    fftw_complex x[N];

    // output array
    fftw_complex y[N];

    // fill the first array with some numbers
    for (int idx = 0; idx < N; ++idx) {
        x[idx][REAL] = idx + 1;
        x[idx][IMAG] = 0;
    }
    
    // compute the FFT of x and store results in y
    fft(x, y);
    // display the results
    std::cout << "FFT =" << std::endl;
    displayComplex(y);

    // "shifted" results
    fftShift(y);
    std::cout << "\nFFT shifted =" << std::endl;
    displayComplex(y);
    
    return 0;
}

没有看到任何明显的问题,我可以看到适用于我的情况(可能是错误的,我还在学习C++),所以我做了一个帐户,这是我在这里的第一个问题。欢迎提供反馈,以便我可以使我的问题更容易理解。

xzv2uavs

xzv2uavs1#

std::complex
对于类型为std::complex<T>的任何对象zreinterpret_cast<T(&)[2]>(z)[0]z的真实的部,reinterpret_cast<T(&)[2]>(z)[1]z的虚部。
此要求的目的是保持C++库复数类型和C语言复数类型(及其数组)之间的二进制兼容性,这两种类型具有相同的对象表示要求。
现在,fftw_complex不一定是C语言中定义的复数类型--但是你可能会做一个类似的类型转换,事实上,fftw3.h头中的注解表明它很可能是正确的:

/* If <complex.h> is included, use the C99 complex type.  Otherwise
   define a type bit-compatible with C99 complex */
#if !defined(FFTW_NO_Complex) && defined(_Complex_I) && defined(complex) && defined(I)
#  define FFTW_DEFINE_COMPLEX(R, C) typedef R _Complex C
#else
#  define FFTW_DEFINE_COMPLEX(R, C) typedef R C[2]
#endif

fftw_complex的C++定义变成了double[2],这也是std::rotate失败的原因--你不能给数组赋值。
因此,我将使用fftw_complex*定义函数,如下所示:

void fftShift(std::complex<double>* data) {
    static_assert(sizeof(fftw_complex) == sizeof(std::complex<double>));

    // even number of elements
    if(N % 2 == 0) {
        std::rotate(&data[0], &data[N >> 1], &data[N]);
    }
    // odd number of elements
    else {
        std::rotate(&data[0], &data[(N >> 1) + 1], &data[N]);
    }
}

void fftShift(fftw_complex* data) {
    fftShift(reinterpret_cast<std::complex<double>*>(data));
}

int main() {
    fftw_complex data[N];
    fftShift(data);
}

一个更好的替代方法是在C++代码中使用std::complex<double>,并且只在调用fftw函数时强制转换为fftw_complex*

std::vector<std::complex<double>> wave;

fftw_function( reinterpret_cast<fftw_complex*>(wave.data()), wave.size() );

1.为什么这个方法在教程视频中有效,但对我无效?
这是因为他们在教程中使用了Visual Studio,而那个版本的标准库中的std::rotate实现使用了std::iter_swap,它实际上能够交换整个数组,如果iter_swap是标准强制要求的,我不知道,但是g++,clang++,icx和MSVC实现能够做到这一点,但是,g++、clang++和icx在它们的rotate实现中不使用它们自己的iter_swap--至少在本例中不使用。
我们可以从实际使用std::iter_swaprotate @ cppreference中借用一个rotate实现,然后所有四个都可以rotate您的fftw_complex-使用它们自己的iter_swapDemo
1.有哪些方法可以帮助您理解此错误消息的含义?
这意味着std::rotate尝试将一个double[2]赋值给另一个double[2]as-if 方法是:

double a[2]{};
double b[2]{};
a = b;          // error: invalid array assignment

1.如何实现一个有效的fftShift函数?
见上文。
1.我是否应该使用不同的编译器版本?
不,即使是g++clang++的更新版本也会抱怨同样的问题。您可以使用Visual Studio或rotate的不同实现(如www.example.com上的实现cppreference.com)-但我建议您只调整和使用std::complex<double>,在需要时将其转换为fftw_complex

相关问题