这里我使用的 fft 类 是使用的这位博主的
https://blog.csdn.net/shadown1ght/article/details/77628389

我在上面封装一下 可以用qt的qvector 使用起来非常方便
把 fft.h 和 fft.cpp 添加进去,调用接口 传出时域数据,会返回频域数据

我这里使用的超强绘图控件是 QCustomplot
支持大量数据显示 500w 条都不卡顿 支持拖动 缩放等交互操作

我的原始数据也就是时域 是 4096个 -10 到 10的随机数

	QVector<double> x_,y_;
    for(auto i = 0; i<4096;i++)
        x_<<i;
        y_<<qrand() % 20 - 10;

fft类的使用

	fft* m_fft = new fft();
	*typedef struct Complex
    double rl;              //实部 用这个当做y轴画图像就可以
    double im;              //虚部
	}Complex;
	//用上面的原始数据的初始化这个结构体类
	QVector<Complex> in_(x_.size());
    QVector<Complex> out_(x_.size());
    for(auto i = 0; i< x_.size();i++)
        in_[i].rl = y_[i];
	//调用接口 生成频域的 out_ 数据 
    m_fft->fft1(in_,in_.size(),out_);
    QVector<double> x1,y1;
    for(auto i = 0;i<out_.size()/2;i++)
        x1<<i;
        //用计算出的实部当做y轴画图
        y1<<out_[i].rl;
    plot2->graph(0)->setData(x1,y1);
    plot2->replot();

fft 类的完整code

fft.h

#ifndef FFT_H
#define FFT_H
#include <QObject>
#include <QDebug>
*               计算傅里叶变换频谱
#define      MAX_MATRIX_SIZE                4194304             // 2048 * 2048
#define      PI                             3.141592653
#define      MAX_VECTOR_LENGTH              10000
typedef struct Complex
    double rl;              //实部 用这个当做y轴画图像就可以
    double im;              //虚部
}Complex;
class fft : public QObject
    Q_OBJECT
public:
    explicit fft(QObject *parent = nullptr);
//   bool fft1(Complex  const inVec[], int  const len, Complex  outVec[]);
    //傅里叶转换 频域
    bool fft1(QVector<Complex>inVec, int  const len, QVector<Complex>&outVec);
    //逆转换
   bool ifft(Complex  const inVec[], int  const len, Complex  outVec[]);
   int get_computation_layers(int  num);
   bool is_power_of_two(int  num);
   void test();
signals:
public slots:
#endif // FFT_H

fft.cpp

#include "fft.h"
#include <QDebug>
fft::fft(QObject *parent) : QObject(parent)
bool fft::fft1(QVector<Complex> inVec, const int len, QVector<Complex> &outVec)
    if ((len <= 0) || (inVec.isEmpty()) || ( outVec.isEmpty()))
        return false;
    if (!is_power_of_two(len))
        return false;
    qDebug()<<"come in";
    // create the weight array
    Complex         *pVec = new Complex[len];
    Complex         *Weights = new Complex[len];
    Complex         *X = new Complex[len];
    int                   *pnInvBits = new int[len];
    //memcpy(pVec, inVec, len*sizeof(Complex));
    for(int i = 0; i < len;i++)
        pVec[i].im = inVec.at(i).im;
        pVec[i].rl = inVec.at(i).rl;
    // 计算权重序列
    double fixed_factor = (-2 * PI) / len;
    for (int i = 0; i < len / 2; i++) {
        double angle = i * fixed_factor;
        Weights[i].rl = cos(angle);
        Weights[i].im = sin(angle);
    for (int i = len / 2; i < len; i++) {
        Weights[i].rl = -(Weights[i - len / 2].rl);
        Weights[i].im = -(Weights[i - len / 2].im);
    int r = get_computation_layers(len);
    // 计算倒序位码
    int index = 0;
    for (int i = 0; i < len; i++) {
        index = 0;
        for (int m = r - 1; m >= 0; m--) {
            index += (1 && (i & (1 << m))) << (r - m - 1);
        pnInvBits[i] = index;
        X[i].rl = pVec[pnInvBits[i]].rl;
        X[i].im = pVec[pnInvBits[i]].im;
    // 计算快速傅里叶变换
    for (int L = 1; L <= r; L++) {
        int distance = 1 << (L - 1);
        int W = 1 << (r - L);
        int B = len >> L;
        int N = len / B;
        for (int b = 0; b < B; b++) {
            int mid = b*N;
            for (int n = 0; n < N / 2; n++) {
                int index = n + mid;
                int dist = index + distance;
                pVec[index].rl = X[index].rl + (Weights[n*W].rl * X[dist].rl - Weights[n*W].im * X[dist].im);                      // Fe + W*Fo
                pVec[index].im = X[index].im + Weights[n*W].im * X[dist].rl + Weights[n*W].rl * X[dist].im;
            for (int n = N / 2; n < N; n++) {
                int index = n + mid;
                pVec[index].rl = X[index - distance].rl + Weights[n*W].rl * X[index].rl - Weights[n*W].im * X[index].im;        // Fe - W*Fo
                pVec[index].im = X[index - distance].im + Weights[n*W].im * X[index].rl + Weights[n*W].rl * X[index].im;
//        memcpy(X, pVec, len*sizeof(Complex));
        for(int i = 0; i< len;i++)
            X[i].im = pVec[i].im;
            X[i].rl = pVec[i].rl;
   // memcpy(outVec, pVec, len*sizeof(Complex));
    for(int i = 0; i < len;i++)
        outVec[i].im = pVec[i].im;
        outVec[i].rl = pVec[i].rl;
    if (Weights)      delete[] Weights;
    if (X)                 delete[] X;
    if (pnInvBits)    delete[] pnInvBits;
    if (pVec)           delete[] pVec;
    return true;
/*bool fft::fft1(const Complex inVec[], const int len, Complex outVec[])
    char msg[256] = "11111 ";
    if ((len <= 0) || (NULL == inVec) || (NULL == outVec))
        return false;
    if (!is_power_of_two(len))
        return false;
    // create the weight array
    Complex         *pVec = new Complex[len];
    Complex         *Weights = new Complex[len];
    Complex         *X = new Complex[len];
    int                   *pnInvBits = new int[len];
    memcpy(pVec, inVec, len*sizeof(Complex));
    // 计算权重序列
    double fixed_factor = (-2 * PI) / len;
    for (int i = 0; i < len / 2; i++) {
        double angle = i * fixed_factor;
        Weights[i].rl = cos(angle);
        Weights[i].im = sin(angle);
    for (int i = len / 2; i < len; i++) {
        Weights[i].rl = -(Weights[i - len / 2].rl);
        Weights[i].im = -(Weights[i - len / 2].im);
    int r = get_computation_layers(len);
    // 计算倒序位码
    int index = 0;
    for (int i = 0; i < len; i++) {
        index = 0;
        for (int m = r - 1; m >= 0; m--) {
            index += (1 && (i & (1 << m))) << (r - m - 1);
        pnInvBits[i] = index;
        X[i].rl = pVec[pnInvBits[i]].rl;
        X[i].im = pVec[pnInvBits[i]].im;
    // 计算快速傅里叶变换
    for (int L = 1; L <= r; L++) {
        int distance = 1 << (L - 1);
        int W = 1 << (r - L);
        int B = len >> L;
        int N = len / B;
        for (int b = 0; b < B; b++) {
            int mid = b*N;
            for (int n = 0; n < N / 2; n++) {
                int index = n + mid;
                int dist = index + distance;
                pVec[index].rl = X[index].rl + (Weights[n*W].rl * X[dist].rl - Weights[n*W].im * X[dist].im);                      // Fe + W*Fo
                pVec[index].im = X[index].im + Weights[n*W].im * X[dist].rl + Weights[n*W].rl * X[dist].im;
            for (int n = N / 2; n < N; n++) {
                int index = n + mid;
                pVec[index].rl = X[index - distance].rl + Weights[n*W].rl * X[index].rl - Weights[n*W].im * X[index].im;        // Fe - W*Fo
                pVec[index].im = X[index - distance].im + Weights[n*W].im * X[index].rl + Weights[n*W].rl * X[index].im;
        memcpy(X, pVec, len*sizeof(Complex));
    memcpy(outVec, pVec, len*sizeof(Complex));
    if (Weights)      delete[] Weights;
    if (X)                 delete[] X;
    if (pnInvBits)    delete[] pnInvBits;
    if (pVec)           delete[] pVec;
    return true;
bool fft::ifft(const Complex inVec[], const int len, Complex outVec[])
    char msg[256] = "11111 ";
        if ((len <= 0) || (!inVec))
            return false;
        if (false == is_power_of_two(len)) {
            return false;
        double         *W_rl = new double[len];
        double         *W_im = new double[len];
        double         *X_rl = new double[len];
        double         *X_im = new double[len];
        double         *X2_rl = new double[len];
        double         *X2_im = new double[len];
        double fixed_factor = (-2 * PI) / len;
        for (int i = 0; i < len / 2; i++) {
            double angle = i * fixed_factor;
            W_rl[i] = cos(angle);
            W_im[i] = sin(angle);
        for (int i = len / 2; i < len; i++) {
            W_rl[i] = -(W_rl[i - len / 2]);
            W_im[i] = -(W_im[i - len / 2]);
        // 时域数据写入X1
        for (int i = 0; i < len; i++) {
            X_rl[i] = inVec[i].rl;
            X_im[i] = inVec[i].im;
        memset(X2_rl, 0, sizeof(double)*len);
        memset(X2_im, 0, sizeof(double)*len);
        int r = get_computation_layers(len);
        if (-1 == r)
            return false;
        for (int L = r; L >= 1; L--) {
            int distance = 1 << (L - 1);
            int W = 1 << (r - L);
            int B = len >> L;
            int N = len / B;
            //sprintf(msg + 6, "B %d, N %d, W %d, distance %d, L %d", B, N, W, distance, L);
            //OutputDebugStringA(msg);
            for (int b = 0; b < B; b++) {
                for (int n = 0; n < N / 2; n++) {
                    int index = n + b*N;
                    X2_rl[index] = (X_rl[index] + X_rl[index + distance]) / 2;
                    X2_im[index] = (X_im[index] + X_im[index + distance]) / 2;
                    //sprintf(msg + 6, "%d, %d: %lf, %lf", n + 1, index, X2_rl[index], X2_im[index]);
                    //OutputDebugStringA(msg);
                for (int n = N / 2; n < N; n++) {
                    int index = n + b*N;
                    X2_rl[index] = (X_rl[index] - X_rl[index - distance]) / 2;           // 需要再除以W_rl[n*W]
                    X2_im[index] = (X_im[index] - X_im[index - distance]) / 2;
                    double square = W_rl[n*W] * W_rl[n*W] + W_im[n*W] * W_im[n*W];          // c^2+d^2
                    double part1 = X2_rl[index] * W_rl[n*W] + X2_im[index] * W_im[n*W];         // a*c+b*d
                    double part2 = X2_im[index] * W_rl[n*W] - X2_rl[index] * W_im[n*W];          // b*c-a*d
                    if (square > 0) {
                        X2_rl[index] = part1 / square;
                        X2_im[index] = part2 / square;
            memcpy(X_rl, X2_rl, sizeof(double)*len);
            memcpy(X_im, X2_im, sizeof(double)*len);
        // 位码倒序
        int index = 0;
        for (int i = 0; i < len; i++) {
            index = 0;
            for (int m = r - 1; m >= 0; m--) {
                index += (1 && (i & (1 << m))) << (r - m - 1);
            outVec[i].rl = X_rl[index];
            outVec[i].im = X_im[index];
            //sprintf(msg + 6, "X_rl[i]: %lf, %lf,  index: %d", out_rl[i], out_im[i], index);
            //OutputDebugStringA(msg);
        if (W_rl)      delete[] W_rl;
        if (W_im)    delete[] W_im;
        if (X_rl)      delete[] X_rl;
        if (X_im)     delete[] X_im;
        if (X2_rl)     delete[] X2_rl;
        if (X2_im)    delete[] X2_im;
        return true;
int fft::get_computation_layers(int num)
    int nLayers = 0;
    int len = num;
    if (len == 2)
        return 1;
    while (true) {
        len = len / 2;
        nLayers++;
        if (len == 2)
            return nLayers + 1;
        if (len < 1)
            return -1;
bool fft::is_power_of_two(int num)
    int temp = num;
    int mod = 0;
    int result = 0;
    if (num < 2)
        return false;
    if (num == 2)
        return true;
    while (temp > 1)
        result = temp / 2;
        mod = temp % 2;
        if (mod)
            return false;
        if (2 == result)
            return true;
        temp = result;
    return false;
void fft::test()
    double vec[] = { 15, 32, 9, 222, 118, 151, 5, 7, 56, 233, 56, 121, 235, 89, 98, 111 };
    int len = sizeof(vec) / sizeof(double);
    Complex *inVec = new Complex[len];
    Complex *outVec = new Complex[len];
    Complex *invert = new Complex[len];
    memset(inVec, 0, len*sizeof(Complex));
    for (int i = 0; i < len; i++)
        inVec[i].rl = vec[i];
    // Fourier transformation
    fft1(inVec, len, outVec);
    // print result
    char msg[256] = "11111 ";
   // OutputDebugStringA("11111 快速傅里叶变换结果为:");
    qDebug()<<"快速傅里叶变换结果为:";
    for (int i = 0; i < len; i++) {
        if (outVec[i].im < 0)
            sprintf(msg + 6, "result[%d]: %lf - %lfi", i+1, outVec[i].rl, -outVec[i].im);
            sprintf(msg + 6, "result[%d]: %lf + %lfi", i + 1, outVec[i].rl, outVec[i].im);
        //OutputDebugStringA(msg);
        qDebug()<<msg;
    //OutputDebugStringA("11111 逆变换结果为:");
    qDebug()<<"逆变换结果为:";
    ifft(outVec, len, invert);
    for (int i = 0; i < len; i++) {
        sprintf(msg + 6, "ifft[%d]: %lf", i + 1, invert[i].rl);
        //OutputDebugStringA(msg);
        qDebug()<<msg;
    delete[] inVec;
    delete[] outVec;
    delete[] invert;

我只用这个接口就可以
在这里插入图片描述

最近在学中频信号处理的一些东西,顺便用 QT 写了一个小工具,可以显示信号的时域波形图、幅度谱、功率谱、二次方谱、四次方谱、八次方谱、瞬时包络、瞬时频率、瞬时相位、非线性瞬时相位、瞬时幅度直方图、瞬时频率直方图、瞬时相位直方图、眼图、星座图、语谱图、瀑布图。 学习QT的UI界面操作和IIR、FIR滤波器设计及滤波,FFT频谱分析。详细代码和完整工程文件,下载即可运行,适合QT新手以及学习IIR、FIR滤波、FFT的C语言算法实现,受益绝对匪浅。 (1)在上位机,使用QT软件编写完成两种或三种频率信号的合成,在图形界面上显示合成的时域波形; (2)使用FFT计算合成信号的频率,在界面上绘制频谱图; (3)使用matlab的Filter Designer设计FIR和IIR滤波器,分别完成一种频率成分的去除,并且在界面上显示滤除后的信号时域波形。 1)QcustomPlot是QT提供的一个第三方库,在使用前需要在QcustomPlot官网上进行下载 2)把解压完的QcustomPlot压缩包中的qcustomplot.h和qcustomplot.cpp文件添加到工程文件中来 3)创建一个weiget提升为QcustomPlot 4)初始化代码 void MainWindow::cust #------------------------------------------------- # Project created by QtCreator 2021-03-09T09:33:42 #------------------------------------------------- QT += core gui serialport printsupport greaterThan(QT_MAJOR_VERSION, 4): QT += wid.. Qt尽管非常强大,但对时频分析的控件支持不是很好。以前主要靠Qwt的Spectrogram来做,但眼瞅着Qt Charts 开源后,Qwt的更新越来越少,真的怕那天它凉凉了。Qt Charts 美工要比Qwt更加摩登,可是显然背后的行业背景不是信号处理,其距离数据分析更进一步(股票啦、人口啦等等),支持二维时频不是很好。经过一段时间尝试,找到了至少三种办法,这里做一个记录。 1 使用 Qt Dat...... 但是在网上搜索了很久,没有一个非常直接的答案。所以我觉得很有必要写一篇文章来说明,在QT msvc2015 32bit环境下如何实现功率谱密度估计。 本文并没有经过严格编辑,但代码是已经可以跑通的,如... ① 开始菜单——打开VS2015开发人员命令提示窗口 ② 找到lib.exe所在文件夹路径,输入命令cd D:\Microsoft Visual Studio 14.0\VC\bin\amd64转到该路径下 ③ 将下载的fftw3压缩包解压,文件夹fftw-3.3.5-dll64中的libfftw3-3.def、libfftw3f3.def、libfftw3 ComplexNumber operator*(ComplexNumber j,ComplexNumber q)//复数乘法。ComplexNumber operator-(ComplexNumber j,ComplexNumber q)//复数减法。ComplexNumber operator+(ComplexNumber j,ComplexNumber q)//复数加法。ComplexNumber::ComplexNumber(double Re, double Im)//构造函数。