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);
Réponses:
Comme nous l'avons vu, il est préférable d'utiliser la somme des sections, c'est-à-dire de diviser le filtre d'ordre supérieur en filtres de second ordre en cascade. La question mise à jour a la matrice SOS. En utilisant ce code et un exemple ici, l'objet Python peut être utilisé pour générer les sections individuelles.
En matlab
En Python
Des informations supplémentaires sur les virgules fixes sont disponibles ici
la source
Les «bits fractionnaires» sont le nombre de bits dans un bus que vous avez dédié pour représenter la partie fractionnaire d'un nombre (par exemple, le 0,75 en 3,75).
Disons que vous avez un bus numérique de 4 bits de large, quel nombre
1001
représente? Cela pourrait signifier «9» si vous le traitez comme un entier positif (2 ^ 3 + 2 ^ 0 = 8 + 1 = 9). Ou cela pourrait signifier -7 dans la notation du complément à deux: (-2 ^ 3 + 2 ^ 0 = -8 + 1 = -7).Qu'en est-il des nombres contenant des fractions, c'est-à-dire des nombres «réels»? Les nombres réels peuvent être représentés dans le matériel comme «virgule fixe» ou «virgule flottante». Il semble que ces générateurs de filtres utilisent des points fixes.
Retour à notre bus 4 bits (
1001
). Introduisons un point binaire pour que nous obtenions1.001
. Cela signifie que nous utilisions maintenant les bits sur le RHS du point pour construire des entiers, et les bits sur le LHS pour construire une fraction. Le nombre représenté par un bus numérique réglé sur1.001
est 1,125 (1
* 2 ^ 0 +0
* 2 ^ -1 +0
* 2 ^ -2 +1
* 2 ^ -3 = 1 + 0,125 = 1,125). Dans ce cas, sur les 4 bits du bus, nous en utilisons 3 pour représenter la partie fractionnaire d'un nombre. Ou, nous avons 3 bits fractionnaires.Donc, si vous avez une liste de nombres réels comme vous l'avez ci-dessus, vous devez maintenant décider combien de bits fractionnaires vous souhaitez les représenter. Et voici le compromis: plus vous utilisez de bits fractionnaires, plus vous pouvez représenter le nombre que vous voulez, mais plus votre circuit devra être grand. Et de plus, moins vous utilisez de bits fractionnaires, plus la réponse en fréquence réelle du filtre sera différente de celle que vous avez conçue au départ!
Et pour aggraver les choses, vous cherchez à créer un filtre de réponse impulsionnelle infinie (IIR). Ceux-ci peuvent en fait devenir instables si vous n'avez pas assez de bits fractionnaires et entiers!
la source
Alors Marty a bien pris soin de la question des bits. Sur le filtre lui-même, je pense que vous recevez probablement un avertissement ou une plainte de matlab au sujet de coefficients mal mis à l'échelle? Quand je trace le filtre, de scipy pas matlab mais c'est probablement très similaire.
Ce qui est de 100 dB vers le bas dans la bande passante! Donc, vous voudrez peut-être vous assurer que vous voulez un filtre d'ordre plus petit, ce qui vous aidera de toute façon à la mise en œuvre. Lorsque j'arrive à un filtre de 6e ordre, j'arrête de recevoir des plaintes concernant les mauvais coefficients. Essayez peut-être de réduire la commande et voyez si elle répond toujours à vos besoins.
la source