主要内容

实现数字滤波器算法浮点和定点类型使用和0

这个例子向您展示了如何将一个有限脉冲响应(杉木)滤波器定点通过分离算法的定点类型规范代码。

分离数据类型规范从算法代码允许您:

  • 重用算法代码使用不同的数据类型

  • 保持你的算法与数据类型规范整洁和switch语句为不同的数据类型

  • 保持你的算法代码更加可读

  • 定点和浮点比较基线之间切换

  • 定点设置之间切换变化不改变算法的代码

原始算法

这个例子将MATLAB®代码有限脉冲响应(杉木)滤波器定点。

n的公式输出y (n)(杉木)滤波器给定滤波器的系数b和输入x是:

y (n) = b (1) * (2) x (n) + b * x (n - 1) +…+ b(结束)* x (n-length (b) + 1)

线性缓冲区实现

有几种不同的方式写一冷杉过滤器。一种方法是用一个线性缓冲区的功能后,b是一个行向量和z是一个列向量b一样的长度。

函数[y, z] = fir_filt_linear_buff (b, x, z) y = 0(大小(x));n = 1:长度(x) z = [x (n);z (1: end-1)];y (n) = b * z;结束结束

向量z是由x的当前和以前的样品:

z = [x (n);x (n - 1);. .;x (n-length (b) + 1))

向量z称为状态向量。这是作为一个输入和一个输出。一个输入时,滤波器的初始状态。当它是一个输出,它的最终状态是过滤器。定义外的状态向量函数允许您不断通过过滤器流数据在实时系统中,和重用同一项目内的滤波算法。状态向量通常名为z,因为它是延误的同义词美元z ^ {1} $与z变换有关。Lotfi德的z变换是为纪念他的开创性工作在这一领域[1]。

线性缓冲区实现利用MATLAB的方便矩阵语法和很容易阅读和理解。然而,它引入了一个完整的副本状态为每个样本的输入缓冲区。

环形缓冲区的实现

更有效地实现冷杉过滤器,您可以存储在一个循环的缓冲区中,z,其元素是z (p) = x (n), p =国防部(n - 1、长度(b)) + 1, n = 1, 2, 3, ....

例如,让长度(b) = 3,并初始化p和z:

p = 0, z = [0 0 0]

从第一个样品,填补国家z缓冲以循环的方式。

n = 1, p = 1, z (1) (1) = x, z = [x (1) 0 0] y (1) = b (1) * b z (1) + (2) * z z (3) + b (3) * (2)
n = 2, p = 2, z (2) = x (2), z = [x (1) (2) 0] y (2) z = b (1) * (2) + b z (1) (2) * + b (3) * z (3)
n = 3, p = 3, z (3) = x (3), z = x (x (1) (2) (3)] y (3) = b (1) * z (3) + b (2) * b z (2) + (3) * z (1)
n = 4, p = 1, z (4) (1) = x, z = [x (4) x (2) (3)] y (4) = b (1) * b z (1) + (2) * z z (3) + b (3) * (2)
n = 5, p = 2, z (2) = x (5), z = [x (4) (5) x (3)] y (5) z = b (1) * (2) + b z (1) (2) * + b (3) * z (3)
n = 6, p = 3, z (3) = x (6), z = x (x (4) (5) (6)] y (6) = b (1) * z (3) + b (2) * b z (2) + (3) * z (1)

你可以使用一个循环缓冲区实现冷杉过滤器如下MATLAB函数。

函数(y, z, p) = fir_filt_circ_buff_original (b, x, z, p) y = 0(大小(x));nx =长度(x);nb =长度(b);n = 1: nx p = p + 1;如果p > nb, p = 1;结束z (p) = x (n);acc = 0;k = p;j = 1: nb acc = acc + b (j) * z (k);k = k - 1;如果k < 1, k =注;结束结束y (n) = acc;结束结束

测试文件

创建一个测试文件验证浮点算法是将它转换为定点之前预期。您可以使用相同的测试文件提出定点数据类型和比较定点后的浮点基准转换的结果。

测试向量应该代表现实的输入运动的完整系统所期望的值的范围。现实的输入是冲动,大笔的正弦曲线,线性调频信号,你可以使用线性验证输出是正确的理论。信号产生最大输出可用于验证您的系统不溢出。

设置

运行以下代码来捕获和重置当前状态的全球定点数学和定点偏好设置。

resetglobalfimath;FIPREF_STATE = (fipref);resetfipref;

运行下面的测试函数代码复制到一个临时文件夹这个例子不干扰你的工作。

tempdirObj = fidemo.fiTempdir (“fir_filt_circ_buff_fixed_point_conversion_example”);
拷贝文件(fullfile (matlabroot,“工具箱”,“定点”,“fidemos”,' + fidemo ',“fir_filt_ * m”),“。”,“f”);

滤波器系数

使用以下低通滤波器系数,计算从信号处理工具箱使用fir1函数。

0.25 b = fir1(11日);
b = (-0.004465461051254 - -0.004324228005260 + 0.012676739550326 + 0.074351188907780 + 0.172173206073645 + 0.249588554524763 + 0.249588554524763 + 0.172173206073645 + 0.074351188907780 + 0.012676739550326 -0.004324228005260 -0.004465461051254) ';

时间向量

利用这段时间向量来创建测试信号。

nx = 256;t = linspace(0, 10 *π,nx) ';

脉冲输入

冷杉的响应一个脉冲输入滤波器是滤波器系数。

x_impulse = 0 (nx, 1);x_impulse (1) = 1;

产生的最大输出的信号

一个过滤器的最大输出时输入的符号与滤波器的脉冲响应的迹象。

x_max_output =符号(fliplr (b));x_max_output = repmat (x_max_output装天花板(nx /长度(b)), 1);x_max_output = x_max_output (1: nx);

的最大输出级的1-norm脉冲响应,这是常态(b - 1) = (abs (b))。

maximum_output_magnitude =规范(b, 1)% #好< * NOPTS >
maximum_output_magnitude = 1.0352

的正弦

正弦之和是一个典型的输入过滤器,您可以很容易地看到高频率过滤掉的阴谋。

f0 = 0.1;f1 = 2;x_sines =罪(2 *π* t * f0) + 0.1 *罪(2 *π* t * f1);

尖声地说

唧唧声给一个形象化的低通滤波器的作用通过低频和高频衰减。

f_chirp = 1/16;%的目标频率x_chirp =罪(π* f_chirp * t ^ 2);%线性啁啾标题= {“冲动”,的最大输出,正弦的总和,“唧唧喳喳”};x = [x_impulse x_max_output、x_sines x_chirp);

最初的函数调用

在开始转换为定点之前,打电话给你的原始功能的测试文件输入建立一个基线比较随后的输出。

y0 = 0(大小(x));i = 1:尺寸(x, 2)%初始化状态每一列的输入p = 0;z = 0(大小(b));y0 (:, i) = fir_filt_circ_buff_original (b, x (:, i), z, p);结束

基线输出

fir_filt_circ_buff_plot(1、标题,t, x, y0)

准备工具和代码生成

在MATLAB算法后的第一步是准备工具,这就需要代码生成。在转换之前,您可以使用编码器。筛选器函数来分析你的代码并确定不支持的功能和语言功能。金宝app

入口点函数

做仪表和代码生成时,方便有一个入口点函数,调用函数转换为定点。你可以把数字滤波器的输入不同的数据类型,并添加调用不同的过滤器进行比较。通过使用一个入口点函数可以运行两个定点和浮点变量的过滤器,以及不动点的不同变体。这允许您更快速的遍历代码到达最优定点设计。

函数y = fir_filt_circ_buff_original_entry_point (b, x,重置)如果输入参数个数< 3,重置= true;结束%定义循环缓冲z缓冲位置索引p。%那么他们就被宣布为持久的过滤器可以被称为一个流%循环,每部分捡,最后一节。持续的z p如果isempty (z) | |重置p = 0;z = 0(大小(b));结束(y, z, p) = fir_filt_circ_buff_original (b, x, z, p);结束

测试文件

你的测试文件调用编译后的入口点函数。

函数y = fir_filt_circ_buff_test (b, x)
y = 0(大小(x));
我= 1:尺寸(x, 2)重置= true;y (:, i) = fir_filt_circ_buff_original_entry_point_mex (b, x (:, i),重置);结束
结束

建立原始的函数

编译与buildInstrumentedMex原始入口点函数。这对测井仪器代码你可以收集最大和最小值模拟,提出数据类型。

重置= true;buildInstrumentedMexfir_filt_circ_buff_original_entry_pointarg游戏{b, x(: 1),重置}

运行原始的函数

通过算法来运行您的测试文件输入日志最小和最大值。

日元= fir_filt_circ_buff_test (b (x);

显示类型

使用showInstrumentationResults查看你所有的变量的数据类型和的最小值和最大值在测试文件运行记录。看看最大值记录为输出变量y和累加器变量acc和注意,他们获得的理论最大输出值计算。

showInstrumentationResultsfir_filt_circ_buff_original_entry_point_mex

看到这些结果的代码生成报告:

  • 选择函数fir_filt_circ_buff_original

  • 选择变量选项卡

验证原始的函数

每次你修改你的函数,验证结果仍然匹配您的基线。

fir_filt_circ_buff_plot2(2、标题,t, x, y0, y1)

使用类型转换函数表

从算法,不同的数据类型:

  1. 创建一个表的数据类型定义。

  2. 修改算法代码来使用数据类型的表。

这个例子显示了迭代步骤通过创建不同的文件。在实践中,可以使迭代修改同一个文件。

原始类型表

创建一个类型表使用的结构与原型变量设置为原始类型。使用基线类型验证你最初的正确转换,也用它来编程浮点和定点类型之间的切换函数。索引变量j, k, n, nb, nx由MATLAB编码器™自动转换为整数,因此您不需要指定类型的表。

原型值指定为空([]),因为使用的数据类型,而不是价值。

函数T = fir_filt_circ_buff_original_types () T.acc =双([]);结核病=双([]);T.p =双([]);T.x =双([]);T.y =双([]);T.z =双([]);结束

Type-aware滤波函数

准备过滤功能和入口点函数被使用type-aware演员和零函数和类型表。

使用下标赋值acc (:) =…,p(:)=1, and k(:)=nb to preserve data types during assignment. See the "Cast fi Objects" section in the Fixed-Point Designer documentation for more details about subscripted assignment and preserving data types.

函数调用y = 0(大小(x),“喜欢”,T.y)创建0 x一样大小的数组的属性变量T.y。最初,T。y是一个双fir_filt_circ_buff_original_types中定义的函数,但它是定义成一种定点后在这个例子。

函数调用acc =投(0,“喜欢”,T.acc)投0值和变量T.acc相同的属性。最初,T。acc是fir_filt_circ_buff_original_types双中定义的函数,但它是定义成一种定点后在这个例子。

函数(y, z, p) = fir_filt_circ_buff_typed (b, x, z, p, T) y = 0(大小(x),“喜欢”,T.y);nx =长度(x);nb =长度(b);n = 1: nx p (:) = p + 1;如果p > nb, p (,) = 1;结束z (p) = x (n);acc =投(0,“喜欢”,T.acc);k = p;j = 1: nb acc (:) = acc + b (j) * z (k);k (:) = k - 1;如果k < 1, k(:) =注;结束结束y (n) = acc;结束结束

Type-aware入口点函数

函数调用p1 =投(0,“喜欢”,T1.p)投0值和变量T1.p相同的属性。最初,T1。p是一个双重fir_filt_circ_buff_original_types中定义的函数,但后来它被界定为一个整数类型。

函数调用z1 = 0(大小(b)、“喜欢”、T1.z)创建了一个数组的0 b一样的大小的属性变量T1.z。最初,T1。z是一个双重fir_filt_circ_buff_original_types中定义的函数,但它是定义成一种定点后在这个例子。

函数日元= fir_filt_circ_buff_typed_entry_point (b, x,重置)如果输入参数个数< 3,重置= true;结束%%基线类型%T1 = fir_filt_circ_buff_original_types ();%每个调用过滤器需要保持自己的状态。持续的z1 p1如果isempty (z1) | |重置p1 =投(0,“喜欢”,T1.p);z1 = 0(大小(b),“喜欢”,T1.z);结束b1 =投(b,“喜欢”,T1.b);x1 =投(x,“喜欢”,T1.x);(z1日元,p1) = fir_filt_circ_buff_typed (b1, x1, z1, p1, T1);结束

验证修改的功能

每次你修改你的函数,验证结果仍然匹配您的基线。因为你使用了原始类型的类型表,输出应该是相同的。这验证了转换正确分离算法的类型。

buildInstrumentedMexfir_filt_circ_buff_typed_entry_pointarg游戏{b, x(: 1),重置}日元= fir_filt_circ_buff_typed_test (b (x);fir_filt_circ_buff_plot2(3、标题,t, x, y0, y1)

提出从模拟最小/最大日志数据类型

使用showInstrumentationResults函数提出定点部分长度,给定一个默认签名定点类型和16位字长。

showInstrumentationResultsfir_filt_circ_buff_original_entry_point_mex-defaultDTnumerictype (16)-proposeFL

在插装代码生成报告,选择函数fir_filt_circ_buff_original和变量选项卡,查看这些结果。

创建一个定点类型表

使用该类型的代码生成报告,指导您在选择定点类型和创建一个定点类型表使用一个与原型结构变量。

用你的知识算法改进的建议。例如,您正在使用acc变量作为一个蓄电池,所以32位。从代码生成的报告中,您可以看到,acc至少需要2整数位,防止溢出,所以设定部分长度为30。

变量p作为索引,所以你可以使它成为一个装入的16位整数。

原型值指定为空([]),因为使用的数据类型,而不是价值。

函数T = fir_filt_circ_buff_fixed_point_types () T.acc = fi([],真的,32岁,30);结核病= fi([],真的,16、17);T.p = int16 ([]);T.x = fi(14)[],真的,16日;T.y = fi(14)[],真的,16日;T.z = fi(14)[],真的,16日;结束

不动点添加到入口点函数

添加一个调用定点类型表的入口点函数:

T2 = fir_filt_circ_buff_fixed_point_types ();持续的z2 p2如果isempty (z2) | |重置p2 =投(0,“喜欢”,T2.p);z2 = 0(大小(b),“喜欢”,T2.z);结束b2 =投(b,“喜欢”,T2.b);x2 =投(x,“喜欢”,T2.x);(y2, z2, p2) = fir_filt_circ_buff_typed (b2, x2, z2, p2, T2);

构建和运行算法与定点数据类型

buildInstrumentedMexfir_filt_circ_buff_typed_entry_pointarg游戏{b, x(: 1),重置}(y1, y2) = fir_filt_circ_buff_typed_test (b (x);showInstrumentationResultsfir_filt_circ_buff_typed_entry_point_mex

看到这些结果的代码生成报告:

  • 选择的入口点函数,fir_filt_circ_buff_typed_entry_point

  • 选择fir_filt_circ_buff_typed以下代码:

(y2, z2, p2) = fir_filt_circ_buff_typed (b2, x2, z2, p2, T2);
  • 选择变量选项卡

16位字长,精度数学

验证的结果在一个可接受的公差基准。

fir_filt_circ_buff_plot2(4、标题,t, x, y1, y2);

你的算法已经被转换为定点MATLAB代码。如果你也想转换为c代码,然后继续下一节。

生成c代码

本节描述如何生成有效的c代码的定点MATLAB代码与前一节。

所需的产品下载188bet金宝搏

你需要MATLAB编码器™生成c代码,你需要嵌入式编码器®硬件实现的设置在本例中使用。

算法针对最有效的c代码

输出变量y被初始化为0,然后完全覆盖之前使用它。因此,y填满所有零是不必要的。您可以使用编码器。nullcopy函数声明一个变量实际上没有填充值,这使得在这种情况下更高效的代码。然而,你必须非常小心当使用编码器。nullcopy因为如果你访问一个元素分配一个变量之前,那么你正在访问未初始化的内存和其内容是不可预测的。

何时使用编码器的经验法则。nullcopy是初始化时需要大量的时间相对于其他算法。如果你不确定,那么最安全的做法是不使用它。

函数(y, z, p) = fir_filt_circ_buff_typed_codegen (b, x, z, p, T)%使用编码器。nullcopy只有当你确信所有的价值%变量是覆盖之前使用它。y = coder.nullcopy(0(大小(x),“喜欢”T.y));nx =长度(x);nb =长度(b);n = 1: nx p (:) = p + 1;如果p > nb, p (,) = 1;结束z (p) = x (n);acc =投(0,“喜欢”,T.acc);k = p;j = 1: nb acc (:) = acc + b (j) * z (k);k (:) = k - 1;如果k < 1, k(:) =注;结束结束y (n) = acc;结束结束

本地的c代码类型

你可以设置定点数学属性匹配本机操作c产生最有效的c代码,但这个例子表明,它可以创建问题溢出和产生不准确的结果修正在下一节。它并不总是产生问题,首先它是值得一试,看看你可以得到最干净的c代码。

设置定点数学属性使用地板上舍入和溢出,因为这些都是默认的操作封装在C。

设置产品和资金的定点数学属性匹配本地C 32位整数类型,和保持最低有效位(ls下载188bet金宝搏b)的数学操作。

将这些设置添加到一个定点类型表。

函数T = fir_filt_circ_buff_dsp_types () F = fimath (“RoundingMethod”,“地板”,“OverflowAction”,“包装”,“ProductMode”,“KeepLSB”,“ProductWordLength”32岁的“SumMode”,“KeepLSB”,“SumWordLength”、32);T.acc = fi([],真的,32岁,30岁,F);T.p = int16 ([]);肺结核= fi([],真的,16、17 F);T.x = fi([],真的,16日,14日,F);T.y = fi([],真的,16日,14日,F);T.z = fi([],真的,16日,14日,F);结束

测试本地c代码类型

添加一个调用类型表的入口点函数和运行测试文件。

(y1、y2、y3) = fir_filt_circ_buff_typed_test (b (x);% #好< * ASGLU >

在情节的第二行中,您可以看到,最大输出误差的两倍大小的输入,显示的值应该是正溢出来负。你也可以看到其他输出不溢出。这就是为什么它是重要的您的测试文件运动的全部范围值除了其他典型的输入。

fir_filt_circ_buff_plot2(5、标题,t, x, y₁, y3);

按比例缩小的双类型找到溢出

按比例缩小的双变量存储在双精度浮点数据,所以他们在全面执行算术。他们还保留其定点设置,所以他们能够报告时计算出的定点类型的范围。

改变数据类型扩展双,并添加这些设置scaled-double类型表。

函数T = fir_filt_circ_buff_scaled_double_types () F = fimath (“RoundingMethod”,“地板”,“OverflowAction”,“包装”,“ProductMode”,“KeepLSB”,“ProductWordLength”32岁的“SumMode”,“KeepLSB”,“SumWordLength”、32);DT =“ScaledDouble”;T.acc = fi([],真的,32岁,30岁,F,“数据类型”,DT);T.p = int16 ([]);结核病= fi([],真的,16、17 F,“数据类型”,DT);T.x = fi (F[],真的,16日,14日,“数据类型”,DT);T.y = fi (F[],真的,16日,14日,“数据类型”,DT);T.z = fi (F[],真的,16日,14日,“数据类型”,DT);结束

添加一个调用scaled-double类型表的入口点函数和运行测试文件。

(y1、y2、y3、y4) = fir_filt_circ_buff_typed_test (b (x);% #好< * NASGU >

显示测量结果与scaled-double类型。

showInstrumentationResultsfir_filt_circ_buff_typed_entry_point_mex

看到这些结果的代码生成报告:

  • 选择的入口点函数,fir_filt_circ_buff_typed_entry_point

  • 选择fir_filt_circ_buff_typed_codegen以下代码:

(y4 z4, p4) = fir_filt_circ_buff_typed_codegen (b4, x4, z4, p4、T4);
  • 选择变量选项卡。

  • 查看表中的变量。所有的变量溢出,这表明溢出发生在一个操作的结果。

  • 悬停在运营商在报告中(+、-、*、=)。

  • 悬停在“+”这条线的MATLAB代码插装代码生成报告:

acc (:) = acc + b (j) * z (k);

报告显示,和溢出:

和溢出的原因是不能产品b (j) * z (k)产生一个numerictype(真的,32岁,31)因为b numerictype(真的,16、17)和z numerictype(真的,16日14)。和类型设置为“保持最低有效位”(KeepLSB),所以总和numerictype(真的,32岁,31)。然而,2整数比特是必要的存储的最小和最大模拟值-1.0045 + 1.035,分别。

调整,以避免溢出

组的分数长度16而不是17,b (j) * z (k) numerictype(真的,32岁,30),所以也是numerictype之和(真的,32岁,30)后KeepLSB规则。

保留所有其他设置,并设置

肺结核= fi([],真的,16日,16日,F);

然后之和的MATLAB代码不再溢出:

acc (:) = acc + b (j) * z (k);

运行测试文件使用新的设置和阴谋的结果。

(y1、y2、y3、y4日元)= fir_filt_circ_buff_typed_test (b (x);

您可以看到溢出得以避免。然而,故事情节表现出偏见和一个更大的错误由于使用C的自然地板上舍入。如果这种偏见是可以接受的,那你就老老实实呆在这儿,生成的c代码很干净。

fir_filt_circ_buff_plot2(6、标题,t, x, y₁,日元);

消除偏见

如果应用程序中的偏见是不可接受的,然后改变“最近”的舍入方法消除偏见。四舍五入到最近的生成更复杂的c代码,但它可能是必要的为你如果你想消除偏见和有一个小错误。

最后定点类型表与最近的舍入和调整系数的长度是:

函数T = fir_filt_circ_buff_dsp_nearest_types () F = fimath (“RoundingMethod”,“最近的”,“OverflowAction”,“包装”,“ProductMode”,“KeepLSB”,“ProductWordLength”32岁的“SumMode”,“KeepLSB”,“SumWordLength”、32);T.acc = fi([],真的,32岁,30岁,F);T.p = int16 ([]);肺结核= fi([],真的,16日,16日,F);T.x = fi([],真的,16日,14日,F);T.y = fi([],真的,16日,14日,F);T.z = fi([],真的,16日,14日,F);结束

从入口点函数调用这个类型表和运行和策划的输出。

(y1、y2、y3、y4日元,日元)= fir_filt_circ_buff_typed_test (b (x);fir_filt_circ_buff_plot2(7、标题,t, x, y₁,日元);

代码生成命令

运行这个构建函数生成c代码。创建一个构建函数是一个最佳实践,这样你就可以为你的核心算法生成c代码没有的入口点函数或测试文件的c代码的核心算法可以包含在一个更大的项目。

函数fir_filt_circ_buff_build_function ()%%声明输入参数%T = fir_filt_circ_buff_dsp_nearest_types ();b = 0 (1 12“喜欢”、肺结核);x = 0(256年1“喜欢”,T.x);z = 0(大小(b),“喜欢”,T.z);p =投(0,“喜欢”,T.p);%%的代码生成配置%h = coder.config (“自由”);h。PurelyIntegerCode = true;h。SaturateOnIntegerOverflow = false;h。金宝appSupportNonFinite = false;h.HardwareImplementation。ProdBitPerShort = 8;h.HardwareImplementation。ProdBitPerInt = 16;h.HardwareImplementation。ProdBitPerLong = 32;%%生成c代码%codegenfir_filt_circ_buff_typed_codegenarg游戏{b, x, z, p、T}配置h-launchreport结束

生成的c代码

使用这些设置,MATLAB编码器生成c代码如下:

空白fir_filt_circ_buff_typed_codegen (const int16_T b [12], const int16_T x [256], int16_T z [12], int16_T * p, int16_T y [256]) {int16_T n;int32_T acc;int16_T k;int16_T j;(n = 0;n < 256;n + +) {(* p) + +;如果(* p > 12) {* p = 1;}z [* p - 1] = x [n];acc = 0 l; k = *p; for (j = 0; j < 12; j++) { acc += (int32_T)b[j] * z[k - 1]; k--; if (k < 1) { k = 12; } } y[n] = (int16_T)((acc >> 16) + ((acc & 32768L) != 0L)); } }

运行下面的代码来恢复全球。

fipref (FIPREF_STATE);clearInstrumentationResultsfir_filt_circ_buff_original_entry_point_mexclearInstrumentationResultsfir_filt_circ_buff_typed_entry_point_mex清晰的fir_filt_circ_buff_original_entry_point_mex清晰的fir_filt_circ_buff_typed_entry_point_mex

运行下面的代码来删除临时文件夹。

tempdirObj.cleanUp;

引用:

[1]j . r . Ragazzini和洛杉矶德。“采样系统的分析”。:交易的71年美国电气工程师学会(II)(1952),页225 - 234。