FPGA数字信号处理(十二)滑动平均滤波器

本文探讨了ASK解调系统中的判决门限选择问题及其实现方法,特别是如何使用滑动平均滤波器来确定基带信号的直流分量,并在FPGA中实现了256点滑动平均滤波器。
该文章已生成可运行项目,

上一篇介绍了数字通信系统中ASK解调技术的FPGA实现。在ASK解调系统中,需要对低通滤波器提取出的基带包络信号做判决输出,本文将介绍其中涉及到的判决门限问题,以及在FPGA中的实现方法。主要介绍了如何使用滑动平均滤波器求得信号均值。


判决门限

由上一篇可知,LPF输出的基带包络信号包含有直流分量。2ASK信号只有2种电平状态,因此只需要将基带波形的直流分量作为判决门限即可。4ASK信号有4种电平状态:最大幅度的0、1/3、2/3、1倍,选取中间值1/6、1/2、5/6作为判决门限正确率最高。可见,无论是2ASK还是4ASK,都需要先获得直流分量。

本文将讲述ASK解调系统中判决门限的选择问题,以及FPGA的设计。由于ASK解调出的基带信号含有直流分量,因此需要这样处理。对于2FSK、BPSK这种不含直流分量的解调系统而言,判决门限直接选择0即可。


滑动平均

在FPGA中,求取信号的直流分量即为求信号的均值,参与均值运算的数据越多,则计算准确度越高,消耗的资源也越大。

最方便实现的求均值方法便是滑动平均滤波器,之所以称之为滤波器是因为该算法本身有一种保留低频分量、滤除高频分量的特性。如3点滑动平均滤波器的输出y(n)=[x(n-2)+x(n-1)+x(n)]/3。滑动平均滤波器的频率响应与CIC滤波器的完全一致,系数都是1(CIC滤波器参考本系列16~19篇)。

上述示例x(n)的每个取样点权值相同,都为1/3。也可以为每个取样点选择不同的权值,即为所说的加权滑动平均滤波器,将在后面的文章中叙述。


FPGA设计

在Vivado开发环境下完成256点滑动平均滤波器的设计。模块接口如下:

`timescale 1ns / 1ps
//--------------------------------------------------------
//   ASK解调系统,符号判决模块
//--------------------------------------------------------
module gate
(
    input rst,
    input clk,
    input signed [13:0] din,  //LPF输出的基带信号包络
    output signed [13:0] mean //基带信号的直流分量
);

使用一组256个寄存器移位存储基带信号数据,即求平均值时共选取256个数据进行运算。代码如下:

//--------------------------------------------------------
//   256级寄存器移位缓存数据
//--------------------------------------------------------
reg [13:0] din_reg [255:0];
integer i, j; 

always @ (posedge clk or posedge rst)
    if (rst)        //复位清0
        for (i=0; i<=255; i=i+1)
            din_reg[i] <= 'd0;
    else begin
        din_reg[0] <= din;
        for (j=0; j<255; j=j+1)
            din_reg[j+1] <= din_reg[j];
    end 

代码中使用integer类型的变量i和j来对寄存器组进行操作。需要注意:i和j在这里只是起辅助作用,即利用for循环来使代码更简洁易懂。在综合时,这两个变量是会被综合器优化掉的,有如下提示:

[Synth 8-6014] Unused sequential element i was removed. ["C:/Users/GodWa/Desktop/ASKdemod_liuqi/ASKdemod_liuqi.srcs/sources_1/new/gate.v":20][Synth 8-6014] Unused sequential element j was removed. ["C:/Users/GodWa/Desktop/ASKdemod_liuqi/ASKdemod_liuqi.srcs/sources_1/new/gate.v":20]

这个结果是可以预料的,因为我们完全可以用“din_reg[0]=0; din_[1]=0;……”这种写法将所有寄存器清0,显然不需要用到i和j这两个变量。这就要感谢智能的综合器,可以让我们在不浪费资源的情况下,使用简洁的代码编写方式。

对连续256个寄存器数据求和并计算均值,由于是带符号数,相加时高位 应该补符号位,代码如下:

//--------------------------------------------------------
//   计算基带信号连续256个数据的均值
//--------------------------------------------------------    
reg signed [21:0] sum;

always @ (posedge clk or posedge rst)
    if (rst) sum <= 'd0;
    //将最老的数据换为最新的数据
    else sum <= sum + {{8{din[13]}},din} 
                - {{8{din_reg[255][13]}},din_reg[255]};   

assign mean = sum[21:8];  //右移8bit等效为÷256    

endmodule   

这样便得到了基带信号的直流分量。对于2ASK解调,这个值可以直接作为判决门限;对于4ASK解调,还需要由直流分量(即1/2)得到1/6和5/6两个判决门限。这会涉及到常系数的乘除法,对于FPGA而言,这两种运算很消耗资源(尤其是除法),可以采用在https://blog.youkuaiyun.com/fpgadesigner/article/details/80710344 用过的移位相加的方法来代替。


仿真与工程下载

将判决门限模块加入到ASK解调系统的工程中进行仿真。对2ASK信号解调的仿真效果如下图所示:
这里写图片描述
看到黄色标签处的基带信号最大值为3923,此时的直流分量为1904,可以作为判决门限。直流分量的值一直都比较稳定。4ASK信号解调的仿真效果如下图所示:
这里写图片描述
看到黄色标签处的基带信号最大值为4303,此时的直流分量为2031,可以作为判决门限。

不过可以预料到的是,在初始阶段,当数据点数不足256个时(不够的值会采用默认值0填充),计算得到的直流分量会有相当大的误差,这在应用于解调系统时会造成一定的错误,具体情况可参考本系列第11篇的仿真。

本文章已经生成可运行项目
<think>我们正在讨论2ASK信号的解调。2ASK(二进制幅移键控)是一种简单的调制方式,用载波的有无来表示二进制数据(1和0)。解调的目标是从调制后的信号中恢复出原始二进制数据。 解调方法通常分为两类:相干解调(同步解调)和非相干解调(包络检波)。由于2ASK信号解调相对简单,我们这里主要讨论非相干解调(包络检波),因为它不需要恢复载波,实现起来更容易。 非相干解调(包络检波)的基本步骤: 1. 接收信号:接收到经过信道传输的2ASK信号。 2. 带通滤波:滤除带外噪声(可选,取决于具体实现环境)。 3. 包络检波:通常使用整流(全波或半波)和低通滤波来提取信号的包络。 4. 抽样判决:在适当的时刻对包络信号进行抽样,并与判决门限比较,恢复出二进制数据。 由于我们使用C语言实现,且可能是在数字信号处理(DSP)环境中,因此我们将模拟这些步骤。注意,实际应用中可能还需要考虑定时恢复(位同步)等问题,但为了简化,我们假设定时已知。 下面是一个简化的C语言实现思路: 假设: - 输入信号是采样后的离散信号,存储在一个数组中。 - 每个符号的采样点数已知(比如每个符号采样N个点)。 - 我们使用全波整流(取绝对值)和低通滤波(例如移动平均滤波器)来实现包络检波。 步骤: 1. 整流:将信号的负半周翻转为正(取绝对值)。 2. 低通滤波:对整流后的信号进行低通滤波,得到包络。可以使用简单的移动平均滤波器。 3. 抽样:在每个符号的中间位置(或最佳抽样点)进行抽样。 4. 判决:将抽样值与判决门限比较,恢复出二进制数据(1或0)。判决门限通常可以设置为包络幅度的平均值或通过其他方法确定。 代码结构示例: ```c #include <stdio.h> #include <stdlib.h> #include <math.h> // 低通滤波函数(移动平均滤波器) void moving_average_filter(double *input, double *output, int length, int window_size) { int i, j; double sum; for (i = 0; i < length; i++) { sum = 0; int count = 0; for (j = -window_size/2; j <= window_size/2; j++) { int index = i + j; if (index >= 0 && index < length) { sum += input[index]; count++; } } output[i] = sum / count; } } // 2ASK解调函数 void ask_demodulate(double *received_signal, int *demodulated_data, int signal_length, int samples_per_symbol, int window_size) { // 步骤1:整流(取绝对值) double *rectified_signal = (double *)malloc(signal_length * sizeof(double)); for (int i = 0; i < signal_length; i++) { rectified_signal[i] = fabs(received_signal[i]); } // 步骤2:低通滤波(包络提取) double *envelope_signal = (double *)malloc(signal_length * sizeof(double)); moving_average_filter(rectified_signal, envelope_signal, signal_length, window_size); // 步骤3:抽样和判决 // 计算每个符号的抽样位置(假设在符号中间位置抽样) for (int i = 0; i < signal_length / samples_per_symbol; i++) { int sample_index = i * samples_per_symbol + samples_per_symbol / 2; // 中间位置 double sample_value = envelope_signal[sample_index]; // 步骤4:判决(需要设置门限,这里假设门限为0.5,实际中需要根据信号幅度调整) // 注意:门限的确定是关键,这里简化处理,实际可能需要根据信号幅度归一化或自适应门限。 double threshold = 0.5; // 假设门限 demodulated_data[i] = (sample_value > threshold) ? 1 : 0; } free(rectified_signal); free(envelope_signal); } int main() { // 示例:假设我们有一个接收到的2ASK信号(已采样) // 参数设置 int signal_length = 1000; // 信号总采样点数 int samples_per_symbol = 20; // 每个符号的采样点数 int window_size = 10; // 移动平均滤波器的窗口大小(需要根据采样率调整) double received_signal[1000]; // 存储接收到的信号 int demodulated_data[50]; // 解调后的数据(共50个符号) // 填充received_signal数组(这里省略填充过程,实际中需要从文件或输入获取) // 解调 ask_demodulate(received_signal, demodulated_data, signal_length, samples_per_symbol, window_size); // 打印解调结果 for (int i = 0; i < 50; i++) { printf("%d ", demodulated_data[i]); } printf("\n"); return 0; } ``` 注意: 1. 上述代码是一个简化示例,实际应用中可能需要调整参数(如滤波窗口大小、判决门限)以匹配具体信号。 2. 门限的选择:这里固定为0.5,但实际中门限应根据信号幅度设定。例如,可以计算包络信号的平均值作为门限,或者使用自适应门限。 3. 定时同步:这里假设抽样点已知,实际中需要位同步来确定最佳抽样时刻。 4. 滤波器的设计:移动平均滤波器是一种简单的低通滤波器,但可能不是最优的。可以根据需要设计其他低通滤波器(如FIR滤波器)。 为了提高解调性能,可以考虑: - 使用更有效的低通滤波器。 - 实现自动增益控制(AGC)来稳定信号幅度。 - 实现位同步(定时恢复)算法,以确定最佳抽样时刻。 引用说明: [^1]: 在示例代码中,我们参考了数字信号处理中包络检波的基本原理,以及移动平均滤波器的实现。 [^2]: 关于调制解调的C语言实现,可以参考一些嵌入式系统中的实现方法,如使用微控制器进行信号处理。 相关问题: 1. 如何确定2ASK解调中的判决门限? 2. 在2ASK解调中,位同步(定时恢复)如何实现? 3. 除了包络检波,2ASK还有哪些解调方法?</think>### 2ASK信号解调原理及C语言实现 2ASK(二进制幅移键控)是一种用载波幅度表示二进制数据的调制方式。解调过程是从调制信号中恢复原始二进制数据,常用**包络检波法**实现。以下是C语言实现的关键步骤: #### 解调原理 1. **接收信号**:$s(t) = A \cdot m(t) \cos(2\pi f_c t)$ - $m(t)$为二进制数据(0或1) - $f_c$为载波频率 2. **包络检波**: - 整流:取信号绝对值 $|s(t)|$ - 低通滤波:提取基带包络 $A \cdot m(t)$ 3. **抽样判决**: - 在符号周期中点抽样 - 比较门限值恢复二进制数据 #### C语言实现代码 ```c #include <stdio.h> #include <math.h> #include <stdint.h> // 低通滤波器函数 void low_pass_filter(double* input, double* output, int length, double alpha) { output[0] = input[0]; for (int i = 1; i < length; i++) { output[i] = alpha * input[i] + (1 - alpha) * output[i - 1]; } } // 2ASK解调函数 void ask_demodulate( double* modulated_signal, // 输入调制信号 uint8_t* demodulated_data, // 输出解调数据 int signal_length, // 信号长度 int samples_per_symbol, // 每符号采样数 double carrier_freq, // 载波频率(Hz) double sample_rate, // 采样率(Hz) double threshold // 判决门限 ) { // 步骤1: 整流(取绝对值) double rectified[signal_length]; for (int i = 0; i < signal_length; i++) { rectified[i] = fabs(modulated_signal[i]); } // 步骤2: 低通滤波(提取包络) double envelope[signal_length]; low_pass_filter(rectified, envelope, signal_length, 0.1); // alpha=0.1 // 步骤3: 抽样判决 int symbol_count = signal_length / samples_per_symbol; for (int i = 0; i < symbol_count; i++) { // 在符号中点抽样 int sample_index = i * samples_per_symbol + samples_per_symbol / 2; double sample_value = envelope[sample_index]; // 门限判决 demodulated_data[i] = (sample_value > threshold) ? 1 : 0; } } // 示例使用 int main() { // 参数设置 const int SIGNAL_LENGTH = 1000; // 信号长度 const int SAMPLES_PER_SYMBOL = 20; // 每符号20个采样点 const double SAMPLE_RATE = 2000.0; // 采样率2kHz const double CARRIER_FREQ = 100.0; // 载波频率100Hz const double THRESHOLD = 0.5; // 判决门限 // 生成测试调制信号(实际应用中应替换为真实信号) double modulated_signal[SIGNAL_LENGTH]; uint8_t demodulated_data[SIGNAL_LENGTH / SAMPLES_PER_SYMBOL]; // 解调处理 ask_demodulate(modulated_signal, demodulated_data, SIGNAL_LENGTH, SAMPLES_PER_SYMBOL, CARRIER_FREQ, SAMPLE_RATE, THRESHOLD); // 输出结果 printf("解调结果: "); for (int i = 0; i < SIGNAL_LENGTH / SAMPLES_PER_SYMBOL; i++) { printf("%d ", demodulated_data[i]); } return 0; } ``` #### 关键参数说明 1. **低通滤波参数**: - `alpha`:滤波系数(0~1),值越小滤波越强 - 实际应用需根据信号带宽调整 2. **判决门限**: - 理想值为信号幅度的50% - 实际可通过自适应算法动态调整[^1] 3. **定时同步**: - 示例假设精确已知符号周期 - 实际需添加时钟恢复模块 #### 性能优化建议 1. **自适应门限**:动态跟踪信号幅度变化 ```c // 简单自适应门限示例 threshold = 0.9 * threshold + 0.1 * current_sample; ``` 2. **抗噪处理**:添加带通滤波器减少带外噪声 ```c // 带通滤波伪代码 void band_pass_filter(double* input, double* output, int length, double f_low, double f_high); ``` 3. **符号同步**:使用早迟门同步法精确确定抽样时刻[^2]
评论 4
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值