Розробка фільтра Баттерворта в Matlab та отримання коефіцієнтів фільтра [ab] як цілих чисел для онлайн-генератора коду Verilog HDL


15

Я створив дуже простий фільтр Баттерворта з низькою прохідністю за допомогою Matlab. Наступний фрагмент коду демонструє те, що я зробив.

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

У [b, a] зберігаються коефіцієнти фільтра. Я хотів би отримати [b, a] цілі числа, щоб я міг використовувати онлайн-генератор коду HDL для генерації коду у Verilog.

Значення Matlab [b, a] здаються занадто малими для використання з онлайн-генератором коду (сценарій Perl на стороні сервера відмовляється генерувати код з коефіцієнтами), і мені цікаво, чи можна було б отримати [b, a] у формі, яка може бути використана як власне введення.

Коефіцієнти, які я отримую в Matlab:

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

Коефіцієнти b, які я отримую в Matlab:

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

Використовуючи онлайн-генератор, я хотів би розробити фільтр з 12-бітовою бітовою шириною та формою I або II фільтра. Я не знаю, що мається на увазі під "дробовими бітами" за вищенаведеним посиланням.

Запускаючи генератор коду (http://www.spiral.net/hardware/filter.html) з переліченими вище коефіцієнтами [b, a], з дробовими бітами, встановленими на 20, та бітовою шириною 12, я отримую таку помилку запуску :

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. 

Як я можу змінити дизайн, щоб ця помилка не сталася?

ОНОВЛЕННЯ: Використовуючи Matlab для створення фільтра Баттерворта 6-го порядку, я отримую такі коефіцієнти:

Для:

1.0000
-5.4914
12.5848
-15.4051
10.6225
-3.9118
0.6010 

для b:

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

Запускаючи онлайн-генератор коду (http://www.spiral.net/hardware/filter.html), я отримую таку помилку (з дробовими бітами як 8 та пропускною здатністю 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.

Можливо, коефіцієнти b занадто малі, або, можливо, генератор коду (http://www.spiral.net/hardware/filter.html) хоче [b, a] в іншому форматі?

ОНОВЛЕННЯ:

Можливо, мені потрібно зробити масштаб коефіцієнтів [b, a] за кількістю дробових розрядів, щоб отримати коефіцієнти як цілі числа.

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

Однак я все ще думаю, що коефіцієнти b надзвичайно малі. Що я тут роблю неправильно?

Можливо, інший тип фільтра (або метод проектування фільтра) буде більш підходящим? Хтось може зробити пропозицію?

ОНОВЛЕННЯ: Як запропоновано Джейсоном Р та Крістофером Фелтоном у коментарях нижче, SOS фільтр був би більш підходящим. Зараз я написав код Matlab, щоб отримати фільтр 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);

Матриця SOS, яку я отримую, це:

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

Чи можливо все ж використовувати інструмент генерації коду Verilog (http://www.spiral.net/hardware/filter.html) для реалізації цього фільтра SOS, або я просто мушу написати Verilog вручну? Чи є хороша довідка?

Мені було б цікаво, чи краще було б використовувати фільтр FIR у цій ситуації.

ПОВІДОМЛЕННЯ: Рекурсивні фільтри IIR можуть бути реалізовані за допомогою цілочислової математики шляхом вираження коефіцієнтів у вигляді дробів. (Докладніші відомості див. У чудовій книзі обробки сигналу СМП від Сміта: http://www.dspguide.com/ch19/5.htm )

Наступна програма Matlab перетворює коефіцієнти фільтра Баттерворта в дробові частини за допомогою функції Matlab rat (). Тоді, як зазначено в коментарях, розділи другого порядку можна використовувати для чисельної реалізації фільтра (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
Фільтри IIR вищого порядку, подібні до цього, зазвичай реалізуються за допомогою розділів другого порядку ; Ви отримуєте потрібний фільтр шляхом каскадування декількох етапів другого порядку (з одним етапом першого порядку, якщо бажане замовлення непарне). Зазвичай це більш надійна реалізація, ніж безпосередньо реалізація фільтра вищого порядку.
Джейсон R

3
Якщо ви не зробите те, що пропонує @JasonR, у вас будуть дуже великі розміри слів. Такі фільтри можуть вийти з ладу з плаваючою точкою з однією точністю, коли реалізовано з базовою структурою IIR, вам потрібен SOS.
Крістофер Фелтон

@JasonR: Дякую, що ви запропонували це. Я оновив відповідь вище.
Ніколас Кінар

@ChristopherFelton: Дякую за те, що допомогли підкріпити це.
Ніколас Кінар

Так, за допомогою нової матриці SOS ви можете створити 3 фільтри з сайту. Або ви можете використовувати мій код тут . Він працюватиме так само, як веб-сайт. Я із задоволенням оновлю сценарій, окрім матриці SOS.
Крістофер Фелтон

Відповіді:


5

Як обговорювалося, найкраще використовувати суму розділів, тобто розбити фільтр вищого порядку на каскадні фільтри 2-го порядку. Оновлене запитання має матрицю SOS. Використовуючи цей код і приклад тут об’єкт Python може використовуватися для генерації окремих розділів.

В матлабі

save SOS

У Python

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

Додаткову інформацію про фіксовану точку можна знайти тут


Дякую вам за всі проникливі посилання та код Python; Я сподіваюся, що ваша відповідь (та інші відповіді, розміщені тут) слугуватимуть гарними посиланнями для багатьох інших. Я хотів би, щоб я міг позначити всі відповіді тут як прийняті.
Ніколас Кінар

1
Якщо у вас є якісь проблеми, повідомте мене, і я оновлю / виправлюю код, якщо він не працює для вас. Я його модифікую (порівняно скоро, так), щоб безпосередньо прийняти матрицю SOS.
Крістофер Фелтон

1
Я спробував реалізувати власну версію з вашого прикладу. У моїй системі мені довелося використовувати "з нульового імпорту нулів" і змінити ломатмат на loadmat (). Чи відповідає матриця SOS Matlab ( mathworks.com/help/toolbox/signal/ref/ss2sos.html ) у такому ж форматі, як очікувалося? Під час спроби отримати доступ до матриці SOS я отримую таку помилку: "TypeError: unhashable type", коли інтерпретатор досягає рядка "b [ii] = SOS [0: 3, ii]"
Ніколас Кінар

1
Це залежало б від формату файлу SOS.mat. Якщо ви просто >>> роздрукуєте (matfile), він покаже вам ключі в завантаженому файлі .mat. Scipy.io.loadmat завжди завантажується як словник (BOMK).
Крістофер Фелтон

1
Так, це правильно, вихід 0 - це вхід до 1 і так далі. Трохи слід задуматися про ширину слова. За замовчуванням є два використання 24-бітного дробу (0 ціле число, 23 дріб, 1 знак). Я вважаю, ви спочатку хотіли використовувати меншу ширину слова.
Крістофер Фелтон

10

"Дробові біти" - це кількість бітів в шині, яку ви виділили для представлення дробової частини числа (наприклад, .75 в 3.75).

Скажіть, у вас цифрова шина шириною 4 біти, яке число 1001представляє? Це може означати "9", якщо ви трактуєте це як натуральне ціле (2 ^ 3 + 2 ^ 0 = 8 + 1 = 9). Або це може означати -7 у позначенні доповнення двох: (-2 ^ 3 + 2 ^ 0 = -8 + 1 = -7).

Що з числами з деякими дробами, тобто "реальними" числами? Реальні цифри можуть бути представлені апаратно як "фіксована точка" або "плаваюча точка". Схоже, ці генератори фільтрів використовують фіксовану точку.

Назад до нашої 4-бітної шини ( 1001). Давайте введемо бінарний пункт, щоб ми отримали 1.001. Це означає, що зараз використовували біти на RHS точки для побудови цілих чисел, а біти на LHS для створення дробу. Число, представлене цифровою шиною, встановлене на 1.0011,125 ( 1* 2 ^ 0 + 0* 2 ^ -1 + 0* 2 ^ -2 +1 * 2 ^ -3 = 1 + 0,125 = 1,125). У цьому випадку з 4 біт шини ми використовуємо 3 з них для представлення дробової частини числа. Або у нас є 3 дробових біта.

Отже, якщо у вас є список реальних чисел, таких як у вас вище, тепер ви повинні вирішити, скільки дробових бітів ви хочете їх представляти. І ось компроміс: чим більше дробових бітів ви використовуєте, тим ближче ви можете представляти потрібне вам число, але чим більша буде ваша схема. І що більше, чим менше дробових бітів ви використовуєте, тим далі фактична частотна характеристика фільтра буде відхилятися від тієї, яку ви спроектували на старті!

І що ще гірше, ви хочете створити фільтр нескінченного імпульсного реагування (IIR). Вони насправді можуть стати нестабільними, якщо у вас недостатньо дробових і цілих бітів!


Дякуємо, що надали цю проникливу відповідь. Я намагався запустити генератор коду, використовуючи малі b коефіцієнти, наведені вище, і я все одно отримую деякі помилки. Чи можете ви запропонувати щось, що я міг би зробити, щоб правильно запустити генератор? Я оновлю відповідь вище, щоб показати, що я зробив.

10

Тож Марті добре вирішив питання про біт. Щодо самого фільтра, я думаю, ви, ймовірно, отримуєте попередження або скаргу від matlab щодо погано масштабованих коефіцієнтів? Коли я будую фільтр, від scipy не matlab, але це, ймовірно, дуже схоже.

Відповідь

Що на 100 дБ вниз на пропускній смузі! Отже, ви можете переконатися, що вам потрібен фільтр менших замовлень, який допоможе вам у впровадженні. Коли я дістаюсь до фільтра 6-го порядку, я перестаю отримувати скарги на погані коефіцієнти. Можливо, спробуйте зменшити замовлення і побачити, чи воно все ще відповідає вашим вимогам.


Дякуємо, що запропонували це! Я думаю, що фільтр 6-го порядку працював би так само добре. Використовуючи fvtool матлаба, я вважаю, що відповідь хороша для моєї програми. Зараз я оновив свою відповідь вище. Однак у генераторі коду Verilog HDL ( spiral.net/hardware/filter.html ) все ще не так . Можливо, він хоче [b, a] в іншому форматі. Крім того, +1 для використання SciPy.
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.