基于FPGA的BPSK、QPSK以及OQPSK实现

发布于:2024-04-17 ⋅ 阅读:(108) ⋅ 点赞:(0)

大家第一次接触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这样一个过程,避免了信号包络起伏大和相位发生突变。

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