有一个FIR滤波器的算法,但它是浮点数:C 语言编程中的 FIR 滤波器实现
如果我想要一个具有此规范的定点算法,我将如何做?
FIR滤波器接收并发送8位定点数 Q7格式通过标准输入和输出。记得输出 测量时间(即时报价数(也采用十六进制格式。在 上一节中介绍的准则,您的程序应调用 getchar(( 读取 Q7 值。应该调用 putchar(( 来编写一个 Q7 值。
系数为
c0=0.0299 c1=0.4701c2=0.4701 c3=0.299
对于定点算法,我需要实现自己的定点数乘法,对吧?
我应该像结构体一样存储固定点数,即
struct point
{
int integer;
int fraction;
};
我应该使用班次来实现编号,特别是如何实现?
数字是 32 位的,所以我可以像下面这样写班次吗?
#define SHIFT_AMOUNT 16 // 2^16 = 65536
#define SHIFT_MASK ((1 << SHIFT_AMOUNT) - 1)
所以我认为我必须实现一种乘法算法,然后是 FIR 算法本身?这是对的吗?你可以帮我吗?
更新
我编译并运行了一个像在 anser 中一样的程序,但它给了我意想不到的输出。
#include <stdio.h>
#include "system.h"
#define FBITS 16 /* number of fraction bits */
const int c0 = (( 299<<FBITS) + 5000) / 10000; /* (int)(0.0299*(1<<FBITS) + 0.5) */
const int c1 = ((4701<<FBITS) + 5000) / 10000; /* (int)(0.4701*(1<<FBITS) + 0.5) */
/* Ditto for C3 and C2 */
const int c2 = (( 4701<<FBITS) + 5000) / 10000; /* (int)(0.4701 *(1<<FBITS) + 0.5) */
const int c3 = ((299<<FBITS) + 5000) / 10000; /* (int)(0.299*(1<<FBITS) + 0.5) */
#define HALF (1 << (FBITS) >> 1) /* Half adjust for rounding = (int)(0.5 * (1<<FBITS)) */
signed char input[4]; /* The 4 most recent input values */
int output = 0;
void firFixed()
{
signed char sum = c0*input[0] + c1*input[1] + c2*input[2] + c3*input[3];
output = (signed char)((sum + HALF) >> FBITS);
printf("output: %dn", output);
}
int main( void )
{
int i=0;
signed char inVal;
while (scanf("%c", &inVal) > 0)
{
if (i>3)
{
i=0;
}
input[i]=inVal;
firFixed();
i++;
}
return 0;
}
为什么输出计算不正确,为什么输出在一次输入后写入多次?
更新
我尝试编写定点FIR滤波器,算法可能不是100%正确的:
#include <stdio.h>
#include "system.h"
#define FBITS 16 /* number of fraction bits */
const int c0 = (( 299<<FBITS) + 5000) / 10000; /* (int)(0.0299*(1<<FBITS) + 0.5) */
const int c1 = ((4701<<FBITS) + 5000) / 10000; /* (int)(0.4701*(1<<FBITS) + 0.5) */
/* Ditto for C3 and C2 */
const int c2 = (( 4701<<FBITS) + 5000) / 10000; /* (int)(0.4701 *(1<<FBITS) + 0.5) */
const int c3 = ((299<<FBITS) + 5000) / 10000; /* (int)(0.299*(1<<FBITS) + 0.5) */
#define HALF (1 << (FBITS) >> 1) /* Half adjust for rounding = (int)(0.5 * (1<<FBITS)) */
signed char input[4]; /* The 4 most recent input values */
char get_q7( void );
void put_q7( char );
void firFixed()
{
int sum = c0*input[0] + c1*input[1] + c2*input[2] + c3*input[3];
signed char output = (signed char)((sum + HALF) >> FBITS);
put_q7(output);
}
int main( void )
{
int i=0;
while(1)
{
if (i>3)
{
i=0;
}
input[i]=get_q7();
firFixed();
i++;
}
return 0;
}
#include <sys/alt_stdio.h>
char get_q7( void );
char prompt[] = "Enter Q7 (in hex-code): ";
char error1[] = "Illegal hex-code - character ";
char error2[] = " is not allowed";
char error3[] = "Number too big";
char error4[] = "Line too long";
char error5[] = "Line too short";
char get_q7( void )
{
int c; /* Current character */
int i; /* Loop counter */
int num;
int ok = 0; /* Flag: 1 means input is accepted */
while( ok == 0 )
{
num = 0;
for( i = 0; prompt[i]; i += 1 )
alt_putchar( prompt[i] );
i = 0; /* Number of accepted characters */
while( ok == 0 )
{
c = alt_getchar();
if( c == (char)26/*EOF*/ ) return( -1 );
if( (c >= '0') && (c <= '9') )
{
num = num << 4;
num = num | (c & 0xf);
i = i + 1;
}
else if( (c >= 'A') && (c <= 'F') )
{
num = num << 4;
num = num | (c + 10 - 'A');
i = i + 1;
}
else if( (c >= 'a') && (c <= 'f') )
{
num = num << 4;
num = num | (c + 10 - 'a');
i = i + 1;
}
else if( c == 10 ) /* LF finishes line */
{
if( i > 0 ) ok = 1;
else
{ /* Line too short */
for( i = 0; error5[i]; i += 1 )
alt_putchar( error5[i] );
alt_putchar( 'n' );
break; /* Ask for a new number */
}
}
else if( (c & 0x20) == 'X' || (c < 0x20) )
{
/* Ignored - do nothing special */
}
else
{ /* Illegal hex-code */
for( i = 0; error1[i]; i += 1 )
alt_putchar( error1[i] );
alt_putchar( c );
for( i = 0; error2[i]; i += 1 )
alt_putchar( error2[i] );
alt_putchar( 'n' );
break; /* Ask for a new number */
}
if( ok )
{
if( i > 10 )
{
alt_putchar( 'n' );
for( i = 0; error4[i]; i += 1 )
alt_putchar( error4[i] );
alt_putchar( 'n' );
ok = 0;
break; /* Ask for a new number */
}
if( num >= 0 && num <= 255 )
return( num );
for( i = 0; error3[i]; i += 1 )
alt_putchar( error3[i] );
alt_putchar( 'n' );
ok = 0;
break; /* Ask for a new number */
}
}
}
return( 0 ); /* Dead code, or the compiler complains */
}
#include <sys/alt_stdio.h>
void put_q7( char ); /* prototype */
char prom[] = "Calculated FIR-value in Q7 (in hex-code): 0x";
char hexasc (char in) /* help function */
{
in = in & 0xf;
if (in <=9 ) return (in + 0x30);
if (in > 9 ) return (in - 0x0A + 0x41);
return (-1);
}
void put_q7( char inval)
{
int i; /* Loop counter */
for( i = 0; prom[i]; i += 1 )
alt_putchar( prom[i] );
alt_putchar (hexasc ((inval & 0xF0) >> 4));
alt_putchar (hexasc (inval & 0x0F));
alt_putchar ('n');
}
FIR 滤波器结果中的每个点只是未过滤数据中值的加权总和。如果您有 8 位输入数据和 32 位算术,则除了普通乘法和加法之外,您不需要任何其他内容。
快速访问维基百科告诉我,Q7本质上是一个8位2的补码整数,所以如果目标平台使用2的补码,那么简单地将收到的字节描述为(有符号字符(将在提升为整数时为其提供正确的数值。 如果将系数预乘以 2 的幂,则加权和将乘以相同的 2 次幂。 然后,四舍五入除法只是添加一个半调整值,后跟一个带符号的右移。 对于 16 位分数,预乘常量为:
#define FBITS 16 /* number of fraction bits */
const int C0 = (( 299<<FBITS) + 5000) / 10000; /* (int)(0.0299*(1<<FBITS) + 0.5) */
const int C1 = ((4701<<FBITS) + 5000) / 10000; /* (int)(0.4701*(1<<FBITS) + 0.5) */
/* Ditto for C3 and C2 */
#define HALF (1 << (FBITS) >> 1) /* Half adjust for rounding = (int)(0.5 * (1<<FBITS)) */
这种奇怪的原因是在不依赖任何浮点舍入的情况下获得您想要的有效位。 现在,如果:
signed char input[4];
。包含 4 个最新的输入值,您的输出值为:
sum = c0*input[0] + c1*input[1] + c2*input[2] + c3*input[3];
output = (signed char)((sum + HALF) >> FBITS);
由于所有系数均为正且总和为 1.0,因此不可能溢出。
在获得简单版本工作后,您可以尝试许多优化。 其他系数的一个可能的小故障是 C0-C3 常量四舍五入以产生不完全等于 1<<FBITS
的值。 我测试了一下,这些值不会发生这种情况(你需要c0*(1<<LBITS)
有一个正好是 0.5 的分数部分;这意味着所有其他缩放系数也将有 0.5 作为它们的分数部分。 它们都四舍五入,总和会太大 2。 这可能会给滤波器增加非常小的意外增益。
这不会发生在你给出的系数上。
编辑:我忘了。在求和计算期间,整数部分和分数部分都位于相同的 32 位 int 中。 使用 8 位输入(7+符号(和 16 位分数,过滤器中最多可以有 2^(32 - 16 - 8( = 2^8 = 256 个点(此时,您显然会有一个系数数组,以及一个乘加循环来计算总和。 如果(输入大小(+(分数位(+ log2(滤波器大小(超过 32,则可以尝试将 sum 字段扩展到 C99 long long 或 int64_t 值(如果可用(,或者编写扩展精度加移逻辑(如果不可用(。 硬件中的扩展精度在可用时使用起来要好得多。