Diseñé un filtro Butterworth de paso bajo muy simple usando Matlab. El siguiente fragmento de código demuestra lo que he hecho.
fs = 2.1e6;
flow = 44 * 1000;
fNorm = flow / (fs / 2);
[b,a] = butter(10, fNorm, 'low');
En [b, a] se almacenan los coeficientes del filtro. Me gustaría obtener [b, a] como enteros para poder usar un generador de código HDL en línea para generar código en Verilog.
Los valores de Matlab [b, a] parecen ser demasiado pequeños para usar con el generador de código en línea (el script Perl del lado del servidor se niega a generar código con los coeficientes), y me pregunto si sería posible obtener [b, a] en una forma que se puede utilizar como una entrada adecuada.
Los coeficientes a que obtengo en Matlab son:
1.0000
-9.1585
37.7780
-92.4225
148.5066
-163.7596
125.5009
-66.0030
22.7969
-4.6694
0.4307
Los coeficientes b que obtengo en Matlab son:
1.0167e-012
1.0167e-011
4.5752e-011
1.2201e-010
2.1351e-010
2.5621e-010
2.1351e-010
1.2201e-010
4.5752e-011
1.0167e-011
1.0167e-012
Usando el generador en línea, me gustaría diseñar un filtro con un ancho de bit de 12 bits y un formulario de filtro I o II. No sé qué se entiende por "bits fraccionales" en el enlace anterior.
Al ejecutar el generador de código (http://www.spiral.net/hardware/filter.html) con los coeficientes [b, a] enumerados anteriormente, con bits fraccionarios establecidos en 20 y un ancho de bits de 12, recibo el siguiente error de ejecución :
Integer A constants: 1048576 -9603383 39613104 -96912015 155720456 -171714386 131597231 -69209161 23904282 -4896220 451621
Integer B constants: 0 0 0 0 0 0 0 0 0 0 0
Error: constants wider than 26 bits are not allowed, offending constant = -69209161, effective bitwidth = 7 mantissa + 20 fractional = 27 total.
An error has occurred - please revise the input parameters.
¿Cómo puedo cambiar mi diseño para que este error no ocurra?
ACTUALIZACIÓN: Al usar Matlab para generar un filtro Butterworth de sexto orden, obtengo los siguientes coeficientes:
Para:
1.0000
-5.4914
12.5848
-15.4051
10.6225
-3.9118
0.6010
para b:
0.0064e-005
0.0382e-005
0.0954e-005
0.1272e-005
0.0954e-005
0.0382e-005
0.0064e-005
Al ejecutar el generador de código en línea (http://www.spiral.net/hardware/filter.html), ahora recibo el siguiente error (con bits fraccionales como 8 y ancho de bits de 20):
./iirGen.pl -A 256 '-1405' '3221' '-3943' '2719' '-1001' '153' -B '0' '0' '0' '0' '0' '0' '0' -moduleName acm_filter -fractionalBits 8 -bitWidth 20 -inData inData -inReg -outReg -outData outData -clk clk -reset reset -reset_edge negedge -filterForm 1 -debug -outFile ../outputs/filter_1330617505.v 2>&1
At least 1 non-zero-valued constant is required. Please check the inputs and try again.
¿Quizás los coeficientes b son demasiado pequeños, o quizás el generador de código (http://www.spiral.net/hardware/filter.html) quiere el [b, a] en otro formato?
ACTUALIZAR:
Quizás lo que necesito hacer es escalar los coeficientes [b, a] por el número de bits fraccionarios para obtener los coeficientes como enteros.
a .* 2^12
b .* 2^12
Sin embargo, sigo pensando que los coeficientes b son extremadamente pequeños. ¿Qué estoy haciendo mal aquí?
¿Quizás otro tipo de filtro (o método de diseño de filtro) sería más adecuado? ¿Alguien podría hacer una sugerencia?
ACTUALIZACIÓN: Según lo sugerido por Jason R y Christopher Felton en los comentarios a continuación, un filtro SOS sería más adecuado. Ahora he escrito un código de Matlab para obtener un filtro SOS.
fs = 2.1e6;
flow = 44 * 1000;
fNorm = flow / (fs / 2);
[A,B,C,D] = butter(10, fNorm, 'low');
[sos,g] = ss2sos(A,B,C,D);
La matriz SOS que obtengo es:
1.0000 3.4724 3.1253 1.0000 -1.7551 0.7705
1.0000 2.5057 1.9919 1.0000 -1.7751 0.7906
1.0000 1.6873 1.0267 1.0000 -1.8143 0.8301
1.0000 1.2550 0.5137 1.0000 -1.8712 0.8875
1.0000 1.0795 0.3046 1.0000 -1.9428 0.9598
¿Es posible seguir utilizando la herramienta de generación de código Verilog (http://www.spiral.net/hardware/filter.html) para implementar este filtro SOS, o debería simplemente escribir el Verilog a mano? ¿Hay una buena referencia disponible?
Me pregunto si un filtro FIR sería mejor para usar en esta situación.
MÁS INFORMACIÓN: Los filtros IIR recursivos se pueden implementar usando matemáticas enteras expresando coeficientes como fracciones. (Consulte el excelente libro de procesamiento de señales DSP de Smith para obtener más detalles: http://www.dspguide.com/ch19/5.htm )
El siguiente programa de Matlab convierte los coeficientes de filtro de Butterworth en partes fraccionarias utilizando la función Matlab rat (). Luego, como se menciona en los comentarios, las secciones de segundo orden se pueden usar para implementar numéricamente el filtro (http://en.wikipedia.org/wiki/Digital_biquad_filter).
% variables
% variables
fs = 2.1e6; % sampling frequency
flow = 44 * 1000; % lowpass filter
% pre-calculations
fNorm = flow / (fs / 2); % normalized freq for lowpass filter
% uncomment this to look at the coefficients in fvtool
% compute [b,a] coefficients
% [b,a] = butter(7, fNorm, 'low');
% fvtool(b,a)
% compute SOS coefficients (7th order filter)
[z,p,k] = butter(7, fNorm, 'low');
% NOTE that we might have to scale things to make sure
% that everything works out well (see zp2sos help for 'up' and 'inf' options)
sos = zp2sos(z,p,k, 'up', 'inf');
[n,d] = rat(sos);
sos_check = n ./ d; % this should be the same as SOS matrix
% by here, n is the numerator and d is the denominator coefficients
% as an example, write the the coefficients into a C code header file
% for prototyping the implementation
% write the numerator and denominator matices into a file
[rownum, colnum] = size(n); % d should be the same
sections = rownum; % the number of sections is the same as the number of rows
fid = fopen('IIR_coeff.h', 'w');
fprintf(fid, '#ifndef IIR_COEFF_H\n');
fprintf(fid, '#define IIR_COEFF_H\n\n\n');
for i = 1:rownum
for j = 1:colnum
if(j <= 3) % b coefficients
bn = ['b' num2str(j-1) num2str(i) 'n' ' = ' num2str(n(i,j))];
bd = ['b' num2str(j-1) num2str(i) 'd' ' = ' num2str(d(i,j))];
fprintf(fid, 'const int32_t %s;\n', bn);
fprintf(fid, 'const int32_t %s;\n', bd);
end
if(j >= 5) % a coefficients
if(j == 5)
colstr = '1';
end
if(j == 6)
colstr = '2';
end
an = ['a' colstr num2str(i) 'n' ' = ' num2str(n(i,j))];
ad = ['a' colstr num2str(i) 'd' ' = ' num2str(d(i,j))];
fprintf(fid, 'const int32_t %s;\n', an);
fprintf(fid, 'const int32_t %s;\n', ad);
end
end
end
% write the end of the file
fprintf(fid, '\n\n\n#endif');
fclose(fid);