主要内容

使用铸造和零实现浮点和定点类型的FIR滤波器算法

此示例通过将Firect-Point类型规范从算法代码分离,因此如何将有限脉冲响应(FIR)过滤器转换为固定点。

将数据类型规范与算法代码分离可以:

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

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

  • 保持算法代码更可读

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

  • 在不改变算法代码的情况下切换定点设置的变化

原始算法

这个例子将有限脉冲响应(FIR)滤波器的MATLAB®代码转换为定点。

给定滤波器系数b和输入x, FIR滤波器第n个输出y(n)的公式为:

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

线性缓冲区实现

有几种不同的方法来编写FIR滤波器。一种方法是使用线性缓冲区,如下面的函数,其中b是行向量,z是与b长度相同的列向量。

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

载体z由X的当前和先前的样本组成:

z = [x(n);x(n-1);......;X(n长(b)+1)]

向量z称为状态向量。它既可用作输入,也可用作输出。当它是输入时,它是过滤器的初始状态。当它是输出时,它是过滤器的最终状态。在函数外部定义状态向量允许您在实时系统中连续地通过过滤器传输数据,并在同一项目中重用过滤器算法。状态向量通常被命名为z,因为它是延迟的同义词美元z ^ {1} $与z变换相关。z变换以Lotfi Zadeh的名字命名,以表彰他在这一领域的开创性工作[1]。

线性缓冲区的实现利用MATLAB方便的矩阵语法,易于阅读和理解。但是,它为每个输入示例引入了状态缓冲区的完整副本。

环形缓冲区的实现

为了更有效地实现FIR滤波器,您可以将状态存储在一个循环缓冲区z中,其元素是z(p) = x(n),其中p=mod(n-1,length(b))+1,对于n= 1,2,3, ....

例如,设length(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)
...

您可以使用循环缓冲区来实现FIR滤波器,如下所示的MATLAB函数。

函数[y,z,p] = fir_filt_circ_buff_original(b,x,z,p)y = zeros(size(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_convers_example');
拷贝文件(fullfile (matlabroot,“工具箱”“定点”“fidemos”'+ fidemo'...“fir_filt_ * m”),“。”'F');

滤波器系数

使用以下低通滤波器系数,这些系数是使用信号处理工具箱中的fir1函数计算得到的。

b = fir1(11,0.25);
B = [-0.004465461051254 -0.004324228005260 +0.012676739550326 +0.074351188907780 +0.172173206073645 +0.249588554524763 +0.249588554524763 +0.172173206073645 +0.074351188907780 +0.012676739550326 -0.004465461051254]';

时间向量

使用这个时间向量来创建测试信号。

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

脉冲输入

FIR滤波器对脉冲输入的响应是滤波器系数本身。

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范数,即范数(b,1) = sum(abs(b))。

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

的正弦

正弦和是滤波器的典型输入,你可以很容易地在图中看到高频被过滤掉了。

f0 = 0.1;f1 = 2;x_sin = sin(2* t*f0) + 0.1*sin(2* t*f1);

尖声地说

啁啾可以很好地显示低通滤波器通过低频和衰减高频的作用。

f_chirp = 1/16;%目标频率x_chirp =罪(π* f_chirp * t ^ 2);%线性啁啾标题= {“冲动”'最大输出''豆子的总和'“唧唧喳喳”};X = [x_impulse, x_max_output, x_sine, 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中工作后的第一步是准备用于检测,这需要代码生成。在转换之前,您可以使用编码器。筛选函数,用于分析代码并识别不支持的函数和语言特性。万博1manbetx

入口点函数

在进行检测和代码生成时,使用一个入口点函数来调用要转换为固定点的函数是很方便的。您可以将FIR过滤器的输入转换为不同的数据类型,并添加对过滤器不同变体的调用以进行比较。通过使用入口点函数,您可以运行您的过滤器的定点和浮点变体,以及定点的不同变体。这允许您更快地迭代代码以获得最佳的定点设计。

函数y = fir_filt_circ_buff_original_entry_point(b,x,重置)如果Nargin <3, reset = true;结束定义循环缓冲区z和缓冲区位置索引p。%它们被声明持久化,因此可以在流中调用过滤器%循环,每一节从最后一节停止的地方开始。持续的z p如果is空(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));
For i=1:size(x,2) reset = 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

  • 选择Variables选项卡

验证原始的函数

每次修改函数时,都要验证结果是否仍然匹配基线。

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

将函数转换为使用类型表

要从算法中分离数据类型,请执行以下操作:

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

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

这个例子展示了创建不同文件的迭代步骤。在实践中,您可以对同一个文件进行迭代更改。

原始类型表

使用结构创建一个类型表,该结构中包含变量初始类型的原型。使用基线类型来验证初始转换是否正确,并使用它以编程方式在浮点类型和定点类型之间切换函数。索引变量j, k, n, nb, nx通过MATLAB Coder™自动转换为整数,所以你不需要在表中指定它们的类型。

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

函数t = fir_filt_circ_buff_original_types()t.acc = double([]);t.b = double([]);t.p = double([]);t.x = double([]);t.y = double([]);t.z = double([]);结束

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 = CAST(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 = zeros(size(x),'喜欢', T.y);nx =长度(x);nb =长度(b);n = 1: nx p (:) = p + 1;如果p > nb, p (,) = 1;结束z (p) = x (n);ACC = CAST(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 = cast(0,',',t1.p)与变量t1.p的属性相同的属性投射值0。最初,T1.P是函数fir_filt_circ_buff_original_types中的双重定义,但它在此示例中重新定义为整数类型。

函数调用z1 = zeros(size(b),'like',T1.z)创建一个与b大小相同的零数组,变量为T1.z。最初,T1。Z是在函数fir_filt_circ_buff_original_types中定义的一个double类型,但是在本例的后面,它被重新定义为定点类型。

函数日元= fir_filt_circ_buff_typed_entry_point (b, x,重置)如果Nargin <3, reset = 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_point.arg游戏{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([],true,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如果is空(z2) ||重置p2 = cast(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_point.arg游戏{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);
  • 选择Variables选项卡

16位字长,完全精确的数学

验证结果在基准的可接受公差范围内。

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

你的算法现在已经被转换为定点MATLAB代码。如果您还想转换为c代码,请继续下一节。

生成c代码

本节描述如何从前一节中的定点MATLAB代码生成高效的c代码。

所需的产品s manbetx 845

你需要MATLAB Coder™来生成c代码,你需要Embedded Coder®用于本例中使用的硬件实现设置。

优化的算法最有效的c代码

输出变量y被初始化为零,然后在使用它之前完全重写。因此,用所有的0填充y是不必要的。你可以用编码器。Nullcopy函数来声明一个变量,而不实际用值填充它,这使得这种情况下的代码更有效。然而,在使用coder时必须非常小心。因为如果你在变量赋值之前访问它的元素,那么你是在访问未初始化的内存,它的内容是不可预知的。

什么时候使用编码器的经验法则。与算法的其他部分相比,初始化需要大量的时间。如果你不确定,那么最安全的做法就是不要使用它。

函数[y,z,p] = fir_filt_circ_buff_typed_codegen(b,x,z,p,t)只有当您确定每一个值时,才能使用编码器.NULLCOPY%该变量在使用前已被覆盖。y = coder.nullcopy(zeros(size(x),'喜欢'T.y));nx =长度(x);nb =长度(b);n = 1: nx p (:) = p + 1;如果p > nb, p (,) = 1;结束z (p) = x (n);ACC = CAST(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位整数类型,并保留数学操作的最低s manbetx 845有效位(lbs)。

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

函数t = fiMath()f = fimath('roundingmethod'“地板”...“OverflowAction”“包装”...'ProductMode'“KeepLSB”...'profectwordlength'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代码类型

在入口点函数中添加一个对types表的调用,然后运行测试文件。

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

在图的第二行中,您可以看到最大输出错误是输入大小的两倍,这表明一个应该为正的值溢出为负。您还可以看到其他输出没有溢出。这就是为什么让您的测试文件练习除了其他典型输入之外的全部值范围是很重要的。

fir_filt_circ_buff_plot2(5,标题,t,x,y1,y3);

缩放Double类型以查找溢出

缩放双变量将其数据存储在双精度浮点中,因此它们执行全范围的算术运算。它们还保留它们的定点设置,因此当计算超出定点类型的范围时,它们能够报告。

将数据类型更改为缩放双精度,并将这些设置添加到缩放双精度类型表中。

函数T = fir_filt_circ_buff_scaled_doule_types () F = fimath('roundingmethod'“地板”...“OverflowAction”“包装”...'ProductMode'“KeepLSB”...'profectwordlength'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 >

显示缩放双类型的检测结果。

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);
  • 选择Variables选项卡。

  • 看看表中的变量。遍布溢出的变量都没有,这表示作为操作结果发生的溢出发生。

  • 将鼠标悬停在报表中的操作符(+、-、*、=)上。

  • 将鼠标悬停在代码生成报告中这行MATLAB代码中的“+”上:

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

报告显示:

sum溢出的原因是b(j)*z(k)的全精度乘积产生一个numerictype(true,32,31),因为b有numerictype(true,16,17), z有numerictype(true,16,14)。和类型设置为“保留最低有效位”(KeepLSB),因此和具有numerictype(true,32,31)。但是,需要两个整数位来存储最小的模拟值-1.0045和最大的模拟值+1.035。

调整以避免溢出

将B到16的分数长而不是17,以便B(j)* z(k)是numerictype(true,32,30),因此该和在Keeplsb规则之后的总和也是numerictype(true,32,30)总和。

保持所有其他设置不变,然后设置

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

然后在MATLAB代码中的总和不再溢出:

c(:) = c + 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”...'profectwordlength'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 =零(1,12,'喜欢'、肺结核);x = 0(256年1'喜欢',t.x);z =零(尺寸(b),'喜欢', T.z);p =投(0,'喜欢',t.p);%代码生成配置h = coder.config (“自由”);h.PurelyIntegerCode = true;h.SaturateOnIntegerOverflow = false;h.万博1manbetxSupportNonFinite = false;h.HardwareImplementation.ProdBitPerShort = 8;h.HardwareImplementation.ProdBitPerInt = 16;h.HardwareImplementation.ProdBitPerLong = 32;%生成C代码codegenfir_filt_circ_buff_typed_codegen.arg游戏{b, x, z, p、T}配置h-launchreport结束

生成的c代码

使用这些设置,MATLAB编码器生成以下C-code:

void 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;For (n = 0;n < 256;n + +) {(* p) + +;If (*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;

引用:

J. R. Ragazzini和l.a. . Zadeh。抽样数据系统的分析。见:美国电气工程师学会学报71 (II)(1952),第225-234页。