J'ai conçu un filtre Butterworth passe-bas très simple en utilisant Matlab. L'extrait de code suivant montre ce que j'ai fait.
fs = 2.1e6;
flow = 44 * 1000;
fNorm = flow / (fs / 2);
[b,a] = butter(10, fNorm, 'low');
Dans [b, a] sont stockés les coefficients de filtre. Je voudrais obtenir [b, a] sous forme d'entiers afin de pouvoir utiliser un générateur de code HDL en ligne pour générer du code dans Verilog.
Les valeurs de Matlab [b, a] semblent trop petites pour être utilisées avec le générateur de code en ligne (le script Perl côté serveur refuse de générer du code avec les coefficients), et je me demande s'il serait possible d'obtenir [b, a] sous une forme qui peut être utilisée comme entrée appropriée.
Les coefficients a que j'obtiens dans Matlab sont:
1.0000
-9.1585
37.7780
-92.4225
148.5066
-163.7596
125.5009
-66.0030
22.7969
-4.6694
0.4307
Les coefficients b que j'obtiens dans Matlab sont:
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
En utilisant le générateur en ligne, je voudrais concevoir un filtre avec une largeur de bit de 12 bits et une forme de filtre I ou II. Je ne sais pas ce que l'on entend par les "bits fractionnaires" sur le lien ci-dessus.
En exécutant le générateur de code (http://www.spiral.net/hardware/filter.html) avec les coefficients [b, a] répertoriés ci-dessus, avec des bits fractionnaires définis à 20 et une largeur de bit de 12, je reçois l'erreur d'exécution suivante :
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.
Comment puis-je modifier ma conception afin que cette erreur ne se produise pas?
MISE À JOUR: En utilisant Matlab pour générer un filtre Butterworth de 6e ordre, j'obtiens les coefficients suivants:
Pour un:
1.0000
-5.4914
12.5848
-15.4051
10.6225
-3.9118
0.6010
pour b:
0.0064e-005
0.0382e-005
0.0954e-005
0.1272e-005
0.0954e-005
0.0382e-005
0.0064e-005
En exécutant le générateur de code en ligne (http://www.spiral.net/hardware/filter.html), je reçois maintenant l'erreur suivante (avec des bits fractionnaires à 8 et une largeur de bit 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.
Peut-être que les coefficients b sont trop petits, ou peut-être que le générateur de code (http://www.spiral.net/hardware/filter.html) veut le [b, a] dans un autre format?
MISE À JOUR:
Peut-être que je dois faire est de mettre à l'échelle les coefficients [b, a] par le nombre de bits fractionnaires pour obtenir les coefficients sous forme d'entiers.
a .* 2^12
b .* 2^12
Cependant, je pense toujours que les coefficients b sont extrêmement faibles. Qu'est-ce que je fais mal ici?
Peut-être qu'un autre type de filtre (ou méthode de conception de filtre) conviendrait mieux? Quelqu'un pourrait-il faire une suggestion?
MISE À JOUR: Comme suggéré par Jason R et Christopher Felton dans les commentaires ci-dessous, un filtre SOS serait plus approprié. J'ai maintenant écrit du code Matlab pour obtenir un filtre 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 que je reçois est:
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
Est-il possible d'utiliser toujours l'outil de génération de code Verilog (http://www.spiral.net/hardware/filter.html) pour implémenter ce filtre SOS, ou dois-je simplement écrire le Verilog à la main? Une bonne référence est-elle disponible?
Je me demande s'il serait préférable d'utiliser un filtre FIR dans cette situation.
DE PLUS: Les filtres IIR récursifs peuvent être implémentés en utilisant des mathématiques entières en exprimant les coefficients sous forme de fractions. (Voir l'excellent livre de Smith sur le traitement du signal DSP pour plus de détails: http://www.dspguide.com/ch19/5.htm )
Le programme Matlab suivant convertit les coefficients du filtre de Butterworth en parties fractionnaires à l'aide de la fonction Matlab rat (). Ensuite, comme mentionné dans les commentaires, des sections de second ordre peuvent être utilisées pour implémenter numériquement le filtre (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);