Ho progettato un filtro Butterworth passa-basso molto semplice usando Matlab. Il seguente frammento di codice dimostra ciò che ho fatto.
fs = 2.1e6;
flow = 44 * 1000;
fNorm = flow / (fs / 2);
[b,a] = butter(10, fNorm, 'low');
In [b, a] sono memorizzati i coefficienti di filtro. Vorrei ottenere [b, a] come numeri interi in modo da poter utilizzare un generatore di codice HDL online per generare codice in Verilog.
I valori di Matlab [b, a] sembrano essere troppo piccoli per essere utilizzati con il generatore di codice online (lo script Perl sul lato server rifiuta di generare codice con i coefficienti) e mi chiedo se sarebbe possibile ottenere [b, a] in una forma che può essere utilizzata come input corretto.
I coefficienti a che ottengo in Matlab sono:
1.0000
-9.1585
37.7780
-92.4225
148.5066
-163.7596
125.5009
-66.0030
22.7969
-4.6694
0.4307
I coefficienti b che ottengo in Matlab sono:
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 il generatore online, vorrei progettare un filtro con una larghezza di bit a 12 bit e una forma di filtro I o II. Non so cosa si intenda per "bit frazionari" al link sopra.
Eseguendo il generatore di codice (http://www.spiral.net/hardware/filter.html) con i coefficienti [b, a] sopra elencati, con bit frazionari impostati su 20 e una larghezza di bit di 12, viene visualizzato il seguente errore di esecuzione :
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.
Come posso cambiare il mio design in modo che questo errore non si verifichi?
AGGIORNAMENTO: Usando Matlab per generare un filtro Butterworth del 6 ° ordine, ottengo i seguenti coefficienti:
Per un:
1.0000
-5.4914
12.5848
-15.4051
10.6225
-3.9118
0.6010
per b:
0.0064e-005
0.0382e-005
0.0954e-005
0.1272e-005
0.0954e-005
0.0382e-005
0.0064e-005
Eseguendo il generatore di codice online (http://www.spiral.net/hardware/filter.html), ora ricevo il seguente errore (con bit frazionari come 8 e larghezza di bit di 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.
Forse i coefficienti b sono troppo piccoli, o forse il generatore di codice (http://www.spiral.net/hardware/filter.html) vuole che [b, a] in un altro formato?
AGGIORNARE:
Forse quello che devo fare è ridimensionare i coefficienti [b, a] del numero di bit frazionari per ottenere i coefficienti come numeri interi.
a .* 2^12
b .* 2^12
Tuttavia, penso ancora che i coefficienti b siano estremamente piccoli. Cosa sto facendo di sbagliato qui?
Forse un altro tipo di filtro (o metodo di progettazione del filtro) sarebbe più adatto? Qualcuno potrebbe dare un suggerimento?
AGGIORNAMENTO: Come suggerito da Jason R e Christopher Felton nei commenti qui sotto, un filtro SOS sarebbe più adatto. Ora ho scritto del codice Matlab per ottenere 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 matrice SOS che ottengo è:
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
È ancora possibile utilizzare lo strumento di generazione del codice Verilog (http://www.spiral.net/hardware/filter.html) per implementare questo filtro SOS o devo semplicemente scrivere Verilog a mano? È disponibile un buon riferimento?
Mi chiederei se sarebbe meglio usare un filtro FIR in questa situazione.
INOLTRE: I filtri ricorsivi IIR possono essere implementati usando la matematica intera esprimendo i coefficienti come frazioni. (Vedi l'eccellente libro di elaborazione del segnale DSP di Smith per ulteriori dettagli: http://www.dspguide.com/ch19/5.htm )
Il seguente programma Matlab converte i coefficienti del filtro Butterworth in parti frazionarie usando la funzione Matlab rat (). Quindi, come menzionato nei commenti, le sezioni del secondo ordine possono essere utilizzate per implementare numericamente il 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);