使用Scade One建模N阶FIR滤波器
在数字信号处理领域,有限冲击响应(FIR)滤波器因具有稳定、可实现线性相位等优势,被广泛应用于音频处理、通信系统、图像处理等场景。下面将从FIR滤波器的数学原理出发,结合Scade One的window原语及相关函数,叙述N阶FIR滤波器的建模。
N阶FIR滤波器的原理回顾
在正式进入Scade One建模环节之前,我们先回顾一下N阶FIR滤波器的数学表达式。对于离散信号而言,N阶FIR滤波器的输出信号yn是输入信号xn与滤波器系数B的线性组合,其数学公式最初可表示为:
其中,B[k]
是N阶FIR滤波器的系数数组,x_(n−k)
表示输入信号x在n−k时刻的取值,k的取值范围从0到N,涵盖了当前时刻及过去N个时刻的输入信号。
不过,结合Scade One中window时序窗口原语的特性,我们需要对上述公式进行适当调整。window原语能够便捷地获取输入信号过去N个时刻的值,但无法直接获取当前时刻的输入值xn。因此,我们将公式拆分为两部分,把包含当前时刻输入值的项单独分离出来,得到:
这样的拆分,既符合window原语的功能特点,又为后续在Scade One中利用该原语进行建模提供了基础。
辅助函数融合乘加的定义与作用
在实现N阶FIR滤波器建模时,为了高效完成 “乘法 - 累加” 运算步骤,我们首先定义了一个名为fmac的函数,该函数实现了融合乘加(Fused Multiply-Accumulation, FMA)的功能。其具体代码定义如下:
function fmac (acc: 'T; c: 'T; x: 'T)
returns (o: 'T) where 'T float
{let o = acc + c * x;
}
从函数定义可以看出,fmac函数接收三个参数:累加值acc、系数c和输入值x。函数的运算逻辑是将系数c与输入值x相乘,然后将结果与累加值acc相加,最终返回新的累加结果o。
基于window时序窗口原语的N阶FIR滤波器建模
借助window时序窗口原语和融合乘加辅助函数,我们可以在Scade One中定义N阶FIR滤波器的节点FIR_filter。该节点能够接收输入信号x和滤波器系数数组B,并输出滤波后的信号y。其具体模型实现如下:
node FIR_filter <<N>> (x: 'T; B: 'T^(N+1))
returns (y: 'T) where 'T float
{
let y = (fold fmac_t) <<N>> (B[0]*x, B[1 .. N], window <<N>> (0^N)(x));
}
图形化模型如下
接下来,我们对该节点的各个部分进行解析,理解window原语在其中发挥的作用:
节点参数与泛型设计
节点FIR_filter采用了泛型设计,其中<<N>>
表示滤波器的阶数,是一个可配置的参数,这使得该节点能够灵活地支持不同阶数的FIR滤波器建模,实现了阶数多态。参数x是输入的离散信号,类型为泛型'T
;参数B是FIR滤波器的系数数组,长度为N+1,类型同样为泛型'T
。节点的输出y是经过滤波处理后的离散信号,类型与输入信号x一致。
window 时序窗口原语的应用
在节点内部,window <<N>> (0^N)(x)
部分,发挥了Scade One新特性的优势。其中,<<N>>
指定了窗口的大小为N,意味着该窗口将存储输入信号x过去N个时刻的值;0^N表示窗口的初始值为N个0,这对应了FIR滤波器初始化时,假设输入信号在初始时刻之前的历史值均为0的常见场景;参数x则是窗口所要跟踪的输入信号。
通过window原语,我们无需手动实现复杂的循环缓存逻辑来存储输入信号的历史值。Scade One的代码生成器会将window原语自动编译为高效的循环缓冲区,不仅简化了建模过程,还保证了生成代码的运行效率。例如,当输入信号x不断更新时,window原语会自动将新的输入值加入缓冲区,并移除最旧的历史值,始终维持缓冲区中存储的是最近N个时刻的输入信号值,即x_(n−1),x_(n−2) ,...,x_(n−N)
,契合了之前拆分后的数学公式中对输入信号历史值的需求。
fold 函数与fmac函数的协同工作
(fold fmac) <<N>> (B[0]*x, B[1 .. N], window <<N>> (0^N)(x))
这一部分实现了对求和项的计算,并将其与B[0]*xn
相加,得到最终的输出y。
其中,fold函数是一种高阶函数,它会迭代处理一个序列,并将一个二元函数应用于序列的每个元素,同时维护一个累加值。在本节点中,fold函数的迭代次数由<<N>>
指定,即与window窗口的大小一致。fold函数的初始累加值为B[0]*xn
;迭代处理的第一个序列是滤波器系数数组中从B[1]到B[N]的部分(即B[1…N]),第二个序列是 window 原语获取到的输入信号过去 N 个时刻的值。
在迭代过程中,fold 函数会依次从系数序列B[1…N]中取出一个系数B[k],从window原语的输出序列中取出对应的输入信号历史值x_(n−k),然后调用fmac函数,将当前的累加值、系数B[k]和输入值x_(n−k)传入函数,计算得到新的累加值。当迭代完成后,fold 函数返回的最终累加值就是N阶FIR滤波器的输出y。