Российский производитель и разработчик сертифицированного измерительного оборудования с 1987 года

фильтрация

 

1. Назначение

Плагин позволяет осуществлять фильтрацию в реальном масштабе времени одновременно по нескольким каналам с децимацией.
  • Фильтрация до 8-ми аналоговых каналов.
  • Выбор методики расчета БИХ фильтра (Бесселя, Баттерворта, Чебышева, Инверсный Чебышева или Эллиптический)
  • Выбор типа фильтра (фильтр нижних частот, фильтр верхних частот, полосовой фильтр, режекторный фильтр).
  • Выбор порядка фильтра.
  • Установка коэффициента децимации позволяет решить довольно типовую задачу, когда требуется осуществлять низкочастотный ввод на уровне долей герца при помощи модуля АЦП, позволяющего оцифровывать данные на больших частотах. При помощи описываемого плагина можно установить сбор с АЦП, например, на 1 кГц, включить ФНЧ, сузив полосу от 0 до 1-2 Гц и, выбрав коэффициент децимации 1000, получить итоговый поток отфильтрованных данных 1 Гц.

2. Исходный текст плагина

/* Плагин, осуществляющий фильтрацию и децимацию в реальном времени до 8-ми каналов На вход плагину поступают до 8-ми входных каналов. Среда разработки LabWindows CVI 9.0. */ #include #include #include #include #include "..\\include\\plugin.h" // индексы параметров #define FILTER_TYPE_INDEX 0 // тип фильтра (ФНЧ, ФВЧ и т.д.) #define FILTER_MODE_INDEX 1 // метод рассчета (Бесселя, Баттерворта и т.д.) #define FILTER_ORDER_INDEX 2 // порядок фильтра #define FILTER_LOW_CUTOFF_INDEX 3 // нижняя частота среза #define FILTER_HIGH_CUTOFF_INDEX 4 // верхняя частота среза #define FILTER_DECIMATION 5 // коэффициент децимации static struct PluginDataInfoStr DataInfo; // структура с настройками АЦП static int device_index=0; // работаем с первым модулем АЦП static int decimation_index[8]; // массив с индексами для децимации double *x_buffer, *y_buffer; // временные буфера для расчетов static IIRFilterPtr filters[8]; // БИХ фильтры // ********************************************************************************************************* // собственно главная функция с фильтрацией void __stdcall PluginDataExchange(struct PluginDataStr *data_str) { int i, adc_channel, index1, adc_nch, n, ch, plugin_nch, n1, decimation_step; double *ptr, *ptr1; decimation_step=DataInfo.parameters_int[FILTER_DECIMATION]; // для ускорения запомним коэффициент децимации for(ch=n1=0, adc_nch=DataInfo.nch[device_index], plugin_nch=DataInfo.plugin_nch; ch < plugin_nch; ch++) // цикл по числу каналов { // переложим данные с обрабатываемого канала в буфер n=data_str->n; // сколько кадров пришло index1=DataInfo.chan_kadr_offset[0][DataInfo.adc_channels[ch]]; // смещение в кадре обрабатываемого канала ptr1=&data_str->data_to_plugin[index1]; // указател на первый отсчет с канала for(i=0, ptr=x_buffer; i < n; i++, ptr1 += adc_nch) *ptr++=*ptr1; // переложим данные в x_buffer IIRCascadeFiltering (x_buffer, n, filters[ch], y_buffer); // прогоним данные через фильтр // сохранение отфильтрованных данных с децимацией ptr=&data_str->data_from_plugin[ch]; // куда положим результат for(i=n1=0, ptr1=y_buffer; i < n; i++) // цикл по числу кадров { if(!decimation_index[ch]) { n1++; *ptr=*ptr1++; ptr+=plugin_nch;}// сохранение отсчета decimation_index[ch]++; // обработка индекса децимации decimation_index[ch] %= decimation_step; } } data_str->n_from_plugin=n1; // сколько создали кадров данных } //********************************************************************************************************* // информационная функция void __stdcall PluginInfo(struct PluginInfoStr *p_info) { int i; char *filter_names[]={"Бесселя", "Баттерворта", "Чебышева", "Инверсный Чебышева", "Эллиптический"}; char *type_names[]={"Фильтр нижних частот", "Фильтр верхних частот", "Полосовой фильтр", "Обратный полосовой фильтр"}; // установим общие переменные strcpy(p_info->name, "Digfilters"); // название плагина p_info->version=0x00010000; // версия 1.0 p_info->lgraph_version=0x221; // плагин разработан для версии 2.33 p_info->max_nch=8; // максимальное число входных каналов 2 p_info->min_nch=1; // минимальное число входных каналов 2 // установим названия входных каналов for(i=0; i < 8; i++) sprintf(p_info->channel_names[i], "Канал %c", 'A'+i); p_info->parameters=6; // всего 6 параметров // Параметр выбора типа фильтра (ФНЧ, ФВЧ и т.п.) strcpy(p_info->parameters_names[FILTER_TYPE_INDEX], "Фильтр"); p_info->parameters_type[FILTER_TYPE_INDEX]=L_TYPE_RING; for(i=0; i < 4; i++) strncpy(p_info->ring_names[FILTER_TYPE_INDEX][i], type_names[i], 63); // Параметр рассчета фильтра strcpy(p_info->parameters_names[FILTER_MODE_INDEX], "Тип фильтра"); p_info->parameters_type[FILTER_MODE_INDEX]=L_TYPE_RING; for(i=0; i < 5; i++) strncpy(p_info->ring_names[FILTER_MODE_INDEX][i], filter_names[i], 63); // Параметр порядок фильтра strcpy(p_info->parameters_names[FILTER_ORDER_INDEX], "Порядок фильтра (от 3 до 10)"); p_info->parameters_type[FILTER_ORDER_INDEX]=L_TYPE_INT; p_info->default_parameters_int[FILTER_ORDER_INDEX]=5; p_info->min_parameters_int[FILTER_ORDER_INDEX]=3; p_info->max_parameters_int[FILTER_ORDER_INDEX]=10; // Нижняя полоса среза strcpy(p_info->parameters_names[FILTER_LOW_CUTOFF_INDEX], "Нижняя полоса среза, Гц"); p_info->parameters_type[FILTER_LOW_CUTOFF_INDEX]=L_TYPE_DOUBLE; p_info->default_parameters_dbl[FILTER_LOW_CUTOFF_INDEX]=100; // Верхняя полоса среза strcpy(p_info->parameters_names[FILTER_HIGH_CUTOFF_INDEX], "Верхняя полоса среза, Гц"); p_info->parameters_type[FILTER_HIGH_CUTOFF_INDEX]=L_TYPE_DOUBLE; p_info->default_parameters_dbl[FILTER_HIGH_CUTOFF_INDEX]=120; // Коэффициент децимации strcpy(p_info->parameters_names[FILTER_DECIMATION], "Коэффициент децимации (1...2048)"); p_info->parameters_type[FILTER_DECIMATION]=L_TYPE_INT; p_info->default_parameters_int[FILTER_DECIMATION]=1; p_info->min_parameters_int[FILTER_DECIMATION]=1; p_info->max_parameters_int[FILTER_DECIMATION]=2048; } // ********************************************************************************************************* // обработка данных о параметрах модулей АЦП от LGraph void __stdcall PluginDataInfo(struct PluginDataInfoStr *d_info) { int i, r; if(!d_info->devices) { strcpy(d_info->error, "Нет модуля АЦП"); return; } if(!d_info->nch[device_index]) { strcpy(d_info->error, "Не выбраны каналы АЦП"); return; } d_info->input_kadrs_min=10; // обрабатываем за один раз не менее 10 кадров d_info->input_kadrs_max=1000000; // и не более 1e6 кадров d_info->plugin_nch=d_info->adc_nch; // число расчетных каналов, порождаемые плагином // проверим коэффициент децимации if(d_info->parameters_int[FILTER_DECIMATION] < 1) d_info->parameters_int[FILTER_DECIMATION]=1; if(d_info->parameters_int[FILTER_DECIMATION] > 2048) d_info->parameters_int[FILTER_DECIMATION]=2048; d_info->plugin_channel_rate=d_info->rate[device_index]/d_info->parameters_int[FILTER_DECIMATION]; // вычислим частоту for(i=0; i < d_info->plugin_nch; i++) sprintf(d_info->plugin_channel_names[i], "Канал %c", 'A'+i); DataInfo=*d_info; // Проверим установленные частоты среза на допустимые значения. if(d_info->parameters_dbl[FILTER_LOW_CUTOFF_INDEX] == 0) { strcpy(d_info->error, "Неверная нижняя частота среза. Должна быть больше 0."); return; } if(d_info->parameters_dbl[FILTER_LOW_CUTOFF_INDEX] > DataInfo.rate[device_index]/2) { strcpy(d_info->error, "Неверная нижняя частота среза. Должна быть меньше половины частоты дискретизации АЦП"); return; } if(d_info->parameters_dbl[FILTER_HIGH_CUTOFF_INDEX] > DataInfo.rate[device_index]/2) { strcpy(d_info->error, "Неверная верхняя частота среза. Должна быть меньше половины частоты дискретизации АЦП"); return; } if(d_info->parameters_dbl[FILTER_LOW_CUTOFF_INDEX] >= d_info->parameters_dbl[FILTER_HIGH_CUTOFF_INDEX]) { strcpy(d_info->error, "Неверная нижняя частота среза. Должна быть меньше верхней частоты среза."); return; } // Расчитаем и подготовим фильтры for(i=0; i < DataInfo.plugin_nch; i++) { if(filters[i] != NULL) FreeIIRFilterPtr (filters[i]); // сотрем старые коэффициенты // выделим память под новые коэффициенты filters[i]=AllocIIRFilterPtr (DataInfo.parameters_int[FILTER_TYPE_INDEX], DataInfo.parameters_int[FILTER_ORDER_INDEX]); if(filters[i] == NULL) { strcpy(d_info->error, "Ошибка создания фильтра."); return; } switch(DataInfo.parameters_int[FILTER_MODE_INDEX]) // расчитаем коэффициенты { case 0: // Бесселя r=Bessel_CascadeCoef (DataInfo.rate[device_index], DataInfo.parameters_dbl[FILTER_LOW_CUTOFF_INDEX], DataInfo.parameters_dbl[FILTER_HIGH_CUTOFF_INDEX], filters[i]); break; case 1: // Баттерворта r=Bw_CascadeCoef (DataInfo.rate[device_index], DataInfo.parameters_dbl[FILTER_LOW_CUTOFF_INDEX], DataInfo.parameters_dbl[FILTER_HIGH_CUTOFF_INDEX], filters[i]); break; case 2: // Чебышева r=Ch_CascadeCoef (DataInfo.rate[device_index], DataInfo.parameters_dbl[FILTER_LOW_CUTOFF_INDEX], DataInfo.parameters_dbl[FILTER_HIGH_CUTOFF_INDEX], 0.1, filters[i]); break; case 3: // Инверсный Чебышева r=InvCh_CascadeCoef (DataInfo.rate[device_index], DataInfo.parameters_dbl[FILTER_LOW_CUTOFF_INDEX], DataInfo.parameters_dbl[FILTER_HIGH_CUTOFF_INDEX], 60.0, filters[i]); break; case 4: // Эллиптический r=Elp_CascadeCoef (DataInfo.rate[device_index], DataInfo.parameters_dbl[FILTER_LOW_CUTOFF_INDEX], DataInfo.parameters_dbl[FILTER_HIGH_CUTOFF_INDEX], 1.0, 60.0, filters[i]); break; } if(r) { sprintf(d_info->error, "Ошибка создания фильтра. Код ошибки %d.", r); return; } } // выделим память под временные буфера if(x_buffer == NULL) x_buffer=malloc(sizeof(double)*1000000); // выделим память под временный буфер if(x_buffer == NULL) { strcpy(d_info->error, "Не хватает памяти"); return; } if(y_buffer == NULL) y_buffer=malloc(sizeof(double)*1000000); // выделим память под временный буфер if(y_buffer == NULL) { strcpy(d_info->error, "Не хватает памяти"); return; } for(i=0; i < 8; i++) decimation_index[i]=0; } // визуальные элементы у плагина отсутствуют void __stdcall PluginVisualSetting(struct PluginVisualMainStr *main_visual_settings, struct PluginVisualStr p_visual[]){} /* Функция сообщает плагину, что начался сбор данных */ void __stdcall PluginStartInput(struct PluginDataStr *data_str) { int i; for(i=0; i < DataInfo.plugin_nch; i++) { decimation_index[i]=0; ResetIIRFilter (filters[i]); } } /* Функция сообщает плагину, что закончился сбор данных */ void __stdcall PluginStopInput(struct PluginDataStr *data_str){} //********************************************************************************************************* // Функция вызываемая при загрузке - выгрузке DLL плагина int __stdcall DllMain (HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) { int i; switch (fdwReason) { case DLL_PROCESS_ATTACH: if (InitCVIRTE (hinstDLL, 0, 0) == 0) return 0; break; case DLL_PROCESS_DETACH: for(i=0; i < 8; i++) if(filters[i] != NULL) FreeIIRFilterPtr (filters[i]); if(x_buffer != NULL) free(x_buffer); if(y_buffer != NULL) free(y_buffer); if (!CVIRTEHasBeenDetached ()) CloseCVIRTE (); break; } return 1; }

Контакты

Адрес: 117105, Москва, Варшавское шоссе, д. 5, корп. 4

Многоканальный телефон:
+7 (495) 785-95-25

Отдел продаж: sale@lcard.ru
Техническая поддержка: support@lcard.ru

Время работы: с 9-00 до 19-00 мск