Saya telah merancang filter Butterworth low-pass yang sangat sederhana menggunakan Matlab. Cuplikan kode berikut menunjukkan apa yang telah saya lakukan.
fs = 2.1e6;
flow = 44 * 1000;
fNorm = flow / (fs / 2);
[b,a] = butter(10, fNorm, 'low');
Dalam [b, a] disimpan koefisien filter. Saya ingin mendapatkan [b, a] sebagai bilangan bulat sehingga saya bisa menggunakan generator kode HDL online untuk menghasilkan kode di Verilog.
Nilai Matlab [b, a] tampaknya terlalu kecil untuk digunakan dengan pembuat kode online (skrip Perl sisi server menolak untuk menghasilkan kode dengan koefisien), dan saya bertanya-tanya apakah mungkin untuk mendapatkan [b, a] dalam bentuk yang dapat digunakan sebagai input yang tepat.
Koefisien yang saya dapatkan di Matlab adalah:
1.0000
-9.1585
37.7780
-92.4225
148.5066
-163.7596
125.5009
-66.0030
22.7969
-4.6694
0.4307
Koefisien b yang saya dapatkan di Matlab adalah:
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
Menggunakan generator online, saya ingin mendesain filter dengan bitwidth 12-bit dan bentuk filter I atau II. Saya tidak tahu apa yang dimaksud dengan "bit pecahan" pada tautan di atas.
Menjalankan generator kode (http://www.spiral.net/hardware/filter.html) dengan koefisien [b, a] yang tercantum di atas, dengan bit fraksional yang ditetapkan pada 20 dan bitwidth 12, saya menerima run error berikut :
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.
Bagaimana saya bisa mengubah desain saya sehingga kesalahan ini tidak terjadi?
UPDATE: Menggunakan Matlab untuk menghasilkan filter Butterworth urutan ke-6, saya mendapatkan koefisien berikut:
Untuk sebuah:
1.0000
-5.4914
12.5848
-15.4051
10.6225
-3.9118
0.6010
untuk b:
0.0064e-005
0.0382e-005
0.0954e-005
0.1272e-005
0.0954e-005
0.0382e-005
0.0064e-005
Menjalankan generator kode online (http://www.spiral.net/hardware/filter.html), saya sekarang menerima kesalahan berikut (dengan bit fraksional 8 dan bitwidth 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.
Mungkin koefisien b terlalu kecil, atau mungkin pembuat kode (http://www.spiral.net/hardware/filter.html) menginginkan [b, a] dalam format lain?
MEMPERBARUI:
Mungkin yang perlu saya lakukan adalah skala koefisien [b, a] dengan jumlah bit fraksional untuk mendapatkan koefisien sebagai bilangan bulat.
a .* 2^12
b .* 2^12
Namun, saya masih berpikir bahwa koefisien b sangat kecil. Apa yang saya lakukan salah di sini?
Mungkin tipe filter lain (atau metode desain filter) akan lebih cocok? Adakah yang bisa memberikan saran?
UPDATE: Seperti yang disarankan oleh Jason R dan Christopher Felton dalam komentar di bawah ini, filter SOS akan lebih cocok. Saya sekarang telah menulis beberapa kode Matlab untuk mendapatkan filter 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);
Matriks SOS yang saya dapatkan adalah:
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
Apakah mungkin untuk tetap menggunakan alat penghasil kode Verilog (http://www.spiral.net/hardware/filter.html) untuk mengimplementasikan filter SOS ini, atau haruskah saya menulis Verilog dengan tangan? Apakah tersedia referensi yang bagus?
Saya akan bertanya-tanya apakah filter FIR akan lebih baik untuk digunakan dalam situasi ini.
MOREOVER: Filter IIR rekursif dapat diimplementasikan menggunakan matematika integer dengan menyatakan koefisien sebagai fraksi. (Lihat buku pemrosesan sinyal DSP Smith yang sangat baik untuk perincian lebih lanjut: http://www.dspguide.com/ch19/5.htm )
Program Matlab berikut mengubah koefisien filter Butterworth menjadi bagian fraksional menggunakan fungsi Matlab rat (). Kemudian seperti yang disebutkan dalam komentar, bagian urutan kedua dapat digunakan untuk mengimplementasikan filter secara numerik (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);