基于FPGA的BPSK、QPSK以及OQPSK实现_fpga实现bpsk调制-程序员宅基地

技术标签: fpga开发  小白学FPGA  

大家第一次接触PSK是什么时候呢?我第一次是在通信原理里面的数字带通传输系统里面接触到了数字调制原理。然后由于自己现在在学FPGA,所以就想着看能不能用FPGA实现一下书本里面所学的BPSK、QPSK以及OQPSK。

首先介绍一下几种调制原理:

一、二进制相移键控(BPSK)

相移键控是利用载波的相位变化来传递信息,而振幅和频率保持不变。在BPSK中,通常用初始相位0和π分别表示二进制“1”和“0”。因此,BPSK信号的时域表达式为  

    

其中φn表示第n个符号的绝对相位,即    

                    

因此,BPSK信号的表达式也可写为

BPSK信号的调制有两种方法,一种是模拟调制方法,如图1(a)所示,另一种则是键控法,如图1(b)所示,即通过开关电路来输出相应相位的载波。我们在这里采用键控法来产生BPSK信号。

(a)模拟调制方法

(b)键控法

图1BPSK信号调制原理框图

 二、正交相移键控(QPSK) 

QPSK即正交相移键控又被称为四相移键控。它具有4种相位状态对应四组数据,即00,01,10,11。

QPSK信号的产生有两种方法,第一种是正交调相,其原理如图2(a)所示。输入的基带信号(单极性归零码元)经过串并转换电路变成两路二进制不归零双极性码元a和b。并行码元a和b的持续时间均是输入码元的2倍。a路码元与载波cos\left ( wct \right )相乘得到I路信号,b路码元与载波-sin(wct)相乘得到Q路信号,再经过相乘电路将I、Q两路信号进行叠加得到QPSK信号。

第二种是相位选择法,其原理如图2(a)所示。这时输入的基带信号经过串并转换后用于控制一个相位选择电路,按照当时输入的双比特ab,决定选择哪一个相位的载波输出。

(a)正交调相法产生QPSK信号

(b)相位选择法产生QPSK信号

图2QPSK信号调制原理框图

三、偏置正交相移键控(OQPSK)

OQPSK称为偏置正交相移键控,它与QPSK的唯一区别在于两个正交分量的两个比特a和b在时间上错开了半个码元周期。在QPSK体制中,它的相邻码元最大相位差达到了180°,由于这样的相位突变在频带受限的系统中会引起信号包络的很大起伏,这是不希望的,所以为了减少此相位突变,将两个正交的分量比特a和b在时间上错开半个码元,使之不能同时改变。 

 下面就进入程序设计及仿真部分:

整个程序设计主要包括伪随机序列产生模块、载波产生模块、调制模块。

一、随机序列产生模块

module m_data(
    input sys_clk,
    input rst_n,
    output m_data1,
    output reg [1:0] m_data2,
    output reg [1:0] om_data2
    );
parameter N = 32;
parameter counter = 10_000;
reg [8:0] reg1;
reg reg2;
reg [N-1:0] count;
reg count1_flag;
reg count2_flag;
assign m_data1 = reg1[0];
always@(posedge sys_clk or negedge rst_n)
begin
    if(!rst_n)
        count <= 32'd0;
    else if(count == counter - 1'b1)
        count <= 32'd0;
    else
        count <= count + 1'b1;
end
always@(posedge sys_clk or negedge rst_n)
begin
    if(!rst_n)
        reg1 <= 9'b000111101;
    else if(count == counter - 1'b1)
    begin
        reg1[8] <= reg1[0]^reg1[2];
        reg1[7:0] <= reg1[8:1]; 
    end
end

always@(posedge sys_clk or negedge rst_n)
begin
    if(!rst_n)
    begin
        om_data2 <= 2'b00;
        reg2 <= 1'b0;
        count1_flag <= 1'b1;
    end
    else if(count == counter - 1'b1)
    begin
        if(count1_flag == 1'b1)
        begin
            om_data2[1] <= m_data1;
            count1_flag <= 1'b0;
        end
        else
        begin 
            om_data2[0] <= m_data1;
            count1_flag <= 1'b1;
        end
    end
end
always@(posedge sys_clk or negedge rst_n)
begin
    if(!rst_n)
    begin
        m_data2 <= 2'b00;
        reg2 <= 1'b0;
        count2_flag <= 1'b1;
    end
    else if(count == counter - 1'b1)
    begin
        if(count2_flag == 1'b1)
        begin
            reg2 <= m_data1;
            count2_flag <= 1'b0;
        end
        else
        begin 
            m_data2[1] <= reg2;
            m_data2[0] <= m_data1;
            count2_flag <= 1'b1;
        end
    end
end
endmodule

二、载波产生模块

module sine_cosine(
    input sys_clk,
    input rst_n,
    output [7:0] sine,
    output [7:0] cosine
    );
parameter N = 32;
/*
Fout = 50khz    Fclk = 50Mhz    Fout = FEORD*(Fclk / 2**N)
FWORD = (Fout * 2**N) / Fclk = 50_000 * 65536 * 65536 / 50_000_000 = 4294967
*/
parameter FWORD = 4294967;
parameter PWORD = 128;
reg [N-1:0] addr;
wire [8:0] sine_addr;
wire [8:0] cosine_addr;
assign sine_addr = addr[N-1:N-9];
assign cosine_addr = addr[N-1:N-9] + PWORD;
always@(posedge sys_clk or negedge rst_n)
begin
    if(!rst_n)
        addr <= 32'd0;
    else
        addr <= addr + FWORD;  
end
rom_sine rom_sine_inst1 (
  .clka(sys_clk),    // input wire clka
  .addra(sine_addr),  // input wire [8 : 0] addra
  .douta(sine)  // output wire [7 : 0] douta
);
rom_sine rom_sine_inst2 (
  .clka(sys_clk),    // input wire clka
  .addra(cosine_addr),  // input wire [8 : 0] addra
  .douta(cosine)  // output wire [7 : 0] douta
);
endmodule

三、调制模块

(1)BPSK

module bpsk(
    input sys_clk,
    input rst_n,
    input m_data1,
    output [8:0] bpsk
    );
reg [31:0] bpsk_PWORD;
wire [8:0] rom_addr;
wire [7:0] bpsk_reg;
assign bpsk = {1'b0,bpsk_reg};
always@(posedge sys_clk or negedge rst_n)
begin
    if(!rst_n)
        bpsk_PWORD <= 32'd0;
    else if(m_data1)
        bpsk_PWORD <= 32'd0;
    else
        bpsk_PWORD <= 32'd256;
end
rom_addr  rom_addr_inst(
    .sys_clk(sys_clk),
    .rst_n(rst_n),
    .PWORD(bpsk_PWORD),
    .rom_addr(rom_addr)
);
rom_sine rom_sine_inst (
  .clka(sys_clk),    // input wire clka
  .addra(rom_addr),  // input wire [8 : 0] addra
  .douta(bpsk_reg)  // output wire [7 : 0] douta
);
endmodule

(2)QPSK

module qpsk(
    input sys_clk,
    input rst_n,
    input [1:0] m_data2,
    input [7:0] sine,
    input [7:0] cosine,
    output reg  [8:0] qpsk
);
reg [1:0] I_tmp;
reg [1:0] Q_tmp;
reg [7:0] I_cos;
reg [7:0] Q_sin;
always@(posedge sys_clk or negedge rst_n)
begin
    if(!rst_n)
        begin
            I_tmp <= 2'b00;
            Q_tmp <= 2'b00;
        end
    else
        begin
             I_tmp <= (m_data2[0]) ? 2'b01 : 2'b11; 
             Q_tmp <= (m_data2[1]) ? 2'b01 : 2'b11;
        end
end
always@(posedge sys_clk or negedge rst_n)
begin
    if(!rst_n)
        Q_sin <= 8'd0;
    else
    begin
        case(Q_tmp)
        2'b01:
            Q_sin <= sine;
        2'b11:
            Q_sin <= -sine;
        endcase
    end
end
always@(posedge sys_clk or negedge rst_n)
begin
    if(!rst_n)
        I_cos <= 8'd0;
    else
    begin
        case(I_tmp)
        2'b01:
            I_cos <= cosine;
        2'b11:
            I_cos <= -cosine;
        endcase
    end
end
always@(posedge sys_clk or negedge rst_n)
begin
    if(!rst_n)
        qpsk <= 9'd0;
    else
        qpsk <= I_cos + Q_sin;
end
endmodule

(3)OQPSK

OQPSK的代码基本上和QPSK保持一致,区别仅在于输入基带信号的不同。

module oqpsk(
    input sys_clk,
    input rst_n,
    input [1:0] m_data2,
    input [7:0] sine,
    input [7:0] cosine,
    output reg [8:0] oqpsk
);
reg [1:0] I_tmp;
reg [1:0] Q_tmp;
reg [7:0] I_cos;
reg [7:0] Q_sin;
always@(posedge sys_clk or negedge rst_n)
begin
    if(!rst_n)
        begin
            I_tmp <= 2'b00;
            Q_tmp <= 2'b00;
        end
    else
        begin
             I_tmp <= (m_data2[0]) ? 2'b01 : 2'b11; 
             Q_tmp <= (m_data2[1]) ? 2'b01 : 2'b11;
        end
end
always@(posedge sys_clk or negedge rst_n)
begin
    if(!rst_n)
        Q_sin <= 8'd0;
    else
    begin
        case(Q_tmp)
        2'b01:
            Q_sin <= sine;
        2'b11:
            Q_sin <= ~sine +1'b1;
        endcase
    end
end
always@(posedge sys_clk or negedge rst_n)
begin
    if(!rst_n)
        I_cos <= 8'd0;
    else
    begin
        case(I_tmp)
        2'b01:
            I_cos <= cosine;
        2'b11:
            I_cos <= ~cosine + 1'b1;
        endcase
    end
end
always@(posedge sys_clk or negedge rst_n)
begin
    if(!rst_n)
        oqpsk <= 9'd0;
    else
        oqpsk <= I_cos + Q_sin;
end
endmodule

以上都是各子模块的代码,最终我们需要在顶层模块中调用以上模块,然后编写仿真文件才能进行仿真。 

 四、仿真结果分析

通过仿真,我们得到如图所示的结果。m_data1是我们生成的数字基带信号,可以看到,当m_data1为1时,我们的BPSK信号相位为0;m_data1为0时,BPSK信号的相位为180°,达到了设计要求。m_data2是m_data1串并转换之后的信号,可以看出它的码元长度是基带信号的2倍,m_data2[1]和m_data2[0]分别代表I、Q两路信号,当I、Q两路信号为10时,相位为7π/4;为00时,相位为5π/4;为11时,相位为1π/4;为01时,相位为3π/4。m_data3是m_data2的I、Q两路信号错开半个码元周期的信号,此时我们可以看到信号从00变到11经历了00到10再到11即相位从5π/4到7π/4再到1π/4这样一个过程,避免了信号包络起伏大和相位发生突变。

到此,本次的设计基本上就结束了。由于水平有限,有些想法和思路还不成熟,存在很大问题。但是我还是想把我的想法分享给大家,希望大家看到之后能有所启发,同时也希望大家在发现错误之后能给我指出来,我会继续对此进行完善。 

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/m0_60956202/article/details/132840919

智能推荐

hbuilderX连接雷电模拟器_hbuilderx运行到雷电模拟器-程序员宅基地

文章浏览阅读5.8k次,点赞5次,收藏7次。1.下载并安装雷电模拟器2.hbuilderX中选择“运行-运行到手机或模拟器-ADB路径设置”,填写雷电模拟器的adb.exe的路径3.设置Android模拟器端口:55554.雷电模拟器打开调试模式:打开“设置-关于平板电脑”,多少次点击“版本号”,直至提醒进入开发者选项,返回上一级,在“关于平板电脑”上方可以看到“开发者选项”,进入并打开“USB调试”5.hbuilderX中选择“运行-运行到手机或模拟器-运行到Android APP基座”..._hbuilderx运行到雷电模拟器

计算机相关国外期刊,计算机 国外期刊-程序员宅基地

文章浏览阅读1k次。1. Artificial Intelligence ( SCI 源刊 EI源刊)http://www.elsevier.comhttp://www.elsevier.com/wps/find/journaldescription.cws_home/505601/description#descriptionISSN: 0004-3702ARTIFICIAL INTELLIGENCEMonthly..._找一篇计算机相关的外文期刊论文。要求:必须是近几年的期刊论文,且内容和计算机相

JSP热部署的实现原理-程序员宅基地

文章浏览阅读3.2k次。一. 概述名词解释:所谓热部署,就是在应用正在运行的时候升级软件,却不需要重新启动应用。对于Java应用程序来说,热部署就是在运行时更新Java类文件。在基于Java的应用服务器实现热部署的过程中,类装入器扮演着重要的角色。大多数基于Java的应用服务器,包括EJB服务器和Servlet容器,都支持热部署。类装入器不能重新装入一个已经装入的类,但只要使用一个_jsp热部署

C语言实现一个Linux环境下的shell_c语言实现一个shellkuangjjia-程序员宅基地

文章浏览阅读1.1k次。C语言实现一个Linux环境下的shell_c语言实现一个shellkuangjjia

rook-ceph15.2完全卸载_rook-ceph 卸载-程序员宅基地

文章浏览阅读1.9k次。rook-ceph O版结合k8s部署,部署失败后,一定要彻底清理,否则磁盘osd识别不到硬盘。1.首先先删除k8s上rook-ceph下的所有资源,按照部署的顺序反着操作。kubectl delete -f cluster.yamlkubectl delete -f operator.yamlkubectl delete -f common.yamlkubectl delete -f crds.yaml(其实后面的common和crds可以不删,为了稳妥一点还是删了吧)删资源的过程中,可._rook-ceph 卸载

电路交换时代的终结:2G终于要退出历史舞台_2g电路交换-程序员宅基地

文章浏览阅读990次。从1G到4G,每一代移动通信技术的频谱效率都会向前迈进一大步。逐步清退2G,让位给新技术,运营商不仅可重耕2G优质频段,还能大幅节省弥足珍贵的频谱资源——如果传统2/3G CS语音退役并迁移至4G VoLTE,运营商可释放达90%的频谱资源。为此,全球多家运营商已陆续关闭2G网络。然而,展望5G,从CS语音到VoLTE再到5G VoNR,这背后却让我们看到了一个更加辉煌的时代——电路交换时代逐渐退出历史舞台。最早的电话网络叫电路交换(circuit switching..._2g电路交换

随便推点

Micro Drive Data Recovery_which may indicate disk corruption.-程序员宅基地

文章浏览阅读607次。Our Microdrive data recovery service recovers lost, deleted or damaged photographic images, videos or data from any type of Microdrive. Our recovery specialists are able to un-delete Microdrive file_which may indicate disk corruption.

android权限赋予流程_adb 给app授权-程序员宅基地

文章浏览阅读3k次。android 权限 permission_adb 给app授权

linux支持大磁盘配置_size of device too big to be-程序员宅基地

文章浏览阅读1.5k次。问题:mkfs.ext3: Size of device /dev/sdc1 too big to be expressed in 32 bits using a blocksize of 4096.CentOS 6.3 x64 支持大于2T,小于16T的数据盘 1.支持2T2.支持x>16T磁盘分区,需要安装插件;e2fsprogs-1.43.4.tar.gz(CentOS 6._size of device too big to be

JS接收url上的参数_servlet如何接收js的url参数-程序员宅基地

文章浏览阅读220次。用正则表达式:function GetQueryString(name) { var reg = new RegExp(“(^|&)”+ name +”=([^&]*)(&|$)”); var r = window.location.search.substr(1).match(reg); if(r!=null)return unescape(r[2]); r_servlet如何接收js的url参数

实战在两台服务器部署项目_autojob两台服务器怎么部署-程序员宅基地

文章浏览阅读3.9k次。目前有的服务器配置两台装了win10的同等配置的虚拟机,两个服务器在一个局域网内 。暂且称为服务器A,服务器B。服务器A安装tomcat,部署应用。服务器B安装mysql和redis数据库。安装服务器环境服务器A安装jdk1.8环境打开环境变量配置。计算机→属性→高级系统设置→高级→环境变量,在系统变量中配置。配置JAVA_HOME。新建,变量名JAVA_HOME,变量值,..._autojob两台服务器怎么部署

11 亿条数据压缩到 12GB,TDengine 在陕煤矿山项目的落地实践_tdengine单表支持最大容量-程序员宅基地

文章浏览阅读3.6k次。从写入性能到查询性能均大幅满足现场实际需求,整体压缩率可以达到3/100。_tdengine单表支持最大容量

推荐文章

热门文章

相关标签