这个示例向您展示了如何通过将定点类型规范从算法代码中分离出来,将有限脉冲响应(FIR)滤波器转换为定点。
将数据类型规范与算法代码分离允许您:
重复使用不同数据类型的算法代码
使用不同数据类型的数据类型规范和切换语句保持算法整洁
让你的算法代码更具可读性
在定点和浮点之间切换以比较基线
在不改变算法代码的情况下切换不同的定点设置
此示例将有限脉冲响应(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:长度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变换有关。z变换是为了纪念Lotfi Zadeh在这一领域的开创性工作而命名的。
线性缓冲区的实现利用了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)
...
您可以像下面的MATLAB函数一样使用循环缓冲区实现FIR滤波器。
函数[y,z,p] = fir_filt_circ_buff_original(b,x,z,p) y = 0 (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 = get(fipref);resetfipref;
运行下面的代码将测试函数复制到一个临时文件夹中,这样这个示例就不会影响到您自己的工作。
tempdirObj = fidemo.fiTempdir(“fir_filt_circ_buff_fixed_point_conversion_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.172173206073645 +0.074351188907780 +0.012676739550326 -0.004324228005260 -0.004465461051254]';
时间向量
使用这个时间向量来创建测试信号。
Nx = 256;T = linspace(0,10*pi,nx)';
脉冲输入
FIR滤波器对脉冲输入的响应是滤波器系数本身。
X_impulse = 0 (nx,1);X_impulse (1) = 1;
产生最大输出的信号
当输入信号的符号与滤波器脉冲响应的符号一致时,滤波器的最大输出就出现了。
X_max_output = sign(fliplr(b))';X_max_output = repmat(X_max_output,ceil(nx/length(b)),1);X_max_output = X_max_output (1:nx);
输出的最大幅值是其脉冲响应的1模,即范数(b,1) = sum(abs(b))。
Maximum_output_magnitude = norm(b,1)% #好< * NOPTS >
Maximum_output_magnitude = 1.0352
sin的和
正弦的和是滤波器的典型输入,你可以很容易地在图中看到高频被过滤掉。
f0 = 0.1;f1 = 2;x_sin = sin(2* *t*f0) + 0.1*sin(2* *t*f1);
尖声地说
一个啁啾给出了一个很好的视觉低通滤波器的作用通过低频和衰减高频。
F_chirp = 1/16;%目标频率X_chirp = sin(pi*f_chirp*t.^2);线性啁啾标题= {“冲动”,的最大输出,sin的和,“唧唧喳喳”};X = [x_impulse, x_max_output, x_sines, x_chirp];
调用原始函数
在开始到固定点的转换之前,调用带有测试文件输入的原始函数来建立一个基线,以便与后续输出进行比较。
Y0 = 0(大小(x));为i = 1:尺寸(x, 2)初始化每一列输入的状态P = 0;Z = 0 (size(b));y0 (:, i) = fir_filt_circ_buff_original (b, x (:, i), z, p);结束
基线输出
fir_filt_circ_buff_plot(1、标题,t, x, y0)
算法在MATLAB中工作后的第一步是为仪器仪表做准备,这需要生成代码。在转换之前,您可以使用编码器。筛选器功能,用于分析代码并识别不受支持的函数和语言特性。金宝app
入口点函数
在进行检测和代码生成时,有一个入口点函数来调用要转换为定点的函数是很方便的。您可以将FIR过滤器的输入转换为不同的数据类型,并添加对过滤器的不同变体的调用以进行比较。通过使用入口点函数,您可以运行过滤器的定点和浮点变量,也可以运行不同的定点变量。这允许您更快地迭代代码,以达到最佳的定点设计。
函数Y = fir_filt_circ_buff_original_entry_point(b,x,reset)如果Nargin <3, reset = true;结束定义循环缓冲区z和缓冲区位置索引p。它们被声明为持久的,因此可以在流中调用过滤器%循环,每个部分从上一个部分停止的地方开始。持续的z p如果Isempty (z) || reset p = 0;Z = 0 (size(b));结束[y,z,p] = fir_filt_circ_buff_original(b,x,z,p);结束
测试文件
测试文件调用已编译的入口点函数。
函数Y = fir_filt_circ_buff_test(b,x)
Y = 0(大小(x));
如果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), reset}
运行原函数
通过算法运行测试文件输入,记录最小值和最大值。
Y1 = 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)
要从算法中分离数据类型,您可以:
创建数据类型定义表。
修改算法代码以使用来自该表的数据类型。
这个例子通过创建不同的文件展示了迭代步骤。在实践中,您可以对同一个文件进行迭代更改。
原始类型表
使用带有变量原型的结构创建类型表,变量原型设置为原始类型。使用基线类型来验证您正确地进行了初始转换,还可以使用它以编程方式在浮点类型和定点类型之间切换函数。索引变量j, k, n, nb, nx由MATLAB Coder™自动转换为整数,因此您不需要在表中指定它们的类型。
将原型值指定为空([]),因为使用的是数据类型,而不是值。
函数T = fir_filt_circ_buff_original_types() T.acc=double([]);结核病=双([]);T.p =双([]);T.x =双([]);T.y =双([]);T.z =双([]);结束
类型感知过滤器函数
通过使用强制转换和零函数以及类型表,将过滤器函数和入口点函数准备为类型感知的。
使用下标赋值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 = zeros(size(x),'like',T.y)创建一个与x大小相同的零数组,具有变量T.y的属性。最初,T.y是在函数fir_filt_circ_buff_original_types中定义的双精度类型,但在本例稍后将其重新定义为定点类型。
函数调用acc = cast(0,'like',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 = cast(0,“喜欢”, T.acc);K = p;为J =1:nb acc(:) = acc + b(J)*z(k);k (:) = k - 1;如果k < 1, k(:) =注;结束结束Y (n) = acc;结束结束
类型感知的入口点函数
函数调用p1 = cast(0,'like',T1.p)将值0转换为与变量T1.p相同的属性。最初,T1。P是函数fir_filt_circ_buff_original_types中定义的double类型,但在本例后面将它重新定义为整数类型。
函数调用z1 = zeros(size(b),'like',T1.z)创建一个与b大小相同的0数组,其属性为变量T1.z。最初,T1。Z是函数fir_filt_circ_buff_original_types中定义的double类型,但在本例后面将它重新定义为定点类型。
函数Y1 = fir_filt_circ_buff_typed_entry_point(b,x,reset)如果Nargin <3, reset = true;结束%%基线类型%T1 = fir_filt_circ_buff_original_types();每次对过滤器的调用都需要维护自己的状态。持续的z1 p1如果Isempty (z1) || reset p1 = cast(0,“喜欢”, T1.p);Z1 = 0 (size(b),“喜欢”, T1.z);结束B1 = cast(b,“喜欢”, T1.b);X1 = cast(x,“喜欢”, T1.x);[y1,z1,p1] = fir_filt_circ_buff_typed(b1,x1,z1,p1,T1);结束
验证修改后的函数
每次修改函数时,验证结果是否与基线匹配。由于您使用了类型表中的原始类型,因此输出应该是相同的。这将验证您正确地将类型从算法中分离出来。
buildInstrumentedMexfir_filt_circ_buff_typed_entry_pointarg游戏{b, x(:,1), reset}Y1 = 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和Variables选项卡查看这些结果。
创建一个定点类型表
使用代码生成报告中建议的类型来指导您选择定点类型,并使用带有变量原型的结构创建定点类型表。
利用你的算法知识来改进提案。例如,您正在使用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如果Isempty (z2) || reset p2 = cast(0,“喜欢”, T2.p);Z2 = 0 (size(b),“喜欢”, T2.z);结束B2 = cast(b,“喜欢”, T2.b);X2 = cast(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), reset}[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代码,那么请进入下一节。
本节描述如何从上一节中的定点MATLAB代码生成高效的c代码。
所需的产品下载188bet金宝搏
您需要MATLAB Coder™来生成c代码,并且您需要Embedded Coder®来实现本示例中使用的硬件实现设置。
算法调整为最有效的c代码
输出变量y被初始化为0,然后在使用它之前被完全覆盖。因此,用全零填充y是不必要的。你可以使用编码器。Nullcopy函数声明一个变量,而不实际用值填充它,这使得在这种情况下的代码更有效。但是,在使用编码器时必须非常小心。Nullcopy,因为如果你在变量的元素被赋值之前访问它,那么你正在访问未初始化的内存,它的内容是不可预测的。
什么时候使用编码器的经验法则。Nullcopy是指初始化比算法的其他部分花费了大量时间。如果你不确定,那么最安全的做法就是不要使用它。
函数(y, z, p) = fir_filt_circ_buff_typed_codegen (b, x, z, p, T)%使用编码器。的每一个值都为空时才进行Nullcopy%变量在使用之前被覆盖。Y = code .nullcopy(0 (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位整数类型,并保留数学操作的最低有下载188bet金宝搏效位(lsb)。
将这些设置添加到一个定点类型表中。
函数T = fir_filt_circ_buff_dsp_types()“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代码类型
向入口点函数中的types表添加一个调用,并运行测试文件。
[y1,y2,y3] = fir_filt_circ_buff_typed_test(b,x);% #好< * ASGLU >
在第二行图中,您可以看到最大输出误差是输入误差的两倍,这表明本应为正的值溢出为负。您还可以看到其他输出没有溢出。这就是为什么除了其他典型输入外,让您的测试文件测试所有范围的值是很重要的。
fir_filt_circ_buff_plot2(5、标题,t, x, y₁,y3);
缩放双类型以查找溢出
缩放双变量将它们的数据存储在双精度浮点中,因此它们在全范围内执行算术。它们还保留它们的定点设置,因此当计算超出定点类型的范围时,它们能够报告。
将数据类型更改为伸缩双精度,并将这些设置添加到伸缩双精度类型表中。
函数T = fir_filt_circ_buff_scaled_double_types()“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);结束
向入口点函数添加一个对缩放双类型表的调用,并运行测试文件。
[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代码中的“+”上:
Acc (:) = Acc + b(j)*z(k);
报告显示,总额溢出:
求和溢出的原因是b(j)*z(k)的全精度积产生了一个数字类型(true,32,31),因为b具有数字类型(true,16,17)而z具有数字类型(true,16,14)。sum类型被设置为“保持最低有效位”(keepplsb),因此sum具有数值类型(true,32,31)。但是,需要2个整数位来分别存储-1.0045和+1.035的最小和最大模拟值。
调整以避免溢出
将b的分数长度设置为16而不是17,这样b(j)*z(k)是数字类型(true,32,30),因此和也是数字类型(true,32,30),遵循求和的keepplsb规则。
保持所有其他设置不变,然后设置
肺结核= fi([],真的,16日,16日,F);
那么这行MATLAB代码中的和就不再溢出:
Acc (:) = Acc + b(j)*z(k);
运行带有新设置的测试文件并绘制结果。
[y1,y2,y3,y4,y5] = 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()“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,y5,y6] = 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 (size(b),“喜欢”, T.z);P = cast(0,“喜欢”, T.p);%代码生成配置%H = code .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 Coder生成以下c代码:
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 = 0L; 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·拉格齐尼和l·a·扎德。“抽样数据系统分析”。见:美国电气工程师学会学报71 (II)(1952),第225-234页。