Projetei um filtro Butterworth low-pass muito simples usando o Matlab. O seguinte trecho de código demonstra o que eu fiz.
fs = 2.1e6;
flow = 44 * 1000;
fNorm = flow / (fs / 2);
[b,a] = butter(10, fNorm, 'low');
Em [b, a] são armazenados os coeficientes do filtro. Eu gostaria de obter [b, a] como números inteiros para que eu possa usar um gerador de código HDL online para gerar código no Verilog.
Os valores do Matlab [b, a] parecem muito pequenos para serem usados com o gerador de código on-line (o script Perl do lado do servidor se recusa a gerar código com os coeficientes), e estou pensando se seria possível obter [b, a] de uma forma que possa ser usada como uma entrada adequada.
Os coeficientes a que recebo no Matlab são:
1.0000
-9.1585
37.7780
-92.4225
148.5066
-163.7596
125.5009
-66.0030
22.7969
-4.6694
0.4307
Os coeficientes b que recebo no Matlab são:
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 o gerador on-line, eu gostaria de criar um filtro com uma largura de bits de 12 bits e um formulário de filtro I ou II. Não sei o que se entende por "bits fracionários" no link acima.
Executando o gerador de código (http://www.spiral.net/hardware/filter.html) com os coeficientes [b, a] listados acima, com bits fracionários definidos em 20 e uma largura de bit de 12, recebo o seguinte erro de execução :
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.
Como posso alterar meu design para que esse erro não ocorra?
ATUALIZAR: Usando o Matlab para gerar um filtro Butterworth de 6ª ordem, obtenho os seguintes 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
Executando o gerador de código online (http://www.spiral.net/hardware/filter.html), agora recebo o seguinte erro (com bits fracionários como 8 e largura de bit 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.
Talvez os coeficientes b sejam muito pequenos, ou talvez o gerador de código (http://www.spiral.net/hardware/filter.html) queira o [b, a] em outro formato?
ATUALIZAR:
Talvez o que eu precise fazer seja dimensionar os coeficientes [b, a] pelo número de bits fracionários para obter os coeficientes como números inteiros.
a .* 2^12
b .* 2^12
No entanto, ainda acho que os coeficientes b são extremamente pequenos. O que eu estou fazendo errado aqui?
Talvez outro tipo de filtro (ou método de design de filtro) seja mais adequado? Alguém poderia fazer uma sugestão?
ATUALIZAÇÃO: Conforme sugerido por Jason R e Christopher Felton nos comentários abaixo, um filtro SOS seria mais adequado. Agora eu escrevi um código Matlab para obter um 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);
A matriz SOS que recebo é:
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
Ainda é possível usar a ferramenta de geração de código Verilog (http://www.spiral.net/hardware/filter.html) para implementar esse filtro SOS, ou devo simplesmente escrever o Verilog à mão? Existe uma boa referência disponível?
Gostaria de saber se um filtro FIR seria melhor usar nesta situação.
Além disso, os filtros IIR recursivos podem ser implementados usando matemática inteira, expressando coeficientes como frações. (Consulte o excelente livro de processamento de sinal DSP da Smith para obter mais detalhes: http://www.dspguide.com/ch19/5.htm )
O programa Matlab a seguir converte os coeficientes do filtro Butterworth em partes fracionárias usando a função rat () do Matlab. Como mencionado nos comentários, as seções de segunda ordem podem ser usadas para implementar numericamente o 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);