【QT】使用QT实现二进制相移键控(BPSK)

一、本文目的&知识

BPSK是二进制相移键控,是一种通信的调制信号的方式,网络上大多都使用MATLAB来实现仿真的。本篇文章将介绍使用QT实现BPSK,目的是理解BPSK数字调制解调的原理,以及QT的基本使用。

二、要实现的功能

1.该程序分为发射机部分和接收机部分,也就是说要两个UI

2.发射机部分
需要打开一个文本文件,文本文件里是101010(字符串),来模拟基带信号
打开文件的同时,显示基带信号的波形
点击发射按钮,对信号进行调制,同时接收机解调信号

3.接收机部分
接收发送机的信号,并且显示解调出来波形
可以将波形保存成文件,文件格式与读取的文件格式相同

总体框架图
在这里插入图片描述

三、BPSK简介

BPSK调制数学表达式 ,其中s(t)是已调信号,m(t)是基带信号,f是载波频率
s ( t ) = A c o s ( 2 π f t + π m ( t ) ) s(t) = Acos(2\pi ft+\pi m(t)) s(t)=Acos(2πft+πm(t))
相干解调:
C o r r ( r ( t ) , s ( t ) ) = ∫ ∞ ∞ r ( τ ) s ( τ − t ) d τ Corr(r(t),s(t))=\int^\infty_\infty r(\tau)s(\tau-t)d\tau Corr(r(t),s(t))=r(τ)s(τt)dτ
低通滤波,在本设计中,低通滤波采用的是差分方程的方式实现
y [ n ] = ( 1 − α ) y [ n − 1 ] + α x [ n ] y[n]=(1-\alpha)y[n-1]+\alpha x[n] y[n]=(1α)y[n1]+αx[n]
其中 α \alpha α是截至频率系数, f c f_c fc是截至频率, T s T_s Ts是抽样时间。
α = 2 π f c T s 2 π f c T s + 1 \alpha=\dfrac{2\pi f_cT_s}{2\pi f_c T_s +1} α=2πfcTs+12πfcTs

四、发射机

在这里插入图片描述

4.1读取文件部分

读取文件部分实现比较简单,就是一个按钮按下信号,然后再到槽函数进行处理,以下是槽函数处理部分代码:

void SendWave::on_pushButton_OpenFile_clicked()
{
    QString bitFileStr=QFileDialog::getOpenFileName(this,
                                                 "选择一个文件",
                                                 "./"
                                                 "*.txt");
    if(bitFileStr.isEmpty()){
        QMessageBox::information(this,"警告","请打开一个文件");
    }else{
        QFile bitFile(bitFileStr);
        bitFile.open(QIODevice::ReadOnly);
        QByteArray ba=bitFile.readAll();
        //装载读取的波形到发射buffer里面
        byteR.clear();
        for(int i=0;i<ba.length();i++){
            double temp=(double)ba.data()[i];
            byteR.push_back(temp);
        }

        bitString=QString(ba);
        bitFile.close();
        int len=bitString.length();

        axisX_r->setMax(len);
        axisX_r->setTickCount(len+1);

        axisX_m->setMax(len);
        axisX_m->setTickCount(len+1);


        DrawLineSeries_R(bitString);
        //direct block drawing
        DrawModulSignal();

    }
}

其中,弹出窗口部分代码依赖QMessageBox这个类

4.2显示波形部分

由于读取文件涉及到了显示信号波形,因此,显示信号波形函数如下:

//original signal
void SendWave::DrawLineSeries_R(QString str){
    QByteArray byte;
    qDebug()<<str;
    byte=str.toLatin1();
    lineSeries_r->clear();
    for(int i=0;i<str.length()*2;i++){
        lineSeries_r->append(QPointF(i, (int)(byte.data()[i]-'0')));
        lineSeries_r->append(QPointF(i+0.99, (int)(byte.data()[i]-'0')));
        qDebug()<<byte.data()[i];
    }

}
//modul signal
void SendWave::DrawModulSignal(){
    QByteArray byte=bitString.toLatin1();
    double inc=(double)(bitString.length())/(double)4097;
    moudulSInc=inc;
    byteM.clear();
    lineSeries_m->clear();
    for(int i=0;i<bitString.length();i++){
        for(double j=(double)i;j<(double)(i+1.0);j+=inc){
            double ph=(byte.data()[i]-'0')*PI;
            double temp=(double)std::sin(j*2*signal_frequency*PI+ph);
            lineSeries_m->append(QPointF(j, temp));
            byteM.push_back((double)temp);
        }
    }
}

在此之前,控制波形的这些对象都需要初始化,其中初始化的操作如下:

axisX_r = new QValueAxis();
    axisY_r = new QValueAxis();
    axisX_c = new QValueAxis();
    axisY_c = new QValueAxis();
    axisX_m = new QValueAxis();
    axisY_m = new QValueAxis();

    lineSeries_r=new QLineSeries();
    lineSeries_c=new QLineSeries();
    lineSeries_m=new QLineSeries();

    chart_r=new QChart();
    chart_carry=new QChart();
    chart_modul=new QChart();



    axisX_r->setTitleText("Time (s)");
    axisY_r->setTitleText("Y");
    axisX_r->setMin(0);
    axisY_r->setMin(0);
    axisY_r->setMax(1);
    axisX_r->setTickCount(10+1);                                  //x 轴刻度是10

    axisX_c->setTitleText("Time (s)");
    axisY_c->setTitleText("Y");
    axisX_c->setMin(0);
    axisX_c->setMax(1);
    axisY_c->setMin(-1);
    axisY_c->setMax(1);
    axisX_c->setTickCount(10+1);                                  //x 轴刻度是10

    axisX_m->setTitleText("Time (s)");
    axisY_m->setTitleText("Y");
    axisX_m->setMin(0);
    axisX_m->setMax(1);
    axisY_m->setMin(-1);
    axisY_m->setMax(1);
    axisX_m->setTickCount(5+1);                                  //x 轴刻度是10

    lineSeries_r->setPointLabelsVisible(false);
    lineSeries_r->setName("base signal");

    lineSeries_c->setPointLabelsVisible(false);
    lineSeries_c->setName("carry signal");

    lineSeries_m->setPointLabelsVisible(false);
    lineSeries_m->setName("modul signal");

    chart_r->addAxis(axisX_r,Qt::AlignBottom);
    chart_r->addAxis(axisY_r,Qt::AlignLeft);
    chart_r->addSeries(lineSeries_r);

    chart_carry->addAxis(axisX_c,Qt::AlignBottom);
    chart_carry->addAxis(axisY_c,Qt::AlignLeft);
    chart_carry->addSeries(lineSeries_c);

    chart_modul->addAxis(axisX_m,Qt::AlignBottom);
    chart_modul->addAxis(axisY_m,Qt::AlignLeft);
    chart_modul->addSeries(lineSeries_m);

    lineSeries_r->attachAxis(axisX_r);
    lineSeries_r->attachAxis(axisY_r);

    lineSeries_c->attachAxis(axisX_c);
    lineSeries_c->attachAxis(axisY_c);

    lineSeries_m->attachAxis(axisX_m);
    lineSeries_m->attachAxis(axisY_m);
    lineSeries_m->setUseOpenGL(true);

    ui->graphicsView->setChart(chart_r);
    ui->graphicsView->setRenderHint(QPainter::Antialiasing);

    ui->graphicsView_c->setChart(chart_carry);
    ui->graphicsView_c->setRenderHint(QPainter::Antialiasing);

    ui->graphicsView_m->setChart(chart_modul);
    ui->graphicsView_m->setRenderHint(QPainter::Antialiasing);

显示波形这部分代码还是很复杂的,除了c++代码操作之外,还有就是工程的操作,大体如下:
要素1:修改.pro文件,就是:QT += core gui charts,因为需要用到这个资源
要素2:在UI文件里要添加Graphics View这个组件,并且将其提升为QChartView
要素3:需要包含QChartView QValueAxis QLineSeries这几个类

发送信号部分

波形显示出来之后,实际上是存到一个数据结构里面了,需要将它发给接收机部分,也是通过一个按钮来实现的

void SendWave::on_pushButton_Send_clicked()
{
    emit sendM_Signal(byteM);
}

4.3信号与槽部分

发送的信号和接收的信号是自定义信号与槽实现的,需要在对应的头文件自定义信号与槽函数,发射机的头文件:

signals:
    void sendM_Signal(QVector<double> dat);

对应的接收机的头文件:

private slots:
    void receiveM_Signal(QVector<double> dat);

五、接收机

接收机示意图

5.1接收机接收信号

接收信号不仅仅需要的是信号本身,还有一个重要的参数是载波主频,这个载波主频我是通过FFT来计算出来的,代码太长就不予展示了,具体在这个链接:浅谈FFT以及FFT算法的基本实现

void Receive::receiveM_Signal(QVector<double> dat){

    Rfrequency=(double)mainfre;
    qDebug()<<"read main frequency:"<<Rfrequency;
    //vector全部转到数组
    RecvDatLen=dat.length();
    double temp_array[dat.length()]={0};
    MainWindow::vector2array(temp_array,dat,(int)dat.size());


    qDebug()<<incr;
    bitInc=incr;
    lineSeries_squ->clear();
    RecvTime=0.0;
    for(int i=0;i<dat.length();i++){
        lineSeries_squ->append(RecvTime,temp_array[i]*temp_array[i]);
        RecvTime+=incr;
    }
    axisX_squ->setMax(RecvTime);

    DrawCorSignal(dat,temp_array);
    DrawSamSignal(corSignal,temp_array);

    qDebug()<<"Recv time:"<<RecvTime;
}

5.2 相干解调+画这个波形

void Receive::DrawCorSignal(QVector<double> dat,double datArray[]){
    double x=0.0;
    int prej=0;
    int nowj=0;

    corSignal.clear();
    lineSeries_cor->clear();
    for(int i=0;i<dat.length();i++){
        double temp=std::sin(2*PI*Rfrequency*x)*datArray[i];
        lineSeries_cor->append(x,temp);
        corSignal.push_back(temp);
        x+=bitInc;
        nowj=x;
        if(prej!=nowj)x=(double)nowj;
        prej=x;
    }
    axisX_cor->setMax(x);
}

5.3 低通滤波+采样+画波形

void Receive::DrawSamSignal(QVector<double> dat,double datArray[]){
    double x=0.0;
    double outData[dat.length()]={0};
    int prej=0;
    int nowj=0;

    LPF_Process(dat,outData,dat.length());
    lineSeries_sam->clear();
    outSignal.clear();
    for(int i=0;i<dat.length();i++){
        lineSeries_sam->append(x,outData[i]);

        outSignal.push_back(QPair<double,double>(x,outData[i]));
        x+=bitInc;
        nowj=x;
        if(prej!=nowj)x=(double)nowj;
        prej=x;
    }
    axisX_sam->setMax(x);
}

低通滤波函数:

double Receive::LPF_Init(double fc,double Ts){
    double b=2.0*PI*fc*Ts;

    return b/(b+1);
}

void Receive::LPF_Process(QVector<double> inData,double outData[],int len){
    double out_pre=inData.at(0);
    double alpha=LPF_Init(1.8,bitInc);
    for(int i=0;i<len;i++){
        double out=out_pre+alpha*(inData.at(i)-out_pre);
        outData[i]=out;
        out_pre=out;
    }
}

六、复频域分析

实现界面如下,用来估计载波中心频率
在这里插入图片描述

七、误码率分析(略)

八、总结

本次设计介绍了基于 C++和 QT 框架设计的 BPSK(Binary Phase Shift Keying,二进制相移键控)通信模拟程序,还对其中涉及的理论知识和技术进行了分析和解释。这项工作旨在设计了一个直观的工具,用于展示了 C++和 QT 在通信系统仿真方面的可行性。

  • 21
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值