引言

       恒模算法(Constant modulus algorithm:CMA)作为一种信道自适应均衡算法在通信与信号处理领域具有很好的应用潜力和前景。相较于RLS和LMS等其他自适应均衡器而言,CMA不需要训练序列,极大的节省了通信带宽资源,可以有效地滤除信道噪声,获取更好的通信质量。本文针对CMA盲均衡算法进行了理论分析,Matlab仿真和FPGA实现。

1、理论分析

          在前面的博文中我们对常见的几种MIMO均衡算法(CMA,LMS,RLS)理论进行了详细介绍,这里不再赘述,见链接:MIMO均衡算法(CMA,LMS,RLS)原理介绍

这里主要介绍一下CMA算法的基本流程:

  • 初始化滤波器长度,权值及遗忘因子,根据具体调制信号类型确定R值
  • 执行卷积,输出滤波结果
  • 计算误差
  • 根据误差动态调整权值

2、Matlab仿真

根据如前所述的算法原理,我们利用Matlab算法仿真工具对该算法进行了仿真,相应代码及注释如下:

% 恒模盲均衡算法(CMA)
function [y,e,w]=CMA(x,M,mu,R)
%% 参数定义
% 输出参数:
% y: 输出信号
% e: 误差输出
% w: 最终滤波器系数% 输入参数:
% x: 输入信号
% M:滤波器长度
% mu: 因子
% R: 常数值
%%
% step1: 算法初始化
% 滤波器系数
w = zeros(1,M);
w((M+1)/2) = 1;
% 输入向量长度
N=length(x);
% 执行CMA
m = 1;
for n = M:2:N % 倒序输入filter_in = x(n:-1:n-M+1);% 计算输出y(m) = w*filter_in;% 误差计算e(m) = y(m)*(abs(y(m))^2 - R);% 滤波器系数更新w = w - mu*e(m)*filter_in';m = m + 1;
end
end
%% 生成单个码元
clear all
close all
clc
Ts = 10e-9; % 码元周期,以秒为单位,倒数为传输速率
N_sample = 8; % 单个码元抽样点数
dt = Ts / N_sample; % 抽样时间间隔
N = 2^15; % 码元数
t = 0 : dt : (N * N_sample - 1) * dt; % 序列传输时间
dt1 = Ts; % 抽样时间间隔
t1 = 0 : dt1 : (N-1) * dt1; % 序列传输时间
gt0 = -ones(1, N_sample);% NRZ
gt1 = ones(1, N_sample);
%% 生成随机序列
RAN = load("tx_OOK_bite.mat"); % 随机0 1序列
RAN = RAN.PRBS;
figure(1);
subplot(5,1,1);
plot(t1,RAN);
axis([0,1000*dt,-1.5,1.5]);
title('原始随机序列');
se1 = [];
for i = 1 : N % 生成序列if RAN(i)==1se1 = [se1 gt1];elsese1 = [se1 gt0];end
end
%% 8倍过采样序列
subplot(5,1,2);
plot(t,se1);
axis([0,1000*dt,-1.5,1.5]);
title('8倍过采样序列');
%% 通过高斯信道
noise = 0.2*randn(1,length(se1));
se1_addnoise = se1 + noise;
subplot(5,1,3);
plot(t,se1_addnoise);
axis([0,1000*dt,-1.5,1.5]);
title('加噪声后序列');
%% 盲均衡
% 数据降为2倍过采样
resample_data = se1_addnoise(1:4:end);
M = 15;
mu = 0.001;
% 对于NRZ信号,R = 1
R = 1;
[y,e,w] = CMA(resample_data',M,mu,R);
subplot(5,1,4);
plot(t1(1:end-7),y);
axis([0,1000*dt,-1.5,1.5]);
title('均衡后序列');
%% 判决
y(y >= 0.5) = 1;
y(y <= -0.5) = 0;
result = [y' RAN(1:N-7)'];
subplot(5,1,5);
plot(t1(1:end-7),y);
axis([0,1000*dt,-1.5,1.5]);
title('判决后序列');
RAN = repmat(RAN,1,4);
BER = BER(y,RAN);

如下所示为CMA算法在调制格式为OOK信号时各处理阶段的对应的波形图,CMA对其他的高阶调制格式也适用,只是不同调制格式对应的R值不一样,R值的具体计算方法我在前面博文的理论介绍过程中也已经给出,如果想把CMA算法应用到高阶调制格式中,可以自行参考相关内容对上述程序进行微调。

3、FPGA实现

一种算法要想在实际应用中产生实际效果,就必须研究其具体的实现方式,FPGA作为一种并行处理器,在通信及信号处理领域应用比较广泛,因此,本文还研究了该算法的FPGA实现。该算法应用流水线的思想进行实现,流水线处理思想增大了数据吞吐量,降低了数据的处理延时,可以使得该算法能应用于高速度的通信场合。

  • 卷积模块(conv.v):该模块主要执行输入数据与滤波器权值数据的卷积操作,实现均衡数据输出
`timescale 1ns/1ps
module conv(// system signalsinput          clk                 , input         rst_n               ,// 输入待滤波数据input       signed [15:0]data_in     ,// 权值数据input       signed [15:0]w0         ,input       signed [15:0]w1         ,input       signed [15:0]w2         ,input       signed [15:0]w3         ,input       signed [15:0]w4         ,input       signed [15:0]w5         ,input       signed [15:0]w6         ,input       signed [15:0]w7         ,input       signed [15:0]w8         ,input       signed [15:0]w9         ,input       signed [15:0]w10        ,input       signed [15:0]w11        ,input       signed [15:0]w12        ,input       signed [15:0]w13        ,input       signed [15:0]w14        ,// 输出数据output      signed[15:0]data_out
);
// 寄存输入数据
reg signed [15:0]x0;
reg signed [15:0]x1;
reg signed [15:0]x2;
reg signed [15:0]x3;
reg signed [15:0]x4;
reg signed [15:0]x5;
reg signed [15:0]x6;
reg signed [15:0]x7;
reg signed [15:0]x8;
reg signed [15:0]x9;
reg signed [15:0]x10;
reg signed [15:0]x11;
reg signed [15:0]x12;
reg signed [15:0]x13;
reg signed [15:0]x14;
// 延时寄存
always @(posedge clk or negedge rst_n) beginif (!rst_n) beginx0 <= 16'd0;x1 <= 16'd0;x2 <= 16'd0;x3 <= 16'd0;x4 <= 16'd0;x5 <= 16'd0;x6 <= 16'd0;x7 <= 16'd0;x8 <= 16'd0;x9 <= 16'd0;x10 <= 16'd0;x11 <= 16'd0;x12 <= 16'd0;x13 <= 16'd0;x14 <= 16'd0;endelse beginx0 <= data_in;x1 <= x0;x2 <= x1;x3 <= x2;x4 <= x3;x5 <= x4;x6 <= x5;x7 <= x6;x8 <= x7;x9 <= x8;x10 <= x9;x11 <= x10;x12 <= x11;x13 <= x12;x14 <= x13;end
end
// 寄存乘积结果
reg signed [31:0] multi_data0;
reg signed [31:0] multi_data1;
reg signed [31:0] multi_data2;
reg signed [31:0] multi_data3;
reg signed [31:0] multi_data4;
reg signed [31:0] multi_data5;
reg signed [31:0] multi_data6;
reg signed [31:0] multi_data7;
reg signed [31:0] multi_data8;
reg signed [31:0] multi_data9;
reg signed [31:0] multi_data10;
reg signed [31:0] multi_data11;
reg signed [31:0] multi_data12;
reg signed [31:0] multi_data13;
reg signed [31:0] multi_data14;
always @(posedge clk or negedge rst_n) beginif (!rst_n) beginmulti_data0 <= 32'd0;multi_data1 <= 32'd0;multi_data2 <= 32'd0;multi_data3 <= 32'd0;multi_data4 <= 32'd0;multi_data5 <= 32'd0;multi_data6 <= 32'd0;multi_data7 <= 32'd0;multi_data8 <= 32'd0;multi_data9 <= 32'd0;multi_data10 <= 32'd0;multi_data11 <= 32'd0;multi_data12 <= 32'd0;multi_data13 <= 32'd0;multi_data14 <= 32'd0;endelse begin// 倒序相乘multi_data0 <= x0*w0;multi_data1 <= x1*w1;multi_data2 <= x2*w2;multi_data3 <= x3*w3;multi_data4 <= x4*w4;multi_data5 <= x5*w5;multi_data6 <= x6*w6;multi_data7 <= x7*w7;multi_data8 <= x8*w8;multi_data9 <= x9*w9;multi_data10 <= x10*w10;multi_data11 <= x11*w11;multi_data12 <= x12*w12;multi_data13 <= x13*w13;multi_data14 <= x14*w14;end
end
//累加
reg signed [32:0] sum;
always @(posedge clk or negedge rst_n) beginif (!rst_n) beginsum <= 33'd0;endelse beginsum <= multi_data0 + multi_data1 + multi_data2 + multi_data3 + multi_data4 + multi_data5 + multi_data6 + multi_data7 + multi_data8 + multi_data9 +multi_data10 + multi_data11 + multi_data12 + multi_data13 + multi_data14;end
end
// Q28 --> Q14
assign data_out = sum>>14;
endmodule
  • 误差计算模块(error.v):该模块主要执行误差计算,该误差用于滤波器权值数据的更新
`timescale 1ns/1ps
module error(input           clk ,input           rst_n,// 滤波完成数据input          signed [15:0]data_in, // 误差output          reg signed [15:0]error// Q14
);
reg signed [31:0] square1;// Q28
reg signed [15:0] square2;// Q14
reg signed [15:0] sub;// Q14
reg signed [15:0] R = 16'd16384;// Q14
reg signed [31:0] mul;// Q28always @ (*) beginsquare1 = data_in*data_in;square2 = square1>>14;sub = square2 - R;mul = data_in*sub;error = mul>>14;
endendmodule
  • 滤波器系数更新模块(w_update.v):该模块主要根据输出误差进行滤波器权值数据的更新,更新后的权值数据输送给卷积模块执行卷积滤波
`timescale 1ns/1ps
module w_update(//system signalsinput    clk                    , input  rst_n                  ,// 误差input    signed [15:0]error     ,// 待滤波数据input    signed [15:0]data_in   ,// 权值数据输出output  reg signed [15:0]w0,output  reg signed [15:0]w1,output  reg signed [15:0]w2,output  reg signed [15:0]w3,output  reg signed [15:0]w4,output  reg signed [15:0]w5,output  reg signed [15:0]w6,output  reg signed [15:0]w7,output  reg signed [15:0]w8,output  reg signed [15:0]w9,output  reg signed [15:0]w10,output  reg signed [15:0]w11,output  reg signed [15:0]w12,output  reg signed [15:0]w13,output  reg signed [15:0]w14
);
// 遗忘因子
reg signed [15:0]mu;
// 寄存输入数据
reg signed [15:0]x0;
reg signed [15:0]x1;
reg signed [15:0]x2;
reg signed [15:0]x3;
reg signed [15:0]x4;
reg signed [15:0]x5;
reg signed [15:0]x6;
reg signed [15:0]x7;
reg signed [15:0]x8;
reg signed [15:0]x9;
reg signed [15:0]x10;
reg signed [15:0]x11;
reg signed [15:0]x12;
reg signed [15:0]x13;
reg signed [15:0]x14;
reg signed [15:0]x15;
reg signed [15:0]x16;
// 延时寄存
always @(posedge clk or negedge rst_n) beginif (!rst_n) beginx0 <= 16'd0;x1 <= 16'd0;x2 <= 16'd0;x3 <= 16'd0;x4 <= 16'd0;x5 <= 16'd0;x6 <= 16'd0;x7 <= 16'd0;x8 <= 16'd0;x9 <= 16'd0;x10 <= 16'd0;x11 <= 16'd0;x12 <= 16'd0;x13 <= 16'd0;x14 <= 16'd0;x15 <= 16'd0;x16 <= 16'd0;mu <= 16'd16;end// conv模块计算消耗2个时钟周期// 为保持误差计算延时对准,这里多延时寄存2个时钟周期else beginx0 <= data_in;x1 <= x0;x2 <= x1;x3 <= x2;x4 <= x3;x5 <= x4;x6 <= x5;x7 <= x6;x8 <= x7;x9 <= x8;x10 <= x9;x11 <= x10;x12 <= x11;x13 <= x12;x14 <= x13;x15 <= x14;x16 <= x15;end
end
// mu*x*e
reg signed [47:0]w0_reg;
reg signed [47:0]w1_reg;
reg signed [47:0]w2_reg;
reg signed [47:0]w3_reg;
reg signed [47:0]w4_reg;
reg signed [47:0]w5_reg;
reg signed [47:0]w6_reg;
reg signed [47:0]w7_reg;
reg signed [47:0]w8_reg;
reg signed [47:0]w9_reg;
reg signed [47:0]w10_reg;
reg signed [47:0]w11_reg;
reg signed [47:0]w12_reg;
reg signed [47:0]w13_reg;
reg signed [47:0]w14_reg;
// 权值更新
always @ (*) beginw0_reg = (mu*x2*error)>>28;w1_reg = (mu*x3*error)>>28;w2_reg = (mu*x4*error)>>28;w3_reg = (mu*x5*error)>>28;w4_reg = (mu*x6*error)>>28;w5_reg = (mu*x7*error)>>28;w6_reg = (mu*x8*error)>>28;w7_reg = (mu*x9*error)>>28;w8_reg = (mu*x10*error)>>28;w9_reg = (mu*x11*error)>>28;w10_reg = (mu*x12*error)>>28;w11_reg = (mu*x13*error)>>28;w12_reg = (mu*x14*error)>>28;w13_reg = (mu*x15*error)>>28;w14_reg = (mu*x16*error)>>28;// 如果时钟频率过高,此处引入的组合逻辑延时可能会导致后面寄存器的建立时间违例w0 = rst_n ? w0 - w0_reg:16'd0;w1 = rst_n ? w1 - w1_reg:16'd0;w2 = rst_n ? w2 - w2_reg:16'd0;w3 = rst_n ? w3 - w3_reg:16'd0;w4 = rst_n ? w4 - w4_reg:16'd0;w5 = rst_n ? w5 - w5_reg:16'd0;w6 = rst_n ? w6 - w6_reg:16'd0;w7 = rst_n ? w7 - w7_reg:16'd16384;w8 = rst_n ? w8 - w8_reg:16'd0;w9 = rst_n ? w9 - w9_reg:16'd0;w10 = rst_n ? w10 - w10_reg:16'd0;w11 = rst_n ? w11 - w11_reg:16'd0;w12 = rst_n ? w12 - w12_reg:16'd0;w13 = rst_n ? w13 - w13_reg:16'd0;w14 = rst_n ? w14 - w14_reg:16'd0;
end
endmodule
  • 顶层模块(Top.v):主要进行对如上所述三个模块的例化,进行模块的封装
`timescale 1ns/1ps
module Top(input          clk                    , input          rst_n                  ,// 输入待滤波数据input       signed [15:0]data_in     ,// 误差output      signed [15:0]error       ,// 输出数据output      signed[15:0]data_out
);
// 权值
wire signed [15:0]w0;
wire signed [15:0]w1;
wire signed [15:0]w2;
wire signed [15:0]w3;
wire signed [15:0]w4;
wire signed [15:0]w5;
wire signed [15:0]w6;
wire signed [15:0]w7;
wire signed [15:0]w8;
wire signed [15:0]w9;
wire signed [15:0]w10;
wire signed [15:0]w11;
wire signed [15:0]w12;
wire signed [15:0]w13;
wire signed [15:0]w14;
//例化卷积滤波模块
conv conv_demo(//system signals.clk(clk)         , .rst_n(rst_n)     ,// 输入待滤波数据.data_in(data_in),// 权值数据.w0(w0)         ,.w1(w1)         ,.w2(w2)         ,.w3(w3)         ,.w4(w4)         ,.w5(w5)         ,.w6(w6)         ,.w7(w7)         ,.w8(w8)         ,.w9(w9)         ,.w10(w10)       ,.w11(w11)       ,.w12(w12)       ,.w13(w13)       ,.w14(w14)       ,// 输出数据.data_out(data_out)
);
// 例化误差计算模块
error error_demo(.clk(clk),.rst_n(rst_n),// 滤波完成数据.data_in(data_out), // 误差.error(error)
);
// 例化权值更新模块
w_update w_update_demo(.clk(clk)     , .rst_n(rst_n) ,// 误差.error(error) ,// 待滤波数据.data_in(data_in),// 权值数据输出.w0(w0)         ,.w1(w1)         ,.w2(w2)         ,.w3(w3)         ,.w4(w4)         ,.w5(w5)         ,.w6(w6)         ,.w7(w7)         ,.w8(w8)         ,.w9(w9)         ,.w10(w10)       ,.w11(w11)       ,.w12(w12)       ,.w13(w13)       ,.w14(w14)
);
endmodule
  • Testbench(tb.v):在Matlab中取得定点化后的数据读入Modelsim中进行测试
`timescale 1ns/1ps
module tb ();
reg clk;
reg rst_n;
// 输入待滤波数据
reg signed [15:0] data_in;
// 误差输出
wire signed [15:0] error;
// 滤波数据输出
wire signed [15:0] data_out;
// 寄存测试数据
reg  signed [15:0] data_in_hex [0:65535];integer fpr_out;
integer  i;
integer  j;initial
begin$display("step1:Load  Data");// 读入加噪声输入数据$readmemh("F:/FPGA_DSP/CMA/data_in_hex.txt",data_in_hex);// 写结果fpr_out = $fopen("F:/FPGA_DSP/CMA/data_out_dec.txt","w");// 时钟、复位初始化      clk = 1'b0;rst_n = 1'b1;#5 rst_n = 1'b0;#5 rst_n = 1'b1;// 输入待滤波数据$display("step2:Write Data to CMA_Filter");for(i = 0; i < 65536 - 15; i = i + 2)beginfor(j = 0; j < 15; j = j + 1)begindata_in = data_in_hex[i + j];#10;end$fwrite(fpr_out,"%d\n",$signed(data_out));end
end
always #5 clk = ~clk;
// 例化顶层模块
Top Top_demo(.clk(clk)    , .rst_n(rst_n) ,// 输入待滤波数据.data_in(data_in)  ,// 误差.error(error)      ,// 输出数据.data_out(data_out)
);
endmodule
  • Modelsim仿真结果:利用如上所述几个模块,我们对CMA算法进行了一个简单的仿真,结果波形图如下

另外,我们还从Modelsim中取出了仿真数据进行误码率计算,误码率结果符合预期。

4、总结

CMA作为一种盲均衡算法,不需要训练序列,具有一定的优势,但因其收敛速度较慢和精度不高,因此也限制了在一些高速通信场合的应用。另外,FPGA实现的算法是一种定点算法,难免存在计算误差,造成计算精度的丧失,如想进一步提高计算精度,可以选择更高的定点量化位数。

CMA盲均衡器---从理论仿真到FPGA实现相关推荐

  1. LMS自适应均衡器---从理论仿真到FPGA实现

    引言 最小均方误差算法(LMS)作为一种自适应均衡和滤波算法,在通信与信号处理中具有广泛的应用,可以有效地滤除信道噪声,获取很好的通信质量.本文针对该算法进行了理论分析,Matlab仿真与FPGA实现 ...

  2. 4qam、16qam、64qam、256qam理论仿真曲线

    本博文给出了4qam.16qam.64qam.256qam理论仿真曲线,画出了EbN0 vs BER的曲线图,可以作为大家学习的一个参考. 仿真结果: %%%%%%%%%%%%%%%%%%%%%%%% ...

  3. 基于ADS的c语言程序设计实验,实验一:基于ADS软件传输线理论仿真设计与分析.docx...

    龙 lerrnl'' 龙 lerrnl'' $ Num=l 1Z=50 Ohm h|」M -- TL1 Z=50 0.0hm E=50 F=5GHz ggj I SrPARAM ETERS . | Z ...

  4. ModelSim仿真Intel FPGA的DDR3问题

    Quartus Prime Pro Edition 18.0.0 Modelsim 10.6c(官方推荐版本,但我实际使用的是QuestaSim2020.1) 使用modelsim仿真Intel FP ...

  5. 基于stm32单片机智能导盲拐杖源程序Proteus仿真设计

    功能介绍: 采用stm32单片机作为主控CPU,采用srf05超声波模块测量障碍物距离,LCD1602显示屏显示当前的障碍物距离,通过传感器测量障碍物的距离的远近来进行相关报警,距离越近,蜂鸣器报警节 ...

  6. FM调制解调Matlab仿真及FPGA设计

    宽带.窄带FM调制信号解调Matlab仿真及FPGA设计

  7. 最小均方误差均衡器的matlab仿真设计,最小均方误差均衡器的Matlab仿真设计

    最小均方误差均衡器的 Matlab 仿真设计 张丹丹 , 张 禾 , 刘慧芳 , 杨 婧 (西南石油大学 电子信息工程学院 , 四川 成都 610500) 摘 要 :随着集成电路与计算机技术的发展 , ...

  8. 【迫零准则】基于迫零准则的自适应线性均衡器的MATLAB仿真

    0.完整源码获得方式 方式1:微信或者QQ联系博主 方式2:订阅MATLAB/FPGA教程,免费获得教程案例以及任意2份完整源码 1.软件版本 MATLAB2013b 2.本算法理论知识 在数字通信系 ...

  9. 【盲信道估计】基于matlab的LMS盲信道估计QPSK仿真

    目录 1.软件版本 2.核心代码 3.操作步骤与仿真结论 4.参考文献 5.完整源码获得方式 1.软件版本 MATLAB2021a 2.核心代码 % CHANNEL EQUALIZATION USIN ...

最新文章

  1. 在 Linux 中创建和管理归档文件教程在 Linux 中创建和管理归档文件教程
  2. iOS代码编程规范 根据项目经验汇总
  3. Redis笔记(六):Java中使用Redis
  4. SAP S/4HANA key user tool extensibility原理
  5. 通过 Azure Pipelines 实现持续集成之docker容器化
  6. 前端DEMO:网络上流行的抖音罗盘
  7. 判断回文递归算法实现
  8. 数字式轮胎气压计行业调研报告 - 市场现状分析与发展前景预测
  9. SSM整合时IDE: File is included in 4 contexts
  10. 【MATLAB信号处理】连续时间信号与系统的频域分析
  11. [流媒体服务器搭建] EasyDarwin服务器搭建及客户端推流完整示例
  12. 福昕PDF编辑器中文版(FoxitPDFEditor)绿色版
  13. 上海立信会计师事务所专场 — 纯前端表格技术应用研讨会
  14. TIM腾讯聊天(即时通信 IM)(咨询客服业务)
  15. 低代码和无代码,完全是两回事
  16. conda多环境切换与安装
  17. 百度地图搜索框在弹出层中无法显示问题
  18. 什么是GIS,GIS的基本功能是什么?
  19. 让王老吉崛起的三大营销战役
  20. qq服务器维护到什么时候,2021qq扩列维护到什么时候?qq扩列升级什么时候结束?...

热门文章

  1. baidu是男的还是女的? 【ZZ】
  2. 锤子坚果手机(YQ601)换屏过程分享
  3. SharePoint 中关于event receivers的讨论
  4. ssh_crm:用户注册、登陆校验拦截器、员工拜访客户功能
  5. sony电子纸触屏失灵解决办法
  6. 请假时长计算和每月工作天数计算
  7. 挑战安卓和iOS!刚刚,华为官宣鸿蒙手机版,P40搭载演示曝光!高管现场表态:我们准备好了...
  8. 中台之上(二):为什么业务架构存在 20 多年,技术人员还觉得它有点虚?
  9. java 雪花算法生成ID
  10. solaris oracle 磁盘阵列,Solaris 下的 oracle 的基本操作。