虽然Simulink中包含了很多已有的Blocks,但并不是时刻符合我们的使用需求,况且有时还不清楚Simulink某些模块的封装,以及内部的运行机制。所以自定义创建simulink模块不失为一个好方法。
MATLAB提供了几种自定义封装Simulink模块的方式。本文简单使用s-function进行封装,仿真16QAM调制解调
仿真前先回顾下QAM的调制解调原理,本文只针对16QAM进行分析和仿真
QAM(正交幅度调制,Quadrature Amplitude Modulation)利用两路独立的数字基带信号,对两个相互正交的同频载波进行抑制载波的双边带调制。QAM本质上是一种 IQ 调制,利用这种已调信号在同一带宽内频谱正交的性质来实现两路并行的数字信号传输。
从数学表达式的角度来看:
式中,An是基带信号的幅度,g(t-nTs)是宽度为Ts的单个基带波形
利用三角函数变换公式可以对上述表达式变换成 正交 的表示形式:
从上式可以看到,QAM调制信号可以“分解”为两路基带数字信号
Σ A g ( t − n T s ) c o s ϕ \Sigma Ag(t-nTs)cos\phi ΣAg(t−nTs)cosϕ 和 Σ A g ( t − n T s ) s i n ϕ \Sigma Ag(t-nTs)sin\phi ΣAg(t−nTs)sinϕ
分别去调制两个 同频 且 相互正交 的载波信号 cos ω \omega ωct 和 sin ω \omega ωct
16QAM基带信息与平面点的对应方式有 方型 和 星型两种(本文仅对方型进行仿真)
方型16QAM星座图如下图所示,平面上的点采用 格雷码 进行映射
由于平面上总共有16个符号,所以需要4个bit表示一个符号
采用一种简易方法:利用MATLAB提供的 s-function 将 .m 文件封装成Simulink模块
sfuntmpl.m参数介绍
- 函数形式:
function [sys,x0,str,ts,simStateCompliance] = sfuntmpl(t,x,u,flag)- 输入参数
t:当前的时间
x:状态向量
u:输入向量
flag:(整数)指示s-function应该执行的任务- 输出参数
sys:通用的返回参数,其值取决于flag的值
x0:初始状态
str:保留
ts:一个1x2的矩阵,第一列指示block的采样时间,第二列指示偏移量- flag的取值对应的s-function程序:
0:mdlInitializeSizes
1:mdlDerivatives
2:mdlUpdate
3:mdlOutputs
4:mdlGetTimeOfNextVarHit
9:mdlTerminate
信源速率:4kbps
载波频率:8kHz
采样频率:80kHz
根据QAM调制解调原理图自建simulink模块,搭建仿真框图
利用Simulink中Random Integer Generator block产生随机二进制序列
设置
set size:2
Sample time:1/4e3
Samples per frame:1(即在每个时刻输出的信号为1x1的帧)
根据16QAM调制流程,需要对信源产生的二进制随机序列进行串/并转换成IQ两路,再分别对这两路进行幅度/相位映射。
由于星座图上每个点代表一个符号,其坐标可以用复数表示。这里Simulink仿真时,将信源产生的 每4个比特 对应 1个符号,分别取出坐标的 实部 和 虚部 即可将数据流分成IQ两路。
Buffer模块参数设置:
Buffer模块降低信息的速率,在每个时刻输出一个[4x1]的帧
sim_REgraymod 和 sim_IMgraymod 为s-function模块,分别取出映射后的实部和虚部。以sim_REgraymod为例。
命令行输入:
edit sfuntmpl
打开s-function模板,将函数名更改为sim_REgraymod,并和.slx文件保存在同一目录下。
function [sys,x0,str,ts,simStateCompliance] = sim_REgraymod(t,x,u,flag)
改动初始化函数 mdlInitializeSizes
sizes.NumContStates = 0; %此仿真不涉及连续和离散状态,都设置为0
sizes.NumDiscStates = 0;
sizes.NumOutputs = 1; %每个时刻输出为1维向量
sizes.NumInputs = 4; %每个时刻输入为4维向量(4x1的帧)
sizes.DirFeedthrough = 1; %直馈
sizes.NumSampleTimes = 1;
%.............
%.............
ts = [1/1e3 0]; %信源速率为4kbps,经过buffer后变成1kbps,初始时间设置为0
改动输出函数 mdlOutputs
function sys=mdlOutputs(t,x,u)
switch [num2str(u(1)) num2str(u(2)) num2str(u(3)) num2str(u(4))]
case '0010'
graytrans=-3+3i;
case '0110'
graytrans=-1+3i;
case '1110'
graytrans=1+3i;
case '1010'
graytrans=3+3i;
case '0011'
graytrans=-3+1i;
case '0111'
graytrans=-1+1i;
case '1111'
graytrans=1+1i;
case '1011'
graytrans=3+1i;
case '0001'
graytrans=-3-1i;
case '0101'
graytrans=-1-1i;
case '1101'
graytrans=1-1i;
case '1001'
graytrans=3-1i;
case '0000'
graytrans=-3-3i;
case '0100'
graytrans=-1-3i;
case '1100'
graytrans=1-3i;
case '1000'
graytrans=3-3i;
end
sys = real(graytrans); %此模块输出实部
基带信号发射前需要经过脉冲成型,可以利用MATLAB自带的 fdatool设计合适的根升余弦滤波器。
设计滤波器参数
Response Type:Raise - Cosine
Design Method:FIR
Window:Kaiser
Beta:0.5
Fs:80000
Fc:8000
Rolloff:0.25
Square root
File —> Export to Simulink Model将设计好的滤波器导出成模块
经过脉冲成型滤波器之前需要对信号进行升采样
利用zero-order hold模块,设置模块的Sample time为采样间隔:1/80kHz
Sine Wave模块可以生成载波
c o s ω t cos\omega t cosωt:
s i n ω t sin\omega t sinωt:
分别与I Q两路信号相乘后相加得到16QAM已调信号
已调信号经过AWGN信号,信噪比设置为15dB
已调信号与载波相乘,经过低通滤波器,再进行抽样判决可恢复出原始信号(此时恢复出的信号为幅度/相位映射后的IQ路信号)
低通滤波器与前面的脉冲成型滤波器相同(既起到低通滤波的作用,同时两个根升余弦滤波器相乘后就是一个升余弦滤波器,通信原理书上都会讲到,在此不赘述)
Gain:gain=2,信号幅度乘2
抽样:还是利用zero-order Hold模块,但是因为抽样的频率和IQ路信息速率一致,所以模块的Sample time设置为1/1e3
sim_SampleDecision 为s-function模块,对抽样后的信号进行判决
代码如下:
function [sys,x0,str,ts,simStateCompliance] = sim_SampleDecision(t,x,u,flag)%更改函数名
function [sys,x0,str,ts,simStateCompliance]=mdlInitializeSizes %编辑初始化函数
%......
sizes.NumOutputs = 1; %输出为1维向量
sizes.NumInputs = 1; %输入为1维向量
sizes.DirFeedthrough = 1;
sizes.NumSampleTimes = 1;
%......
ts = [1/1e3 0]; %采样间隔和前面模块一致
function sys=mdlOutputs(t,x,u) %编辑输出函数
sample_point = u;
sys = round((sample_point+1)/2)*2-1; %判决电平为-2,0,2
% end mdlOutputs
MUX模块可以将两路信号复用到一路,每个时刻输出一个2x1的帧。sim_graydemod 是s-function模块,将星座图上的一个符号逆映射成4位比特序列。
代码如下:
function [sys,x0,str,ts,simStateCompliance] = sim_graydemod(t,x,u,flag) %更改函数名
function [sys,x0,str,ts,simStateCompliance]=mdlInitializeSizes
%.....
sizes.NumOutputs = 4; %输出4位比特
sizes.NumInputs = 2; %输入为二维向量
sizes.DirFeedthrough = 1;
sizes.NumSampleTimes = 1;
%.....
ts = [1/1e3 0];
function sys=mdlOutputs(t,x,u)
switch [num2str(u(1)) num2str(u(2))]
case '11'
sys=[1 1 1 1];
case '13'
sys=[1 1 1 0];
case '33'
sys=[1 0 1 0];
case '31'
sys=[1 0 1 1];
case '-11'
sys=[0 1 1 1];
case '-31'
sys=[0 0 1 1];
case '-13'
sys=[0 1 1 0];
case '-33'
sys=[0 0 1 0];
case '-1-1'
sys=[0 1 0 1];
case '-3-1'
sys=[0 0 0 1];
case '-1-3'
sys=[0 1 0 0];
case '-3-3'
sys=[0 0 0 0];
case '1-1'
sys=[1 1 0 1];
case '3-1'
sys=[1 0 0 1];
case '1-3'
sys=[1 1 0 0];
case '3-3'
sys=[1 0 0 0];
otherwise
sys=[0 0 0 0];
end
Unbuffer 模块
Initial Conditions:0
Convert a frame to scalar samples output at a higher rate.
恢复出信源输出的原始序列
Error rate calculation模块可以计算误码率。通过对比信源产生的原始序列和恢复出的序列时域波形可以看到,这两者之间延迟差了8个码元,计算误码率的时候需要考虑
参数设置如下: