XY осциллограф

 

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

Плагин представляет собой XY самописец, позволяющий выводить данные одного канала АЦП относительного другого в течении продолжительного интервала времени. В частности, подобный самописец может быть использован для снятия вольт-амперных характеристик.
  • Для уменьшения шумов можно установить параметр, задающий длительность усреднения.
  • Выбор масштаба времени.
  • Можно включить режим компенсации межканальной задержки. В этом режиме временной сдвиг, обусловленный тем, что отсчеты каналов X и Y оцифровываются не одновременно, а последовательно, компенсируется путем вычисления смещенных отсчетов канала Y с помощью кубического интерполяционного многочлена.
  • Расчет постоянной составляющей (DC) и среднеквадратического значения переменной составляющей (AC) по обоим каналам X и Y.

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

/* Плагин XY осциллографа для снятия вольт-амперной характеристики. Среда разработки LabWindows CVI 9.0. */ #include "toolbox.h" #include #include #include #include #include #include "..\\include\\plugin.h" // индексы визуальных элементов #define VISUAL_INDEX_GRAPH 0 // график #define VISUAL_INDEX_AC_X 1 // значение AC канала X #define VISUAL_INDEX_DC_X 2 // значение DC канала X #define VISUAL_INDEX_AC_Y 3 // значение AC канала Y #define VISUAL_INDEX_DC_Y 4 // значение DC канала Y #define VISUAL_INDEX_N 5 // размер выборки // индексы параметров #define PARM_AVVERAGE_ON 0 // усреднять значения #define PARM_DURATION_SCALE 1 // масштаб времени #define PARM_DURATION 2 // интервал усреднения #define PARM_COMPENSATE_CHANNEL_DELAY 3 // разрешение компенсации межканальной задержки #define PARM_PRECISION 4 // число знаков после запятой #define PARM_LINE 5 // тип линии #define MAX_SIZE 100000 // максимальное число кадров, которые можно обработать за один раз static struct PluginDataInfoStr DataInfo; // структура с настройками АЦП struct PluginVisualMainStr LgraphVisual; // структура с настройками LGraph2 static int device_index=0; // работаем с первым модулем АЦП static double *YData, *XData, *YNewData, *Yout, *Yfreq; // указатели на временные буфера int Points, AverageIndex, FirstFlag; double AverageData[2], LastData[2]; int LineTypeArray[2]={VAL_THIN_LINE, VAL_FAT_LINE}; // ********************************************************************************************************* // главная функция обмена данными void __stdcall PluginDataExchange(struct PluginDataStr *data_str) { int i, j, chan, index, n, mode, imax, imin, good, inverse_flag, f_detect_flag=0, n1; int chan1, chan2, nch; double pi, *ptr, x_freq, y_freq, freq, dt1, ac, dc, duration; double x_interp[4], y_interp[4], x_new, y_new, error, dt, channel_delay; chan1=DataInfo.adc_channels[0]; // запомним номер канала X плагина (от 0 до 31) chan2=DataInfo.adc_channels[1]; // запомним номер канала Y плагина (от 0 до 31) n=data_str->n; // сколько кадров будем обрабатывать dt=1000000./DataInfo.rate[device_index]; // интервал в микросекундах for(chan=0, nch=DataInfo.nch[device_index]; chan < 2; chan++) // цикл по двум каналам { // переложим в XData, YData данные АЦП index=(chan) ? DataInfo.chan_kadr_offset[device_index][chan2] : DataInfo.chan_kadr_offset[device_index][chan1]; ptr=(chan) ? YData : XData; for(i=0; i < n; i++, index += nch) *ptr++=data_str->data_to_plugin[index]; ACDCEstimator ((chan) ? YData : XData, n, &ac, &dc); // расчет AC,DC значений data_str->slow_data[(chan) ? VISUAL_INDEX_AC_Y : VISUAL_INDEX_AC_X]=ac; data_str->slow_data[(chan) ? VISUAL_INDEX_DC_Y : VISUAL_INDEX_DC_X]=dc; } // вычислим межканальную задержку в микросекундах channel_delay=0; if(DataInfo.interchannel_delay[device_index] > 0 && chan1 != chan2) channel_delay=(DataInfo.chan_kadr_offset[device_index][chan2]-DataInfo.chan_kadr_offset[device_index][chan1])* DataInfo.interchannel_delay[device_index]; // если надо учесть межканальную задержку займемся кубической интерполяцией if(DataInfo.parameters_int[PARM_COMPENSATE_CHANNEL_DELAY] && channel_delay > 0) { for(i=0; i < 3; i++) x_interp[i]=i*dt; for(i=0; i < n; i++) { if(!i) { for(j=0; j < 3; j++) y_interp[j]=YData[i+j]; x_new=-channel_delay; } // первая точка else if(i == n-1) { for(j=0; j < 3; j++) y_interp[j]=YData[i+j-2]; x_new=dt*2-channel_delay; } // все точки else { for(j=0; j < 3; j++) y_interp[j]=YData[i+j-1]; x_new=dt-channel_delay; } // последняя точка PolyInterp (x_interp, y_interp, 3, x_new, &YNewData[i], &error); // интерполяция многочленом третьей степени } memcpy(YData, YNewData, n*sizeof(double)); // заменим исходный массив на смещенный на channel_delay мкс } if(DataInfo.parameters_int[PARM_AVVERAGE_ON]) // если включено усреднение { int points=0; for(i=0; i < n; i++) { AverageData[0] += XData[i]; AverageData[1] += YData[i]; AverageIndex++; if(AverageIndex >= Points) { XData[points]=AverageData[0] / Points; YData[points]=AverageData[1] / Points; points++; AverageData[0]=AverageData[1]=AverageIndex=0; } } n=points; } if(n && !FirstFlag) { for(i=0; i < n; i++) { YData[n-i]=YData[n-i-1]; XData[n-i]=XData[n-i-1]; } XData[0]=LastData[0]; YData[0]=LastData[1]; n++; } if(n) { FirstFlag=0; LastData[0]=XData[n-1]; LastData[1]=YData[n-1]; } // сохраним данные для передачи в ЛГраф data_str->delete_graphs[0]=0; data_str->n_from_graph[0]=n; data_str->x_data[0]=XData; data_str->data_from_graph[0]=YData; data_str->control_index[0]=0; // номер графического элемента, в котором будет нарисован график data_str->color[0]=VAL_RED; // цвет графика data_str->line_type[0]=VAL_SOLID; // тип линии (сплошной, точки и т.п.) data_str->line_mode[0]=LineTypeArray[DataInfo.parameters_int[PARM_LINE]]; } //********************************************************************************************************* // информационная функция void __stdcall PluginInfo(struct PluginInfoStr *p_info) { // установим общие переменные strcpy(p_info->name, "VA oscilloscope"); // название плагина p_info->version=0x00010000; // версия 1.0 p_info->lgraph_version=0x222; // плагин разработан для версии 2.33 p_info->max_nch=p_info->min_nch=2; // максимальное число каналов, которые может обработать плагин 2 // установим параметры входных каналов плагина strcpy(p_info->channel_names[0], "Канал V"); strcpy(p_info->channel_names[1], "Канал A"); p_info->parameters=6; // всего 7 параметров // Включение усреднения strcpy(p_info->parameters_names[PARM_AVVERAGE_ON], "Усреднять значения"); p_info->parameters_type[PARM_AVVERAGE_ON]=L_TYPE_RING; strncpy(p_info->ring_names[PARM_AVVERAGE_ON][0], "Нет", 63); strncpy(p_info->ring_names[PARM_AVVERAGE_ON][1], "Да", 63); p_info->default_parameters_int[PARM_AVVERAGE_ON]=1; // Масштаб временного интервала отображения strcpy(p_info->parameters_names[PARM_DURATION_SCALE], "Масштаб установки временного интервала"); p_info->parameters_type[PARM_DURATION_SCALE]=L_TYPE_RING; strncpy(p_info->ring_names[PARM_DURATION_SCALE][0], "Секунды", 63); strncpy(p_info->ring_names[PARM_DURATION_SCALE][1], "Миллисекунды", 63); strncpy(p_info->ring_names[PARM_DURATION_SCALE][2], "Микросекунды", 63); strncpy(p_info->ring_names[PARM_DURATION_SCALE][3], "Кадры", 63); p_info->default_parameters_int[PARM_DURATION_SCALE]=0; // Временной интервал отображения strcpy(p_info->parameters_names[PARM_DURATION], "Временной интервал усреднения"); p_info->parameters_type[PARM_DURATION]=L_TYPE_DOUBLE; p_info->default_parameters_dbl[PARM_DURATION]=0.1; // Режим компенсации межканальной задержки strcpy(p_info->parameters_names[PARM_COMPENSATE_CHANNEL_DELAY], "Компенсировать межканальную задержку"); p_info->parameters_type[PARM_COMPENSATE_CHANNEL_DELAY]=L_TYPE_RING; strncpy(p_info->ring_names[PARM_COMPENSATE_CHANNEL_DELAY][0], "Нет", 63); strncpy(p_info->ring_names[PARM_COMPENSATE_CHANNEL_DELAY][1], "Да", 63); p_info->default_parameters_int[PARM_COMPENSATE_CHANNEL_DELAY]=1; // Число знаков после запятой strcpy(p_info->parameters_names[PARM_PRECISION], "Число знаков после запятой."); p_info->parameters_type[PARM_PRECISION]=L_TYPE_INT; p_info->default_parameters_int[PARM_PRECISION]=3; p_info->min_parameters_int[PARM_PRECISION]=1; p_info->max_parameters_int[PARM_PRECISION]=10; // Тип линии strcpy(p_info->parameters_names[PARM_LINE], "Тип линии"); p_info->parameters_type[PARM_LINE]=L_TYPE_RING; strncpy(p_info->ring_names[PARM_LINE][0], "Тонкая", 63); strncpy(p_info->ring_names[PARM_LINE][1], "Толстая", 63); if(XData == NULL) { // выделим память XData=malloc(MAX_SIZE*sizeof(double)); if(XData == NULL) { strcpy(p_info->error, "Не хватает памяти"); return; } YData=malloc(MAX_SIZE*sizeof(double)); if(YData == NULL) { free(XData); XData=0; strcpy(p_info->error, "Не хватает памяти"); return; } YNewData=malloc(MAX_SIZE*sizeof(double)); if(YNewData == NULL) { free(XData); XData=0; free(YData); strcpy(p_info->error, "Не хватает памяти"); return; } if(Yout == NULL) Yout=malloc(sizeof(double)*MAX_SIZE/2); if(Yout == NULL) { free(XData); XData=0; strcpy(p_info->error, "Не хватает памяти"); return; } if(Yfreq == NULL) Yfreq=malloc(sizeof(double)*MAX_SIZE); if(Yfreq == NULL) { free(XData); XData=0; strcpy(p_info->error, "Не хватает памяти"); return; } } } // ********************************************************************************************************* // обработка данных о параметрах модулей АЦП от LGraph void __stdcall PluginDataInfo(struct PluginDataInfoStr *d_info) { int chan_x, chan_y; if(!d_info->devices) { strcpy(d_info->error, "Нет модуля АЦП"); return; } if(!d_info->nch[device_index]) { strcpy(d_info->error, "Не выбраны каналы АЦП"); return; } if(d_info->adc_nch < 2) { strcpy(d_info->error, "Не выбраны каналы АЦП"); return; } DataInfo=*d_info; // запомним параметры АЦП chan_x=d_info->adc_channels[0]; // запомним номер канала (от 0 до 31) chan_y=d_info->adc_channels[1]; // запомним номер канала (от 0 до 31) // проверим включен ли канал if(!d_info->chan_on[device_index][chan_x]) { sprintf(d_info->error, "Не выбран канал V"); return; } if(!d_info->chan_on[device_index][chan_y]) { sprintf(d_info->error, "Не выбран канал A"); return; } // определим сколько точек будем обрабатывать switch(d_info->parameters_int[PARM_DURATION_SCALE]) { case 0: Points=d_info->parameters_dbl[PARM_DURATION]*d_info->rate[device_index]+0.5; break; case 1: Points=d_info->parameters_dbl[PARM_DURATION]*d_info->rate[device_index]/1000.+0.5; break; case 2: Points=d_info->parameters_dbl[PARM_DURATION]*d_info->rate[device_index]/1000000.+0.5; break; case 3: Points=d_info->parameters_dbl[PARM_DURATION]; break; } if(Points < 2 && d_info->parameters_int[PARM_AVVERAGE_ON]) { sprintf(d_info->error, "Слишком мало точек"); return; } if(Points > MAX_SIZE && d_info->parameters_int[PARM_AVVERAGE_ON]) { sprintf(d_info->error, "Слишком много точек"); return; } d_info->input_kadrs_min=1; d_info->input_kadrs_max=MAX_SIZE-1; d_info->no_omit_old_data=1; d_info->no_blocks=1; // плагину нужны все данные в режиме просмотра файла } //********************************************************************************************************* // настройка визуальных элементов void __stdcall PluginVisualSetting(struct PluginVisualMainStr *main_visual_settings, struct PluginVisualStr p_visual[]) { int graph_height, graph_width, m_top, m_left; char *duration_names[]={"T, секунды", "T, миллисекунды", "T, микросекукнды", "T, кадры"}; LgraphVisual=*main_visual_settings; main_visual_settings->n=6; // создаем 8 визуальных элементов main_visual_settings->plugin_height=main_visual_settings->height*0.7; // высота 0.7 экрана // ********** НАСТРОЙКИ ГРАФИКА graph_height=main_visual_settings->plugin_height-40; graph_width=main_visual_settings->width-120; m_top=25; m_left=graph_width+20; p_visual[VISUAL_INDEX_GRAPH].type=L_VISUAL_GRAPH; // настроим графические координаты p_visual[VISUAL_INDEX_GRAPH].top=20; // координата по вертикали графика p_visual[VISUAL_INDEX_GRAPH].height=graph_height; // высота графика p_visual[VISUAL_INDEX_GRAPH].width=graph_width; // ширина графика p_visual[VISUAL_INDEX_GRAPH].left=10; // горизонтальная координата // сконфигурируем ось X p_visual[VISUAL_INDEX_GRAPH].x_axis_mode=1; // по умолчанию включим автомасштаб по оси X // сконфигурируем ось Y p_visual[VISUAL_INDEX_GRAPH].y_axis_mode=1; // по умолчанию включим автомасштаб по оси X //************ настройка остальных визуальных элементов p_visual[VISUAL_INDEX_AC_X].type=L_VISUAL_NUMERIC; strcpy(p_visual[VISUAL_INDEX_AC_X].label_text, "AC (X)"); p_visual[VISUAL_INDEX_AC_X].top=m_top; // координата по вертикали p_visual[VISUAL_INDEX_AC_X].left=m_left; // координата по горизонтали p_visual[VISUAL_INDEX_AC_X].y_precision=DataInfo.parameters_int[PARM_PRECISION]; p_visual[VISUAL_INDEX_DC_X].type=L_VISUAL_NUMERIC; strcpy(p_visual[VISUAL_INDEX_DC_X].label_text, "DC (X)"); p_visual[VISUAL_INDEX_DC_X].top=m_top+50; // координата по вертикали p_visual[VISUAL_INDEX_DC_X].left=m_left; // координата по горизонтали p_visual[VISUAL_INDEX_DC_X].y_precision=DataInfo.parameters_int[PARM_PRECISION]; p_visual[VISUAL_INDEX_AC_Y].type=L_VISUAL_NUMERIC; strcpy(p_visual[VISUAL_INDEX_AC_Y].label_text, "AC (Y)"); p_visual[VISUAL_INDEX_AC_Y].top=m_top+50*3+20; // координата по вертикали p_visual[VISUAL_INDEX_AC_Y].left=m_left; // координата по горизонтали p_visual[VISUAL_INDEX_AC_Y].y_precision=DataInfo.parameters_int[PARM_PRECISION]; p_visual[VISUAL_INDEX_DC_Y].type=L_VISUAL_NUMERIC; strcpy(p_visual[VISUAL_INDEX_DC_Y].label_text, "DC (Y)"); p_visual[VISUAL_INDEX_DC_Y].top=m_top+50*4+20; // координата по вертикали p_visual[VISUAL_INDEX_DC_Y].left=m_left; // координата по горизонтали p_visual[VISUAL_INDEX_DC_Y].y_precision=DataInfo.parameters_int[PARM_PRECISION]; p_visual[VISUAL_INDEX_N].type=L_VISUAL_NUMERIC; strcpy(p_visual[VISUAL_INDEX_N].label_text, duration_names[DataInfo.parameters_int[PARM_DURATION_SCALE]]); p_visual[VISUAL_INDEX_N].top=m_top+50*6+40; // координата по вертикали p_visual[VISUAL_INDEX_N].left=m_left; // координата по горизонтали p_visual[VISUAL_INDEX_N].y_precision=DataInfo.parameters_int[PARM_PRECISION]; } // Функция сообщает плагину, что начался/закончился сбор данных (в данном плагине не используем) void __stdcall PluginStartInput(struct PluginDataStr *data_str) { AverageData[0]=AverageData[1]=AverageIndex=0; FirstFlag=1; } void __stdcall PluginStopInput(struct PluginDataStr *data_str) {} //********************************************************************************************************* // Функция вызываемая при загрузке - выгрузке DLL плагина int __stdcall DllMain (HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) { switch (fdwReason) { case DLL_PROCESS_ATTACH: if (InitCVIRTE (hinstDLL, 0, 0) == 0) return 0; break; case DLL_PROCESS_DETACH: if(XData != NULL) free(XData); if(YData != NULL) free(YData); if(YNewData != NULL) free(YNewData); if(Yout != NULL) free(Yout); if(Yfreq != NULL) free(Yfreq); 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 мск

L-CARD в проектах