Matlab'da Butterworth filtresi tasarlamak ve çevrimiçi Verilog HDL kod üreticisi için tamsayı olarak filtre [ab] katsayıları elde etmek


15

Matlab kullanarak çok basit bir düşük geçişli Butterworth filtre tasarladım. Aşağıdaki kod snippet'i ne yaptığımı gösterir.

fs = 2.1e6;
flow = 44 * 1000;
fNorm =  flow / (fs / 2);
[b,a] = butter(10, fNorm, 'low');

[B, a] 'da filtre katsayıları saklanır. Verilog'da kod oluşturmak için bir çevrimiçi HDL kod üreteci kullanabilmeniz için tamsayı olarak [b, a] elde etmek istiyorum .

Matlab [b, a] değerleri çevrimiçi kod üreteci (sunucu tarafı Perl betiği katsayılarla kod üretmeyi reddediyor) ile kullanmak için çok küçük gibi görünüyor ve ben elde etmek mümkün olup olmadığını merak ediyorum [b, a] uygun girdi olarak kullanılabilecek bir formda.

Matlab'da aldığım katsayılar:

1.0000
-9.1585
37.7780
-92.4225
148.5066
-163.7596
125.5009
-66.0030
22.7969
-4.6694
0.4307

Matlab'da aldığım b katsayıları:

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

Online jeneratörü kullanarak, 12 bit bit genişliğine ve I veya II filtre formuna sahip bir filtre tasarlamak istiyorum. Yukarıdaki linkteki "kesirli bitler" ile ne kastedildiğini bilmiyorum.

Kod üretecini (http://www.spiral.net/hardware/filter.html) yukarıda listelenen [b, a] katsayılarıyla, kesirli bitler 20 olarak ayarlanmış ve 12 bit bitiyle çalıştırarak, aşağıdaki çalışma hatasını alıyorum :

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. 

Bu hatanın oluşmaması için tasarımımı nasıl değiştirebilirim?

GÜNCELLEME: 6. dereceden Butterworth filtresi oluşturmak için Matlab kullanarak aşağıdaki katsayıları elde ederim:

Bir için:

1.0000
-5.4914
12.5848
-15.4051
10.6225
-3.9118
0.6010 

b için:

0.0064e-005
0.0382e-005
0.0954e-005
0.1272e-005
0.0954e-005
0.0382e-005
0.0064e-005

Çevrimiçi kod oluşturucuyu (http://www.spiral.net/hardware/filter.html) çalıştırarak, şimdi aşağıdaki hatayı alıyorum (kesirli bitler 8 ve 20 bit genişliği ile):

./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.

Belki de b-katsayıları çok küçük veya kod üreticisi (http://www.spiral.net/hardware/filter.html) başka bir formatta [b, a] istiyor?

GÜNCELLEME:

Belki de yapmam gereken şey, katsayıları tamsayı olarak elde etmek için [b, a] katsayılarını kesirli bit sayısına göre ölçeklendirmektir.

a .* 2^12
b .* 2^12

Bununla birlikte, b katsayılarının son derece küçük olduğunu düşünüyorum. Burada neyi yanlış yapıyorum?

Belki başka bir filtre türü (veya filtre tasarım yöntemi) daha uygun olabilir? Birisi öneride bulunabilir mi?

GÜNCELLEME: Aşağıdaki yorumlarda Jason R ve Christopher Felton tarafından önerildiği gibi, bir SOS filtresi daha uygun olacaktır. Şimdi bir SOS filtresi elde etmek için bazı Matlab kodu yazdım.

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);

Aldığım SOS matrisi:

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

Bu SOS filtresini uygulamak için Verilog kod oluşturma aracını (http://www.spiral.net/hardware/filter.html) kullanmaya devam etmek mümkün mü yoksa Verilog'u el ile mi yazmalıyım? İyi bir referans var mı?

Bir FIR filtresinin bu durumda daha iyi kullanılıp kullanılmayacağını merak ediyorum.

DAHA FAZLA: Yinelemeli IIR filtreleri, katsayıları kesir olarak ifade ederek tamsayı matematik kullanılarak uygulanabilir. (Daha fazla bilgi için Smith'in mükemmel DSP sinyal işleme kitabına bakın: http://www.dspguide.com/ch19/5.htm )

Aşağıdaki Matlab programı, Matlab rat () işlevini kullanarak Butterworth filtre katsayılarını kesirli parçalara dönüştürür. Daha sonra yorumlarda belirtildiği gibi, filtreyi sayısal olarak uygulamak için ikinci dereceden bölümler kullanılabilir (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);

4
Bunun gibi yüksek dereceli IIR filtreleri genellikle ikinci dereceden bölümler kullanılarak uygulanır ; İstediğiniz filtreyi birden çok ikinci derece aşamasını basamaklandırarak elde edersiniz (istenen sipariş tekse tek bir birinci dereceden aşamayla). Genellikle üst düzey filtreyi doğrudan uygulamaktan daha sağlam bir uygulamadır.
Jason R

3
@JasonR'un önerdiği şeyi yapmazsanız çok büyük kelime boyutlarına sahip olursunuz. Bunun gibi filtreler, temel bir IIR yapısı ile uygulandığında tek hassas kayan noktada başarısız olabilir, SOS'a ihtiyacınız vardır.
Christopher Felton

@JasonR: Bunu önerdiğiniz için teşekkürler. Yukarıdaki cevaba göre güncelledim.
Nicholas Kinar

@ChristopherFelton: Bunu güçlendirmeye yardım ettiğin için teşekkür ederim.
Nicholas Kinar

Evet, yeni SOS matrisinizle siteden 3 filtre oluşturabilirsiniz. Veya kodumu burada kullanabilirsiniz . Web sitesi ile aynı şekilde çalışacaktır. Komut dosyasını SOS matrisi dışında memnuniyetle güncelleyeceğim.
Christopher Felton

Yanıtlar:


5

Tartışıldığı gibi, yüksek dereceli filtreyi basamaklı 2. dereceden filtrelere bölen bölümlerin toplamını kullanmak en iyisidir. Güncellenmiş sorunun SOS matrisi vardır. Kullanımı bu kodu ve bir örnek burada Python nesne bireysel bölümleri oluşturmak için kullanılabilir.

Matlab'da

save SOS

Python bölgesinde

import shutil
import numpy
from scipy.io import loadmat
from siir import SIIR

matfile = loadmat('SOS.mat')  
SOS = matfile['SOS']
b = numpy.zeros((3,3))
a = numpy.zeros((3,3))
section = [None for ii in range(3)]
for ii in xrange(3):
    b[ii] = SOS[ii,0:3]
    a[ii] = SOS[ii,3:6]

    section[ii] = SIIR(b=b[ii], a=a[ii], W=(24,0))
    section[ii].Convert()  # Create the Verilog for the section
    shutil.copyfile('siir_hdl.v', 'iir_sos_section%d.v'%(ii))

Sabit nokta hakkında ek bilgi burada bulunabilir


Tüm içgörülü bağlantılar ve Python kodu için çok teşekkür ederim; Umarım cevabınız (ve burada yayınlanan diğer cevaplar) diğerleri için iyi referanslar olarak hizmet eder. Buradaki tüm yanıtları kabul edilen olarak işaretleyebilsem.
Nicholas Kinar

1
Herhangi bir sorununuz varsa bana bildirin ve sizin için işe yaramazsa kodu güncelleyeceğim / düzelteceğim. Bir SOS matrisini doğrudan kabul etmek için (nispeten yakında doh) değiştireceğim.
Christopher Felton

1
Örneğinizden kendi sürümümü uygulamaya çalıştım. Benim sistemimde, "numpy import sıfırları" kullanmak ve loatmat loadmat () değiştirmek zorunda kaldı. Matlab tarafından verilen SOS matrisi ( mathworks.com/help/toolbox/signal/ref/ss2sos.html ) beklenen formatta mı? SOS matrisine erişmeye çalışırken şu hatayı alıyorum: Yorumlayıcı "b [ii] = SOS [0: 3, ii]" satırına ulaştığında "TypeError: unhashable type"
16'da Nicholas Kinar

1
Bu, SOS.mat dosyasının biçimine bağlıdır. Sadece >>> yazdırırsanız (matfile), yüklenen .mat dosyasındaki anahtarları gösterir. Scipy.io.loadmat her zaman sözlük olarak yüklenir (BOMK).
Christopher Felton

1
Evet, bu doğru, 0 çıkışı 1 ve benzeri girişlerdir. Genişlik kelimesine biraz düşünülmelidir. Varsayılan iki kullanımlık 24 bit kesirdir (0 tamsayı, 23 kesir, 1 işareti). Başlangıçta daha küçük bir kelime genişliği kullanmak istediğinize inanıyorum.
Christopher Felton

10

'Kesirli bitler', bir otobüsteki bir sayının kesirli kısmını temsil etmek için ayırdığınız bit sayısıdır (örneğin, 3.75'te .75).

Diyelim ki 4 bit genişliğinde bir dijital veri yolunuz var, hangi numarayı 1001temsil ediyor? Pozitif bir tamsayı olarak ele alırsanız '9' anlamına gelebilir (2 ^ 3 + 2 ^ 0 = 8 + 1 = 9). Veya ikinin tamamlayıcı gösteriminde -7 anlamına gelebilir: (-2 ^ 3 + 2 ^ 0 = -8 + 1 = -7).

İçlerinde bazı kesirleri olan sayılar, yani 'gerçek' sayılar ne olacak? Gerçek sayılar donanımda "sabit nokta" veya "kayan nokta" olarak temsil edilebilir. Bu filtre jeneratörleri sabit nokta kullanıyor gibi görünüyor.

Geri bizim 4 bit otobüs ( 1001). İkili bir noktayı tanıtalım 1.001. Bunun anlamı, tamsayılar oluşturmak için noktanın RHS'sindeki bitleri ve bir kesir oluşturmak için LHS'deki bitleri kullanmasıdır. Dijital veri yolu ile gösterilen sayı 1.0011.125'tir ( 1* 2 ^ 0 + 0* 2 ^ -1 + 0* 2 ^ -2 + 1* 2 ^ -3 = 1 + 0.125 = 1.125). Bu durumda, otobüsteki 4 bitten 3'ünü bir sayının kesirli kısmını temsil etmek için kullanıyoruz. Veya 3 kesirli bitimiz var.

Dolayısıyla, yukarıdaki gibi gerçek sayıların bir listesi varsa, şimdi bunları temsil etmek için kaç kesirli bite karar vermek zorundasınız. Ve işte denge: ne kadar çok kesirli bit kullanırsanız, istediğiniz sayıyı o kadar yakın temsil edebilirsiniz, ancak devrenizin daha büyük olması gerekir. Dahası, ne kadar az kesirli bit kullanırsanız, filtrenin gerçek frekans tepkisi başlangıçta tasarladığınızdan daha fazla sapacaktır!

Daha da kötüsü, Sonsuz Dürtü Tepki (IIR) filtresi oluşturmak istiyorsunuz. Yeterli kesirli ve tamsayı bitiniz yoksa bunlar aslında kararsız olabilir!


Bu anlayışlı cevabı verdiğiniz için teşekkür ederiz. Yukarıdaki küçük b katsayılarını kullanarak kod üretecini çalıştırmaya çalışıyorum ve hala bazı hatalar alıyorum. Jeneratörü doğru şekilde çalıştırmak için yapabileceğim bir şey önerebilir misiniz? Ne yaptığımı göstermek için yukarıdaki cevabı güncelleyeceğim.

10

Bu yüzden Marty bit sorusunu iyi halletti. Filtrenin kendisinde, matlab'dan kötü ölçeklendirilmiş katsayılar hakkında bir uyarı veya şikayet aldığını düşünüyorum? Filtreyi çizdiğimde, sclaby'den matlab değil ama muhtemelen çok benzer.

Tepki

Hangi geçiş bandında 100 dB aşağı! Bu nedenle, yine de uygulamanıza yardımcı olacak daha küçük bir sipariş filtresi istediğinizden emin olmak isteyebilirsiniz. 6. dereceden bir filtreye geldiğimde kötü katsayılarla ilgili şikayetler almayı bırakıyorum. Belki siparişi azaltmayı deneyin ve hala gereksinimlerinizi karşılayıp karşılamadığını görün.


Bunu önerdiğiniz için teşekkürler! Ben bir 6. dereceden filtre de aynı şekilde işe yarayacağını düşünüyorum. Matlab'ın fvtool'unu kullanarak, cevabın uygulamam için iyi olduğunu düşünüyorum. Şimdi yukarıdaki yanıtımı güncelledim. Ancak, Verilog HDL kod oluşturucu ( spiral.net/hardware/filter.html ) ile ilgili bir sorun devam ediyor . Belki de [b, a] 'yı başka bir formatta istiyor. Buna ek olarak, SciPy kullanımı için +1.
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.