Разработка фильтра Баттерворта в 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, а] в форме, которая может быть использована в качестве правильного ввода.

Коэффициенты, которые я получаю в 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 

для б:

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 лучше использовать в этой ситуации.

Еще: рекурсивные БИХ-фильтры могут быть реализованы с использованием целочисленной математики, выражая коэффициенты в виде дробей. (См. Превосходную книгу Смита по обработке сигналов DSP для получения дополнительной информации: 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
Подобные фильтры БИХ высшего порядка обычно реализуются с использованием секций второго порядка ; Вы получаете нужный фильтр, каскадируя несколько ступеней второго порядка (с одним каскадом первого порядка, если желаемый порядок нечетен). Как правило, это более надежная реализация, чем непосредственная реализация фильтра высшего порядка.
Джейсон Р
3
Если вы не сделаете то, что предлагает @JasonR, у вас будут очень большие размеры слов. Подобные фильтры могут давать сбой с плавающей запятой одинарной точности при реализации с базовой структурой IIR, вам нужна SOS.
Кристофер Фелтон
@JasonR: Спасибо, что предложили это. Я обновил ответ выше.
Николас Кинар
@ChristopherFelton: Спасибо за помощь в укреплении этого.
Николас Кинар
Да, с вашей новой матрицей SOS вы можете создать 3 фильтра с сайта. Или вы можете использовать мой код здесь . Он будет работать так же, как веб-сайт. Я с удовольствием обновлю скрипт, кроме SOS матрицы.
Кристофер Фелтон

Ответы:

5

Как уже говорилось, лучше всего использовать сумму сечений, то есть разбить фильтр высшего порядка на каскадные фильтры 2-го порядка. Обновленный вопрос имеет матрицу SOS. Используя этот код и пример здесь, объект Python может использоваться для генерации отдельных разделов.

В матлаб

save SOS

В питоне

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
Я попытался реализовать свою собственную версию из вашего примера. В моей системе мне пришлось использовать «из нулевых импортных нулей» и изменить loatmat на 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

«Дробные биты» - это количество битов в шине, выделенных вами для представления дробной части числа (например, 0,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). Они могут на самом деле работать нестабильно, если у вас недостаточно дробных и целочисленных битов!

Marty
источник
Спасибо за предоставление этого проницательного ответа. Я пытался запустить генератор кода, используя малые коэффициенты b выше, и все еще получаю некоторые ошибки. Не могли бы вы предложить что-нибудь, что я мог бы сделать, чтобы правильно запустить генератор? Я обновлю ответ выше, чтобы показать, что я сделал.
10

Так что Марти хорошо позаботился о битах. Что касается самого фильтра, я думаю, что вы, вероятно, получаете предупреждение или жалобу от Matlab по поводу плохо масштабированных коэффициентов? Когда я строю фильтр, от scipy не matlab, но он, вероятно, очень похож.

отклик

Что на 100 дБ ниже в полосе пропускания! Итак, вы можете убедиться, что вам нужен фильтр меньшего порядка, который в любом случае поможет вам в реализации. Когда я добираюсь до фильтра 6-го порядка, я прекращаю получать жалобы на плохие коэффициенты. Возможно, попробуйте уменьшить заказ и посмотреть, соответствует ли он вашим требованиям.

Макдафф
источник
Спасибо, что предложили это! Я думаю, что фильтр 6-го порядка будет работать так же хорошо. Используя matlab's fvtool, я думаю, что ответ хорош для моего приложения. Я сейчас обновил свой ответ выше. Однако что-то не так с генератором кода Verilog HDL ( spiral.net/hardware/filter.html ). Возможно, он хочет [b, a] в другом формате. Кроме того, +1 за использование SciPy.