概述
基于FPGA的映射调制实现
- 项目简述
- 数学建模
- MATLAB仿真
- FPGA代码
- FPGA映射的代码
- FPGA的映射测试代码
- FPGA与MATLAB的交叉验证
- 小结
- 参考文献
- 总结
项目简述
在通信过程中,经常能碰见调制解调,这里将讲解一种映射调制的方法,并且给出相应的代码供大家学习。一般调制位于无线通信中比较靠后的未知,在交织之后。这里的映射调制并没有介入载波,只是进行了相应的星座图映射,至于最后载波的加入,将在后面的博客中学习。
数学建模
首先我们常见的QPSK与16-QAM的星座图如下:
由星座图可以得到编码之后的如下信息:
QPSK编码对应坐标轴大小
16-QAM编码对应坐标轴大小
星座图映射的对象为位交织输出
u
=
(
u
0
,
u
1
,
.
.
.
,
u
N
l
d
p
c
−
2
,
u
N
l
d
p
c
−
1
)
u=(u_0,u_1,...,u_{N_{ldpc}-2},u_{N_{ldpc}-1})
u=(u0,u1,...,uNldpc−2,uNldpc−1) ,
N
l
d
p
c
=
16200
N_{ldpc}=16200
Nldpc=16200主要是因为LDPC编码之后每包的长度就是16200,星座图映射分为两步:
(1)解复用,将
u
u
u 分解为
N
s
u
b
s
t
r
e
a
m
s
N_{substreams}
Nsubstreams 个子流
(2)将
N
s
u
b
s
t
r
e
a
m
s
N_{substreams}
Nsubstreams 个子流根据调制方式映射到对应星座图。
那么对于不同的调制方式, $N_{substreams}的值如下:
Modulation | Number of sub-streams N s u b s t r e a m s N_{substreams} Nsubstreams |
---|---|
QPSK | 2 |
16-QAM | 8 |
首先我们要完成解复用,其实说白了解复用就是一个串并变换。
解复用模块将输入
v
d
i
v_{di}
vdi 映射到输出
b
i
,
j
b_{i,j}
bi,j,其中
i
i
i为第
i
i
i个子流,
j
=
d
i
d
i
v
N
s
u
b
s
t
r
e
a
m
s
j=di div N_{substreams}
j=di div Nsubstreams。其中对于QPSK调制,i与
d
i
m
o
d
N
s
u
b
s
t
r
e
a
m
s
di mod N_{substreams}
di mod Nsubstreams的值一样,对于16-QAM则进行了相应的变换,如下表:
Modulation format | QPSK | |
---|---|---|
d i m o d N s u b s t r e a m s di mod N_{substreams} di mod Nsubstreams | 0 | 1 |
e | 0 | 1 |
Modulation format | 16-QAM | |||||||
---|---|---|---|---|---|---|---|---|
d i m o d N s u b s t r e a m s di mod N_{substreams} di mod Nsubstreams | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
e | 7 | 1 | 4 | 2 | 5 | 3 | 6 | 0 |
进行完解复用之后就应该进行相应的映射操作,将会将上面产生的 N s u b s t r e a m s N_{substreams} Nsubstreams个并行通道映射到相应的 σ sigma σ个码元,映射的关系如下表:
Modulation | N s u b s t r e a m s N_{substreams} Nsubstreams | σ sigma σ |
---|---|---|
QPSK | 2 | 2 |
16-QAM | 8 | 4 |
对于 QPSK 调制, Nsubstreams 个子流每次组成一个调制符号,而对于 16-QAM,
N
s
u
b
s
t
r
e
a
m
s
N_{substreams}
Nsubstreams 个子流每次组成两个调制符号,前半个子流组成一个调制符号,后半个子流组成一个调制符号。具体公式如下:
定义调制符号:
y
d
o
=
[
y
0
,
d
o
,
.
.
.
,
y
σ
−
1
,
d
o
]
y_{do}=[y_{0,do},...,y_{sigma-1,do}]
ydo=[y0,do,...,yσ−1,do]
对于QPSK调制:
[
y
0
,
d
o
,
.
.
.
,
y
σ
−
1
,
d
o
]
=
[
b
0
,
d
o
,
.
.
.
,
b
N
s
u
b
s
t
r
e
a
m
s
−
1
,
d
o
]
[y_{0,do},...,y_{sigma-1,do}]=[b_{0,do},...,b_{N_{substreams}-1,do}]
[y0,do,...,yσ−1,do]=[b0,do,...,bNsubstreams−1,do]
对于16-QAM调制:
[
y
0
,
2
d
o
,
.
.
.
,
y
σ
−
1
,
2
d
o
]
=
[
b
0
,
d
o
,
.
.
.
,
b
N
s
u
b
s
t
r
e
a
m
s
/
2
−
1
,
d
o
]
[y_{0,2do},...,y_{sigma-1,2do}]=[b_{0,do},...,b_{N_{substreams}/2-1,do}]
[y0,2do,...,yσ−1,2do]=[b0,do,...,bNsubstreams/2−1,do]
[
y
0
,
2
d
o
+
1
,
.
.
.
,
y
σ
−
1
,
2
d
o
+
1
]
=
[
b
N
s
u
b
s
t
r
e
a
m
s
/
2
,
d
o
,
.
.
.
,
b
N
s
u
b
s
t
r
e
a
m
s
−
1
,
d
o
]
[y_{0,2do+1},...,y_{sigma-1,2do+1}]=[b_{N_{substreams}/2,do},...,b_{N_{substreams}-1,do}]
[y0,2do+1,...,yσ−1,2do+1]=[bNsubstreams/2,do,...,bNsubstreams−1,do]
然后就完成了相应的映射操作。也就将原来的码映射到了星座表中的坐标上。
MATLAB仿真
上面我们已经讲解了映射调制的星座图对应关系,但是相信大家还都不是特别明白,接下来给出相应的MATLAB代码供大家学习,学习的时候需要结合数学模型与MATLAB仿真相互学习。
sim_options = struct(...
'CONSTELLATION', '16-QAM' ...
);
fid1 = fopen('mapper_in.txt','r');
DataIn = fscanf(fid1,'%d');
%------------------------------------------------------------------------------
%------------------------------------------------------------------------------
switch sim_options.CONSTELLATION
case 'QPSK'
V = 2; % Bits per cell
case '16-QAM'
V = 4;
otherwise, error('sim_options UNKNOWN CONSTELLATION');
end
%------------------------------------------------------------------------------
% Parameters Definition
%------------------------------------------------------------------------------
CONSTEL = sim_options.CONSTELLATION;
%------------------------------------------------------------------------------
% Procedure
%------------------------------------------------------------------------------
%bits to cells
data = DataIn';
streams = V * 2;
switch CONSTEL
case 'QPSK'
mapping = [0 1 2 3] + 1;
case '16-QAM'
mapping = [7 1 4 2 5 3 6 0] + 1;
end
numBits = length(data);
numCells = floor(numBits/streams);
numBits = numCells*streams;
data = data(1:numBits);
%make streams
cells = reshape(data', streams, size(data,2)/streams)';
%swap this to agree with spec, above line maps the wrong way
cells(:, mapping) = cells;
cells = reshape(cells', [] , 1);
cells = reshape(cells, V , [])';
if ~isempty(cells)
DataOut = bi2de(cells,'left-msb');
else
DataOut = [];
end
save DataOut.mat DataOut
上面的代码主要是源于电子发烧友学院,只是博主为了方便理解,进行了一小部分的更改。需要的同学可以关注上面的课程信息。阅读完相应的MATLAB代码,再观察前面的数学模型,会发现映射调制非常简单。
设置调制方式:
找到输入数据:
设置映射方式:
解复用:
映射到星座图:
FPGA代码
上面我们已经详细介绍了映射调制的原理与MATLAB代码,这里将给出相应的MATLAB代码。代码非常简单,这里也就不对说了。
FPGA映射的代码
tx_Mapper模块:
`timescale 1ns / 1ps
// *********************************************************************************
// Project Name : OSXXXX
// Author : zhangningning
// Email : nnzhang1996@foxmail.com
// Website :
// Module Name : tx_Mapper.v
// Create Time : 2020-05-31 11:23:32
// Editor : sublime text3, tab size (4)
// CopyRight(c) : All Rights Reserved
//
// *********************************************************************************
// Modification History:
// Date By Version Change Description
// -----------------------------------------------------------------------
// XXXX zhangningning 1.0 Original
//
// *********************************************************************************
module tx_Mapper(
input clk ,
input rst_n ,
input s_config_tvalid ,
input s_config_tdata ,
input s_data_tvalid ,
input s_data_tdata ,
output reg s_data_tready ,
input s_data_tlast ,
output reg m_data_tvalid ,
output reg [ 3:0] m_data_tdata ,
input m_data_tready ,
output reg m_data_tlast
);
//========================================================================================
//**************Define Parameter and Internal Signals**********************************
//========================================================================================/
reg mode_type ;
reg [12:0] Ncell ;
reg [ 1:0] QPSK_cnt ;
reg [ 3:0] QPSK_data ;
reg map_ena ;
reg [ 7:0] QPSK_mod_data ;
reg QPSK_mod_valid ;
reg [ 2:0] QAM_cnt ;
reg [ 7:0] QAM_data ;
reg map_enb ;
reg [ 7:0] QAM_mod_data ;
reg QAM_mod_valid ;
reg map_wea ;
reg [11:0] map_addra ;
reg [ 7:0] map_dina ;
reg [12:0] map_addrb ;
wire [ 3:0] map_doutb ;
reg data_ordy ;
reg [12:0] m_cnt ;
reg s_data_tvalid_reg ;
wire start_data_invld ;
//========================================================================================
//************** Main Code **********************************
//========================================================================================/
assign start_data_invld = ~s_data_tvalid_reg && s_data_tvalid;
//====================================================================================
// configure parameter
//====================================================================================
always@(posedge clk)
if(rst_n == 1'b0)
mode_type <= 0;
else if(s_config_tvalid == 1'b1)
mode_type <= s_config_tdata; ///mode_type = 0,QPSK ; mode_type = 1 ,16QAM;
always@(*)
case(mode_type)
1'b0 : Ncell = 13'd8100;
1'b1 : Ncell = 13'd4050;
default : Ncell = 13'd0;
endcase
always@(posedge clk)
if(mode_type == 1'b0 && s_data_tvalid == 1'b1)
QPSK_cnt <= QPSK_cnt + 1'b1;
else
QPSK_cnt <= 0;
always@(posedge clk)
if(rst_n == 1'b0)
QPSK_data <= 0;
else case(QPSK_cnt)
2'b00 : QPSK_data[0] <= s_data_tdata;
2'b01 : QPSK_data[1] <= s_data_tdata;
2'b10 : QPSK_data[2] <= s_data_tdata;
2'b11 : QPSK_data[3] <= s_data_tdata;
default : QPSK_data <= QPSK_data;
endcase
always@(posedge clk)
if(QPSK_cnt == 2'b11)
map_ena <= 1'b1;
else
map_ena <= 1'b0;
always@(posedge clk)
if(map_ena)begin
QPSK_mod_data <= {2'b00,QPSK_data[2],QPSK_data[3],2'b00,QPSK_data[0],QPSK_data[1]};
QPSK_mod_valid <= 1'b1;
end else begin
QPSK_mod_data <= 0;
QPSK_mod_valid <= 0;
end
//16QAM
always@(posedge clk)
if(mode_type == 1'b1 && s_data_tvalid)
QAM_cnt <= QAM_cnt + 1'b1;
else
QAM_cnt <= 0;
always@(posedge clk)
if(rst_n == 1'b0)
QAM_data <= 0;
else case(QAM_cnt)
3'b000 : QAM_data[0] <= s_data_tdata;
3'b001 : QAM_data[1] <= s_data_tdata;
3'b010 : QAM_data[2] <= s_data_tdata;
3'b011 : QAM_data[3] <= s_data_tdata;
3'b100 : QAM_data[4] <= s_data_tdata;
3'b101 : QAM_data[5] <= s_data_tdata;
3'b110 : QAM_data[6] <= s_data_tdata;
3'b111 : QAM_data[7] <= s_data_tdata;
default : QAM_data <= QAM_data;
endcase
always@(posedge clk)
if(QAM_cnt == 3'b111)
map_enb <= 1'b1;
else
map_enb <= 1'b0;
always@(posedge clk)
if(map_enb)begin
QAM_mod_data <= {QAM_data[0],QAM_data[6],QAM_data[4],QAM_data[2],QAM_data[5],QAM_data[3],QAM_data[1],QAM_data[7]};
QAM_mod_valid <= 1'b1;
end else begin
QAM_mod_data <= 0;
QAM_mod_valid <= 0;
end
always@(posedge clk)
map_wea <= QAM_mod_valid || QPSK_mod_valid;
always@(posedge clk)
if(mode_type == 0)
map_dina <= QPSK_mod_data ;
else
map_dina <= QAM_mod_data;
always@(posedge clk)
if(rst_n == 1'b0)
map_addra <= 0;
else if(mode_type == 0 && map_addra == 12'd4050)
map_addra <= 0;
else if(mode_type == 1 && map_addra == 12'd2025)
map_addra <= 0;
else if(map_wea == 1'b1)
map_addra <= map_addra + 1'b1;
always@(posedge clk)
if(map_addra > 12'd3100 && mode_type == 0)
map_addrb <= map_addrb + 1'b1;
else if(map_addra > 12'd1800 && mode_type == 1)
map_addrb <= map_addrb + 1'b1;
else if(map_addrb < (Ncell-1) && map_addrb > 0)
map_addrb <= map_addrb + 1'b1;
else
map_addrb <= 0;
//====================================================================================
// Output
//====================================================================================
always@(posedge clk)
if(mode_type)
m_data_tdata <= {map_doutb[0],map_doutb[1],map_doutb[2],map_doutb[3]};
else
m_data_tdata <= map_doutb;
always@(posedge clk)
if(~rst_n)
m_data_tvalid <= 0;
else if(map_addrb > 0)
m_data_tvalid <= 1;
else if(m_cnt == (Ncell -1))
m_data_tvalid <= 0;
always@(posedge clk)
if(m_data_tvalid)
m_cnt <= m_cnt + 1'b1;
else
m_cnt <= 0;
always@(posedge clk)
if(m_cnt == (Ncell-2))
m_data_tlast <= 1'b1;
else
m_data_tlast <= 1'b0;
//====================================================================================
// m_data_tready && s_data_tready
//====================================================================================
always@(posedge clk)
s_data_tvalid_reg <= s_data_tvalid;
always@(posedge clk)
if(rst_n == 1'b0)
data_ordy <= 1'b0;
else if(s_config_tvalid == 1'b1)
data_ordy <= 1'b1;
else if(start_data_invld == 1'b1)
data_ordy <= 1'b0;
else if(m_data_tlast == 1'b1)
data_ordy <= 1'b1;
always@(posedge clk)
s_data_tready <= data_ordy && m_data_tready;
map_ram map_ram(
.clka (clk ), // input wire clka
.wea (map_wea ), // input wire [0 : 0] wea
.addra (map_addra ), // input wire [11 : 0] addra
.dina (map_dina ), // input wire [7 : 0] dina
.clkb (clk ), // input wire clkb
.addrb (map_addrb ), // input wire [12 : 0] addrb
.doutb (map_doutb ) // output wire [3 : 0] doutb
);
endmodule
这部分代码也是主要参考了电子发烧友学院中的课程。代码非常简单,大家自己阅读学习即可,这里不再详细说明。
FPGA的映射测试代码
测试代码
`timescale 1ns / 1ps
module tb_tx_Mapper;
// Inputs
reg clk ;
reg rstn ;
reg s_config_tvalid ;
wire s_config_tdata ;
reg s_data_tvalid ;
reg s_data_tdata ;
reg s_data_tlast ;
reg m_data_tready ;
// Outputs
wire s_data_tready ;
wire m_data_tvalid ;
wire [3:0] m_data_tdata ;
wire m_data_tlast ;
reg s_data_tvalid1 ;
reg s_data_tlast1 ;
reg mode_type = 1 ;
assign s_config_tdata = mode_type;
integer i;
initial begin
clk = 0;
rstn = 0;
s_config_tvalid = 0;
s_data_tvalid = 0;
m_data_tready = 1;
repeat(10) @(posedge clk);#1;
rstn = 1;
repeat(5) @(posedge clk);#1;
s_config_tvalid = 1;
repeat(1) @(posedge clk);#1;
s_config_tvalid = 0;
for(i=1;i<=1;i=i+1)
begin
repeat(1)@(posedge clk);#1; //begin
s_data_tvalid = 1;
s_data_tlast = 0;
repeat(16200*1-1)@(posedge clk);#1;
s_data_tvalid = 1;
s_data_tlast =1;
repeat(1)@(posedge clk);#1; //end
s_data_tvalid = 0;
s_data_tlast = 0;
repeat(5000*1)@(posedge clk);#1; //IDLE
end
end
always #5 clk = ~clk;
//====================================================================================
// input
//====================================================================================
always@(posedge clk)
begin
s_data_tvalid1 <=`UD s_data_tvalid;
s_data_tlast1 <= `UD s_data_tlast;
end
integer fid1;
initial
begin
fid1 = $fopen("mapper_in.txt","r");
end
always@(posedge clk)
begin
if(s_data_tvalid)
$fscanf(fid1,"%d",s_data_tdata);
end
//====================================================================================
// output
//====================================================================================
integer fid2;
initial
begin
fid2 = $fopen("ms_mapper_out.txt","w");
end
always@(posedge clk)
begin
if(m_data_tvalid)
$fwrite(fid2,"%dn",m_data_tdata);
end
tx_Mapper uut (
.clk (clk ),
.rst_n (rstn ),
.s_config_tvalid (s_config_tvalid ),
.s_config_tdata (s_config_tdata ),
.s_data_tvalid (s_data_tvalid1 ),
.s_data_tdata (s_data_tdata ),
.s_data_tready (s_data_tready ),
.s_data_tlast (s_data_tlast1 ),
.m_data_tvalid (m_data_tvalid ),
.m_data_tdata (m_data_tdata ),
.m_data_tready (m_data_tready ),
.m_data_tlast (m_data_tlast )
);
endmodule
这部分测试代码的编写没有难度,这里不再介绍。
FPGA与MATLAB的交叉验证
通过阅读MATLAB代码,可以知道我们最后会把映射之后的信息保存到相应的mat文件,然后再Modelsim仿真的时候会将映射之后的结果保存到txt文件,那么我们通过MATLAB代码很容易验证实现两块内容的交叉验证。MATLAB代码如下:
clc;
clear all;
load DataOut.mat
data_lab = DataOut;
map_data_lab = [data_lab];
fid1 = fopen('ms_mapper_out.txt','r');
map_data_sim = fscanf(fid1,'%d');
if(isempty(map_data_sim))
map_data_result = 0;
else
map_data_result = sum(abs(map_data_lab - map_data_sim(1:length(map_data_lab))));
end
a = map_data_result;
运行之后,发现如下结果:
从而验证了我们实验的正确性。
小结
相信大家在学习算法的FPGA实现的时候都掌握了上面的流程,就是先在MATLAB中实现,然后再在FPGA中实现,交互验证实现的正确性。
参考文献
[1]、电子发烧友学院
总结
创作不易,认为文章有帮助的同学们可以关注、点赞、转发支持。为行业贡献及其微小的一部分。或者对文章有什么看法或者需要更近一步交流的同学,可以加入下面的群:
最后
以上就是怡然缘分为你收集整理的基于FPGA的映射调制实现项目简述数学建模MATLAB仿真FPGA代码FPGA与MATLAB的交叉验证小结参考文献总结的全部内容,希望文章能够帮你解决基于FPGA的映射调制实现项目简述数学建模MATLAB仿真FPGA代码FPGA与MATLAB的交叉验证小结参考文献总结所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复