фильтрация

 

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

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

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

/* Плагин, осуществляющий фильтрацию и децимацию в реальном времени до 8-ми каналов На вход плагину поступают до 8-ми входных каналов. Среда разработки LabWindows CVI 9.0. */ #include #include #include #include #include "..\\include\\plugin.h" // индексы параметров #define FILTER_CHANNEL_TYPE0 0 #define FILTER_CHANNEL_TYPE1 1 #define FILTER_CHANNEL_TYPE2 2 #define FILTER_CHANNEL_TYPE3 3 #define FILTER_CHANNEL_TYPE4 4 #define FILTER_CHANNEL_TYPE5 5 #define FILTER_CHANNEL_TYPE6 6 #define FILTER_CHANNEL_TYPE7 7 #define FILTER_MODE_INDEX 8 // метод рассчета (Бесселя, Баттерворта и т.д.) #define FILTER_ORDER_INDEX 9 // порядок фильтра #define FILTER_DECIMATION 10 // коэффициент децимации #define FILTERA1_TYPE_INDEX 11 // тип фильтра (ФНЧ, ФВЧ и т.д.) #define FILTERA1_LOW_CUTOFF_INDEX 12 // нижняя частота среза #define FILTERA1_HIGH_CUTOFF_INDEX 13 // верхняя частота среза #define FILTERA2_TYPE_INDEX 14 // тип фильтра (ФНЧ, ФВЧ и т.д.) #define FILTERA2_LOW_CUTOFF_INDEX 15 // нижняя частота среза #define FILTERA2_HIGH_CUTOFF_INDEX 16 // верхняя частота среза #define FILTERB1_TYPE_INDEX 17 // тип фильтра (ФНЧ, ФВЧ и т.д.) #define FILTERB1_LOW_CUTOFF_INDEX 18 // нижняя частота среза #define FILTERB1_HIGH_CUTOFF_INDEX 19 // верхняя частота среза #define FILTERB2_TYPE_INDEX 20 // тип фильтра (ФНЧ, ФВЧ и т.д.) #define FILTERB2_LOW_CUTOFF_INDEX 21 // нижняя частота среза #define FILTERB2_HIGH_CUTOFF_INDEX 22 // верхняя частота среза #define FILTERC1_TYPE_INDEX 23 // тип фильтра (ФНЧ, ФВЧ и т.д.) #define FILTERC1_LOW_CUTOFF_INDEX 24 // нижняя частота среза #define FILTERC1_HIGH_CUTOFF_INDEX 25 // верхняя частота среза #define FILTERC2_TYPE_INDEX 26 // тип фильтра (ФНЧ, ФВЧ и т.д.) #define FILTERC2_LOW_CUTOFF_INDEX 27 // нижняя частота среза #define FILTERC2_HIGH_CUTOFF_INDEX 28 // верхняя частота среза static struct PluginDataInfoStr DataInfo; // структура с настройками АЦП static int device_index=0; // работаем с первым модулем АЦП static int decimation_index[8]; // массив с индексами для децимации double *x_buffer, *y_buffer; // временные буфера для расчетов static IIRFilterPtr filters[8][2]; // БИХ фильтры static unsigned char filters_on[8][2]; // ********************************************************************************************************* // собственно главная функция с фильтрацией 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 if(filters_on[ch][0]) { IIRCascadeFiltering (x_buffer, n, filters[ch][0], y_buffer); // прогоним данные через фильтр memcpy(x_buffer, y_buffer, n*sizeof(double)); } if(filters_on[ch][1]) { IIRCascadeFiltering (x_buffer, n, filters[ch][1], y_buffer); // прогоним данные через фильтр memcpy(x_buffer, y_buffer, n*sizeof(double)); } // сохранение отфильтрованных данных с децимацией ptr=&data_str->data_from_plugin[ch]; // куда положим результат for(i=n1=0, ptr1=x_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, j; char *filter_names[]={"Бесселя", "Баттерворта", "Чебышева", "Инверсный Чебышева", "Эллиптический"}; char *type_names[]={"Фильтр нижних частот", "Фильтр верхних частот", "Полосовой фильтр", "Режекторный фильтр", "Отключен"}; char *group_names[]={"Фильтр A", "Фильтр B", "Фильтр C"}; char text[128]; // установим общие переменные strcpy(p_info->name, "DigfiltersAdv"); // название плагина p_info->version=0x00010000; // версия 1.0 p_info->lgraph_version=0x222; // плагин разработан для версии 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=3+8+3*6; // всего 6 параметров for(i=0; i < 8; i++) { // Параметр выбора типа фильтра (ФНЧ, ФВЧ и т.п.) sprintf(text, "Канал %c", 'A'+i); strcpy(p_info->parameters_names[FILTER_CHANNEL_TYPE0+i], text); p_info->parameters_type[FILTER_CHANNEL_TYPE0+i]=L_TYPE_RING; for(j=0; j < 3; j++) strncpy(p_info->ring_names[FILTER_CHANNEL_TYPE0+i][j], group_names[j], 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_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; for(i=0; i < 6; i++) { int filter_n; char filter_name; filter_n=(i%2)+1; if(i < 2) filter_name='A'; else if(i < 4) filter_name='B'; else filter_name='C'; // Параметр выбора типа фильтра (ФНЧ, ФВЧ и т.п.) sprintf(text, "Фильтр %c%u", filter_name, filter_n); strcpy(p_info->parameters_names[FILTERA1_TYPE_INDEX+i*3], text); p_info->parameters_type[FILTERA1_TYPE_INDEX+i*3]=L_TYPE_RING; p_info->default_parameters_int[FILTERA1_TYPE_INDEX+i*3]=(filter_n == 1) ? 0 : 4; for(j=0; j < 5; j++) strncpy(p_info->ring_names[FILTERA1_TYPE_INDEX+i*3][j], type_names[j], 63); // Нижняя полоса среза sprintf(text, "Нижняя полоса среза фильтра %c%u , Гц", filter_name, filter_n); strcpy(p_info->parameters_names[FILTERA1_LOW_CUTOFF_INDEX+i*3], text); p_info->parameters_type[FILTERA1_LOW_CUTOFF_INDEX+i*3]=L_TYPE_DOUBLE; p_info->default_parameters_dbl[FILTERA1_LOW_CUTOFF_INDEX+i*3]=100; // Верхняя полоса среза sprintf(text, "Верхняя полоса среза фильтра %c%u , Гц", filter_name, filter_n); strcpy(p_info->parameters_names[FILTERA1_HIGH_CUTOFF_INDEX+i*3], text); p_info->parameters_type[FILTERA1_HIGH_CUTOFF_INDEX+i*3]=L_TYPE_DOUBLE; p_info->default_parameters_dbl[FILTERA1_HIGH_CUTOFF_INDEX+i*3]=120; } } // ********************************************************************************************************* // обработка данных о параметрах модулей АЦП от LGraph void __stdcall PluginDataInfo(struct PluginDataInfoStr *d_info) { int i, r, j; 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; // Проверим установленные частоты среза на допустимые значения. for(i=0; i < 6; i++) { int index; index=FILTERA1_TYPE_INDEX+i*3; if(d_info->parameters_int[index] == 4) continue; // не проверяем параметры для отключенного фильтра index=FILTERA1_LOW_CUTOFF_INDEX+i*3; if(d_info->parameters_dbl[index] == 0) { strcpy(d_info->error, "Неверная нижняя частота среза. Должна быть больше 0."); return; } if(d_info->parameters_dbl[index] > DataInfo.rate[device_index]/2) { strcpy(d_info->error, "Неверная нижняя частота среза. Должна быть меньше половины частоты дискретизации АЦП"); return; } index=FILTERA1_HIGH_CUTOFF_INDEX+i*3; if(d_info->parameters_dbl[index] > DataInfo.rate[device_index]/2) { strcpy(d_info->error, "Неверная верхняя частота среза. Должна быть меньше половины частоты дискретизации АЦП"); return; } if(d_info->parameters_dbl[FILTERA1_LOW_CUTOFF_INDEX+i*3] >= d_info->parameters_dbl[index]) { strcpy(d_info->error, "Неверная нижняя частота среза. Должна быть меньше верхней частоты среза."); return; } } // Расчитаем и подготовим фильтры for(i=0; i < DataInfo.plugin_nch; i++) for(j=0; j < 2; j++) { int group_index, index; if(filters[i][j] != NULL) { FreeIIRFilterPtr (filters[i][j]); filters[i][j]=0; } // сотрем старые коэффициенты group_index=DataInfo.parameters_int[FILTER_CHANNEL_TYPE0+i]; // из какой группы (A,B,C) берем параметры фильтров index=group_index*6+FILTERA1_TYPE_INDEX+j*3; // индекс параметров фильтра filters_on[i][j]=(DataInfo.parameters_int[index] == 4) ? 0 : 1; // включен ли соответствующий фильтр if(!filters_on[i][j]) continue; // выделим память под новые коэффициенты filters[i][j]=AllocIIRFilterPtr (DataInfo.parameters_int[index], DataInfo.parameters_int[FILTER_ORDER_INDEX]); if(filters[i][j] == 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[index+1], DataInfo.parameters_dbl[index+2], filters[i][j]); break; case 1: // Баттерворта r=Bw_CascadeCoef (DataInfo.rate[device_index], DataInfo.parameters_dbl[index+1], DataInfo.parameters_dbl[index+2], filters[i][j]); break; case 2: // Чебышева r=Ch_CascadeCoef (DataInfo.rate[device_index], DataInfo.parameters_dbl[index+1], DataInfo.parameters_dbl[index+2], 0.1, filters[i][j]); break; case 3: // Инверсный Чебышева r=InvCh_CascadeCoef (DataInfo.rate[device_index], DataInfo.parameters_dbl[index+1], DataInfo.parameters_dbl[index+2], 60.0, filters[i][j]); break; case 4: // Эллиптический r=Elp_CascadeCoef (DataInfo.rate[device_index], DataInfo.parameters_dbl[index+1], DataInfo.parameters_dbl[index+2], 1.0, 60.0, filters[i][j]); 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, j; for(i=0; i < DataInfo.plugin_nch; i++) for(j=0; j < 2; j++) { decimation_index[i]=0; if(filters_on[i][j]) ResetIIRFilter (filters[i][j]); } } /* Функция сообщает плагину, что закончился сбор данных */ void __stdcall PluginStopInput(struct PluginDataStr *data_str){} //********************************************************************************************************* // Функция вызываемая при загрузке - выгрузке DLL плагина int __stdcall DllMain (HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) { int i, j; 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++) for(j=0; j < 2; j++) if(filters[i][j] != NULL) FreeIIRFilterPtr (filters[i][j]); if(x_buffer != NULL) free(x_buffer); if(y_buffer != NULL) free(y_buffer); if (!CVIRTEHasBeenDetached ()) CloseCVIRTE (); break; } return 1; }

Контакты

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

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

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

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