From 84e847ea142d96d1b8d32b495b09e6e12b3429c3 Mon Sep 17 00:00:00 2001 From: Theodor Chikin Date: Fri, 27 Jun 2025 15:20:50 +0300 Subject: [PATCH] initial commit. Thereis an old software for configuring and reading data from ADC in a RagiPhotonic generator --- BF_companion | Bin 0 -> 26472 bytes l502-BFfirmware0.ldr | 1 + main.c | 585 +++++++++++++++++++++++++++++++++++++++++++ makefile | 48 ++++ plotter.py | 35 +++ 5 files changed, 669 insertions(+) create mode 100755 BF_companion create mode 120000 l502-BFfirmware0.ldr create mode 100644 main.c create mode 100644 makefile create mode 100755 plotter.py diff --git a/BF_companion b/BF_companion new file mode 100755 index 0000000000000000000000000000000000000000..7fbf31eb594d8ccf0bf97cefba3db06b9621c0e0 GIT binary patch literal 26472 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 Rnf~;*%I|kBuqoJB_P?B8N~{0? literal 0 HcmV?d00001 diff --git a/l502-BFfirmware0.ldr b/l502-BFfirmware0.ldr new file mode 120000 index 0000000..519975b --- /dev/null +++ b/l502-BFfirmware0.ldr @@ -0,0 +1 @@ +../BFfirmware_0/build/release/bin/l502-BFfirmware0.ldr \ No newline at end of file diff --git a/main.c b/main.c new file mode 100644 index 0000000..d750a3a --- /dev/null +++ b/main.c @@ -0,0 +1,585 @@ +/* Данный пример представляет из себя консольную программу на языке C, + демонстрирующую работу с модулями E16, L502 и E502 на примере синхронного + ввода данных с АЦП и цифровых линий. + + Перед сбором в примере идет поиск модулей, подключенных по интерфейсам PCI-Express и USB, + и предоставляется список для выбора модуля, с которым нужно работать. + Для подключения по Ethernet нужно указать IP-адреса интересующих модулей + в качестве аргументов командной строки при вызове примера, тогда эти адреса + будут добавлены в конец списка выбора модуля. + Например, если интересуют модули с адресами 192.168.1.5 и 192.168.1.6, + то пример можно вызвать: + x502_stream_read 192.168.1.5 E16:192.168.1.6 + и две дополнительные строки с этими адресами появятся в списке выбора. + Для указания что ip адрес принадлежит модулю E16, адрес надо указать с префиксом, пример: E16:192.168.1.6 + + Настройки частот, количества принимаемых данных и т.д. задаются с помощью макросов в + начале программы. + Настройки логических каналов - с помощью таблиц f_channels/f_ch_modes/f_ch_ranges. + + Пример выполняет прием блоков данных заданного размера. + Сбор идет до нажатия любой клавиши на Windows или CTRL+C на Linux. + + Пример также показывает как выполнять обработку данных и определять начало кадра, + в случае если в X502_ProcessData() передается не целое число кадров. + + Данный пример содержит проект для Visual Studio 2008, а также может быть собран + gcc в Linux или mingw в Windows через makefile или с помощью cmake (подробнее + в комментариях в соответствующих файлах). + + Для того чтобы собрать проект в Visual Studio, измените путь к заголовочным файлам + (Проект (Project) -> Свойства (Properties) -> Свойства конфигурации (Configuration Properties) + -> С/С++ -> Общие (General) -> Дополнительные каталоги включения (Additional Include Directories)) + на тот, где у вас лежат заголовочный файлы x502api.h, l502api.h и e502api.h и измените путь к библиотекам + (Проект (Project) -> Свойства (Properties) -> Свойства конфигурации (Configuration Properties) -> + Компановщик (Linker) -> Общие (General) -> Дополнительные катологи библиотек (Additional Library Directories)). + + Внимание!!: Если Вы собираете проект под Visual Studio и взяли проект с сайта (а не из SDK), + то для корректного отображения русских букв в программе нужно изменить кодировку + или указать сохранение с сигнатурой кодировки для UTF-8: + выберите Файл (File) -> Дополнительные параметры сохранения (Advanced Save Options)... + и в поле Кодировка (Encoding) выберите Юникод (UTF8, с сигнатурой)/Unicode (UTF-8 with signature) + и сохраните изменения в файле. + + */ + +//#include "dislin.h" //graphics library + + +#include //for namigs of saved files + +#include +#include "e502api.h" +//#include "dev_funcs.h" + +#ifdef _WIN32 +#include +#include +#else +#include +#include +#include +#endif + +#include +#include + +// количество используемых логических каналов // +#define ADC_LCH_CNT 1 + +// частота сбора АЦП в Гц// +#define ADC_FREQ 2000000 +// частота кадров (на логический канал). При ADC_FREQ/ADC_LCH_CNT - межкадровой задержки нет // +#define ADC_FRAME_FREQ (ADC_FREQ/ADC_LCH_CNT) +// частота синхронного ввода в Гц// +#define DIN_FREQ 500000 + + +#define TCP_CONNECTION_TOUT 5000 + + +// сколько отсчетов считываем за блок // +#define READ_BLOCK_SIZE 4096*200 +// таймаут на прием блока (мс) // +#define READ_TIMEOUT 2000 + + +// номера используемых физических каналов // +static uint32_t f_channels[ADC_LCH_CNT] = {0}; +// режимы измерения для каналов // +static uint32_t f_ch_modes[ADC_LCH_CNT] = {X502_LCH_MODE_COMM}; +// диапазоны измерения для каналов // +static uint32_t f_ch_ranges[ADC_LCH_CNT] = {X502_ADC_RANGE_1}; + + + +// признак необходимости завершить сбор данных // +static int f_out = 0; + +#ifndef _WIN32 +// Обработчик сигнала завершения для Linux // +static void f_abort_handler(int sig) { + f_out = 1; +} +#endif + +char const *DEV_E502 = "E502"; +char const *DEV_E16 = "E16"; + +typedef struct { + uint32_t ip_addr; + char const *devname; +} ip_dev_list_t; + +/* Функция находит все подключенные модули по интерфейсам PCI-Express и USB и + * сохраняет записи о этих устройствах в выделенный массив. + * Также создаются записи по переданным IP-адресам модулей и добавляются в конец + * массива. + * Указатель на выделенный массив, который должен быть потом очищен, сохраняется + * в pdevrec_list, а количество действительных элементов (память которых должна + * быть в дальнейшем освобождена с помощью X502_FreeDevRecordList()) возвращается + * как результат функции */ +static uint32_t f_get_all_devrec(t_x502_devrec **pdevrec_list, ip_dev_list_t *ip_dev_list, unsigned ip_cnt) { + int32_t fnd_devcnt = 0; + uint32_t pci_devcnt = 0; + uint32_t usb_devcnt = 0; + uint32_t e16_usb_devcnt = 0; + + t_x502_devrec *rec_list = NULL; + + // получаем количество подключенных устройств по интерфейсам PCI и USB // + L502_GetDevRecordsList(NULL, 0, 0, &pci_devcnt); + E502_UsbGetDevRecordsList(NULL, 0, 0, &usb_devcnt); + // E16_UsbGetDevRecordsList(NULL, 0, 0, &e16_usb_devcnt); + + if ((pci_devcnt+usb_devcnt + e16_usb_devcnt + ip_cnt) != 0) { + // выделяем память для массива для сохранения найденного количества записей // + rec_list = malloc((pci_devcnt + usb_devcnt +e16_usb_devcnt + ip_cnt) * sizeof(t_x502_devrec)); + + if (rec_list != NULL) { + unsigned i; + // получаем записи о модулях L502, но не больше pci_devcnt // + if (pci_devcnt!=0) { + int32_t res = L502_GetDevRecordsList(&rec_list[fnd_devcnt], pci_devcnt, 0, NULL); + if (res >= 0) { + fnd_devcnt += res; + } + } + // добавляем записи о модулях E502, подключенных по USB, в конец массива // + if (usb_devcnt!=0) { + int32_t res = E502_UsbGetDevRecordsList(&rec_list[fnd_devcnt], usb_devcnt, 0, NULL); + if (res >= 0) { + fnd_devcnt += res; + } + } + + // добавляем записи о модулях E16, подключенных по USB, в конец массива // + // if (e16_usb_devcnt!=0) { + // int32_t res = E16_UsbGetDevRecordsList(&rec_list[fnd_devcnt], e16_usb_devcnt, 0, NULL); + // if (res >= 0) { + // fnd_devcnt += res; + // } + // } + + // создаем записи для переданного массива ip-адресов // + + } + } + + if (fnd_devcnt != 0) { + // если создана хотя бы одна запись, то сохраняем указатель на выделенный массив // + *pdevrec_list = rec_list; + } else { + *pdevrec_list = NULL; + free(rec_list); + } + + return fnd_devcnt; +} + + +static t_x502_hnd f_dev_select_open(int argc, char** argv) { + t_x502_hnd hnd = NULL; + uint32_t fnd_devcnt,i, dev_ind; + t_x502_devrec *devrec_list = NULL; + ip_dev_list_t *ip_dev_list = NULL; + uint32_t ip_cnt = 0; + + // если есть аргументы командной строки, то предполагаем, что это могут быть + // ip-адреса интересующих устройств. // + if (argc > 1) { + ip_dev_list = malloc((argc - 1) * sizeof(ip_dev_list[0])); + if (ip_dev_list == NULL) { + fprintf(stderr, "Ошибка выделения памяти!\n"); + } else { + for (i=1; (int)i < argc; i++) { + int a[4]; + if (sscanf(argv[i], "%d.%d.%d.%d", &a[0], &a[1], &a[2], &a[3])==4) { + ip_dev_list[ip_cnt].devname = DEV_E502; + ip_dev_list[ip_cnt++].ip_addr = ((a[0] & 0xFF) << 24) | + ((a[1] & 0xFF) << 16) | + ((a[2] & 0xFF) << 8) | + (a[3] & 0xFF); + } else + if (sscanf(argv[i], "E16:%d.%d.%d.%d", &a[0], &a[1], &a[2], &a[3])==4) { + ip_dev_list[ip_cnt].devname = DEV_E16; + ip_dev_list[ip_cnt++].ip_addr = ((a[0] & 0xFF) << 24) | + ((a[1] & 0xFF) << 16) | + ((a[2] & 0xFF) << 8) | + (a[3] & 0xFF); + } + } + } + } + + // получаем список модулей для выбора // + fnd_devcnt = f_get_all_devrec(&devrec_list, ip_dev_list, ip_cnt); + + if (fnd_devcnt == 0) { + printf("Не найдено ни одного модуля\n"); + } else { + // выводим информацию по списку модулей // + printf("Доступны следующие модули:\n"); + for (i=0; i < fnd_devcnt; i++) { + printf("Модуль № %d: %s, %-9s", i, devrec_list[i].devname, + devrec_list[i].iface == X502_IFACE_PCI ? "PCI/PCIe" : + devrec_list[i].iface == X502_IFACE_USB ? "USB" : + devrec_list[i].iface == X502_IFACE_ETH ? "Ethernet" : "?"); + + // при подключении по сети по IP-адресу серийный номер можно узнать + // только после открытия соединения. При этом поле location + // содержит строку с описанием адреса устройства // + if (devrec_list[i].iface != X502_IFACE_ETH) { + printf("Сер. номер: %s\n", devrec_list[i].serial); + } else { + printf("Адрес: %s\n", devrec_list[i].location); + } + } + + // выбираем нужный по введенному номеру модуля по порядку с клавиатуры // + //printf("Введите номер модуля, с которым хотите работать (от 0 до %d)\n", fnd_devcnt-1); + //fflush(stdout); + //scanf("%d", &dev_ind); + dev_ind = 0; + + if (dev_ind >= fnd_devcnt) { + printf("Неверно указан номер модуля...\n"); + } else { + // если ввели номер правильно - создаем описатель // + hnd = X502_Create(); + if (hnd==NULL) { + fprintf(stderr, "Ошибка создания описателя модуля!"); + } else { + // устанавливаем связь с модулем по записи // + int32_t err = X502_OpenByDevRecord(hnd, &devrec_list[dev_ind]); + if (err != X502_ERR_OK) { + fprintf(stderr, "Ошибка установления связи с модулем: %s!", X502_GetErrorString(err)); + X502_Free(hnd); + hnd = NULL; + } + } + } + + // освобождение ресурсов действительных записей из списка // + X502_FreeDevRecordList(devrec_list, fnd_devcnt); + // очистка памяти самого массива // + free(devrec_list); + } + + // освобождаем выделенный массив под IP-адреса (если был выделен) // + free(ip_dev_list); + + return hnd; +} + + +// настройка параметров модуля // +int32_t f_setup_params(t_x502_hnd hnd) { + int32_t err = X502_ERR_OK, i; + + // устанавливаем параметры логической таблицы АЦП // + err = X502_SetLChannelCount(hnd, ADC_LCH_CNT); + for (i=0; (i < ADC_LCH_CNT) && (err == X502_ERR_OK); i++) + err = X502_SetLChannel(hnd, i, f_channels[i], f_ch_modes[i], f_ch_ranges[i], 0); + +// // устанавливаем частоты ввода для АЦП и цифровых входов // +// if (err == X502_ERR_OK) { +// double f_adc = ADC_FREQ, f_frame = ADC_FRAME_FREQ, f_din = DIN_FREQ; +// err = X502_SetAdcFreq(hnd, &f_adc, &f_frame); +// if (err == X502_ERR_OK) +// err = X502_SetDinFreq(hnd, &f_din); +// if (err == X502_ERR_OK) { +// // выводим реально установленные значения - те что вернули функции // +// printf("Установлены частоты:\n Частота сбора АЦП = %0.0f\n" +// " Частота на лог. канал = %0.0f\n Частота цифрового ввода = %0.0f\n", +// f_adc, f_frame, f_din); +// } +// } + + + X502_SetSyncMode(hnd, X502_SYNC_DI_SYN1_FALL); + + //set ADC sample frequency from internal generator with frequency 2 MHz + //X502_SetRefFreq(hnd, X502_REF_FREQ_2000KHZ); + + + X502_SetSyncStartMode(hnd, X502_SYNC_DI_SYN2_RISE); //start ADC samping by external trigger + + + + + // записываем настройки в модуль // + if (err == X502_ERR_OK) + err = X502_Configure(hnd, 0); + + // разрешаем синхронные потоки // + if (err == X502_ERR_OK) { + err = X502_StreamsEnable(hnd, X502_STREAM_ADC); + } + + return err; +} + +int main(int argc, char** argv) { + int32_t err = X502_ERR_OK; + uint32_t ver; + t_x502_hnd hnd = NULL; +#ifndef _WIN32 + struct sigaction sa; + memset(&sa, 0, sizeof(sa)); + // В ОС Linux устанавливаем свой обработчик на сигнал закрытия, + // чтобы завершить сбор корректно // + sa.sa_handler = f_abort_handler; + sigaction(SIGTERM, &sa, NULL); + sigaction(SIGINT, &sa, NULL); + sigaction(SIGABRT, &sa, NULL); +#endif +#ifdef _WIN32 + // для вывода русских букв в консоль для ОС Windows в CP1251 без перевода в OEM // + setlocale(LC_CTYPE, ""); +#endif + // получаем версию библиотеки // + ver = X502_GetLibraryVersion(); + printf("Версия библиотеки: %d.%d.%d\n", (ver >> 24)&0xFF, (ver>>16)&0xFF, (ver>>8)&0xFF); + + ////******* Получение списка устройств и выбор, с каким будем работать *****************// + hnd = f_dev_select_open(argc, argv); + + ////******************************* Работа с модулем *************************// + // если успешно выбрали модуль и установили с ним связь - продолжаем работу // + if (hnd != NULL) { + // получаем информацию // + t_x502_info info; + err = X502_GetDevInfo(hnd, &info); + if (err != X502_ERR_OK) { + fprintf(stderr, "Ошибка получения серийного информации о модуле: %s!", X502_GetErrorString(err)); + } else { + // выводим полученную информацию // + printf("Установлена связь со следующим модулем:\n"); + printf(" Серийный номер : %s\n", info.serial); + printf(" Наличие ЦАП : %s\n", info.devflags & X502_DEVFLAGS_DAC_PRESENT ? "Да" : "Нет"); + printf(" Наличие BlackFin : %s\n", info.devflags & X502_DEVFLAGS_BF_PRESENT ? "Да" : "Нет"); + printf(" Наличие гальваноразвязки: %s\n", info.devflags & X502_DEVFLAGS_GAL_PRESENT ? "Да" : "Нет"); + printf(" Индустриальное исп. : %s\n", info.devflags & X502_DEVFLAGS_INDUSTRIAL ? "Да" : "Нет"); + printf(" Наличие интерф. PCI/PCIe: %s\n", info.devflags & X502_DEVFLAGS_IFACE_SUPPORT_PCI ? "Да" : "Нет"); + printf(" Наличие интерф. USB : %s\n", info.devflags & X502_DEVFLAGS_IFACE_SUPPORT_USB ? "Да" : "Нет"); + printf(" Наличие интерф. Ethernet: %s\n", info.devflags & X502_DEVFLAGS_IFACE_SUPPORT_ETH ? "Да" : "Нет"); + printf(" Версия ПЛИС : %d.%d\n", (info.fpga_ver >> 8) & 0xFF, info.fpga_ver & 0xFF); + printf(" Версия PLDA : %d\n", info.plda_ver); + if (info.mcu_firmware_ver != 0) { + printf(" Версия прошивки ARM : %d.%d.%d.%d\n", + (info.mcu_firmware_ver >> 24) & 0xFF, + (info.mcu_firmware_ver >> 16) & 0xFF, + (info.mcu_firmware_ver >> 8) & 0xFF, + info.mcu_firmware_ver & 0xFF); + } + } + + + if (err == X502_ERR_OK) { + // настраиваем параметры модуля // + err = f_setup_params(hnd); + if (err != X502_ERR_OK) + fprintf(stderr, "Ошибка настройки модуля: %s!", X502_GetErrorString(err)); + } + + + + + +// // запуск синхронного ввода-вывода // +// if (err == X502_ERR_OK) { +// err = X502_StreamsStart(hnd); +// if (err != X502_ERR_OK) +// fprintf(stderr, "Ошибка запуска сбора данных: %s!\n", X502_GetErrorString(err)); +// } + + + f_out = 2; + struct timespec time_started; + struct timespec time_ended; + timespec_get(&time_started, TIME_UTC); + float total_runs = 0.0; + long double delta_time = 0.0; + while(f_out != 1){ + total_runs += 1.0; + + X502_StreamsStart(hnd); //wait for trigger and start sampling + + + int32_t rcv_size; + uint32_t adc_size, din_size; + + // массив для приема необработанных данных // + static uint32_t rcv_buf[READ_BLOCK_SIZE]; + static double adc_data[READ_BLOCK_SIZE]; + static uint32_t din_data[READ_BLOCK_SIZE]; + + // принимаем данные (по таймауту) // + rcv_size = X502_Recv(hnd, rcv_buf, READ_BLOCK_SIZE, READ_TIMEOUT); + timespec_get(&time_ended, TIME_UTC); + + // результат меньше нуля означает ошибку // + if (rcv_size < 0) { + err = rcv_size; + fprintf(stderr, "Ошибка приема данных: %s\n", X502_GetErrorString(err)); + } else if (rcv_size > 0) { + uint32_t first_lch; + // получаем номер логического канала, которому соответствует первый отсчет АЦП в массиве // + X502_GetNextExpectedLchNum(hnd, &first_lch); + + adc_size = sizeof(adc_data)/sizeof(adc_data[0]); + din_size = sizeof(din_data)/sizeof(din_data[0]); + + // обрабатываем принятые данные, распределяя их на данные АЦП и цифровых входов // + err = X502_ProcessData(hnd, rcv_buf, rcv_size, X502_PROC_FLAGS_VOLT, + adc_data, &adc_size, din_data, &din_size); + if (err != X502_ERR_OK) { + fprintf(stderr, "Ошибка обработки данных: %s\n", X502_GetErrorString(err)); + } else { + uint32_t lch; + + printf("Обработано данных АЦП = %d, цифровых входов = %d\n", + adc_size, din_size); + long double t_started = (long double)time_started.tv_sec + 1e-9*(long double)time_started.tv_nsec; + printf("started at %jd.%09ld or %Lg\n", (intmax_t)time_started.tv_sec, time_started.tv_nsec, t_started); + + printf("received at %jd.%09ld \n", (intmax_t)time_ended.tv_sec, time_ended.tv_nsec); + + delta_time = ((long double) time_ended.tv_sec - time_started.tv_sec ) + (((long double) time_ended.tv_nsec) * 1e-9 ) - (((long double) time_started.tv_nsec) * 1e-9 ); + + printf("delta time %Lg sec\n", delta_time); + // если приняли цифровые данные - выводим первый отсчет // + + + + if (din_size != 0) + printf(" din_data = 0x%05X\n", din_data[0]); + + // выводим по одному отсчету на канал. если обработанный блок + // начинается не с начала кадра, то в данном примере для + // вывода берем конец неполного кадра и начало следующего + for (lch=0; lch < ADC_LCH_CNT; lch++) { + // определяем позицию первого отсчета, соответствующего заданному логическому каналу: + // либо с конца не полного кадра, либо из начала следующего // + uint32_t pos = lch >= first_lch ? lch - first_lch : ADC_LCH_CNT-first_lch + lch; + if (pos <= adc_size) { + printf(" lch[%d]=%6.4f\n, pos=%d, adc_size=%d", lch, adc_data[pos], pos, adc_size); + + time_t seconds; + time(&seconds); + + FILE *fptr; + char filename[] = " "; + sprintf(&filename, "adc data %ld.csv", seconds); + fptr = fopen(filename, "w"); + fprintf(fptr, "value number; time, sec; adc_value, V\n"); + + for (uint32_t data_pos; data_pos < adc_size; ++data_pos){ + fprintf(fptr, "%d %6.7f %6.7f \n", data_pos, (double)data_pos / X502_REF_FREQ_2000KHZ , adc_data[data_pos]); //print whole data + + //printf("%d %6.7f %6.7f \n", data_pos, (double)data_pos / X502_REF_FREQ_2000KHZ , adc_data[data_pos]); //print whole data + } + fclose(fptr); + + } else { + printf(" lch[%d]= ---- \n", lch); + } + } + + printf("\n"); + fflush(stdout); + } + } + + + + X502_StreamsStop(hnd); + + /* + xray = + + //plot results. plotter code from: https://www.mps.mpg.de/1757268/exa_c#section_1 + metafl ("cons"); + scrmod ("revers"); + disini (); + pagera (); + complx (); + axspos (450, 1800); + axslen (2200, 1200); + + name ("X-axis", "x"); + name ("Y-axis", "y"); + + labdig (-1, "x"); + ticks (9, "x"); + ticks (10, "y"); + + titlin ("Demonstration of CURVE", 1); + titlin ("SIN(X), COS(X)", 3); + + ic = intrgb (0.95,0.95,0.95); + axsbgd (ic); + + graf (0.0, 360.0, 0.0, 90.0, -1.0, 1.0, -1.0, 0.5); + setrgb (0.7, 0.7, 0.7); + grid (1, 1); + + color ("fore"); + height (50); + title (); + + color ("red"); + curve (xray, y1ray, n); + color ("green"); + curve (xray, y2ray, n); + disfin (); + */ + --f_out; + + + +#ifdef _WIN32 + /* проверка нажатия клавиши для выхода */ + if (err == X502_ERR_OK) { + if (_kbhit()) + f_out = 1; + } +#else + { + // по нажатию Enter включаем/выключаем поток ADC + fd_set fds; + struct timeval tv = { .tv_sec = 0, .tv_usec = 0 }; + + FD_ZERO(&fds); + FD_SET(STDIN_FILENO, &fds); + if (select(1, &fds, NULL, NULL, &tv) == 1) { + int x; + static int adc_disabled = 0; + int ch; + + if (adc_disabled) { + X502_StreamsEnable(hnd, X502_STREAM_ADC); + } else { + X502_StreamsDisable(hnd, X502_STREAM_ADC); + } + adc_disabled = !adc_disabled; + + while(select(1, &fds, NULL, NULL, &tv) == 1) { + read(STDIN_FILENO, &ch, 1); + } + } + } +#endif + +} + + printf("delta time %Lg sec\n", delta_time); + printf("average run time %Lg sec\n", delta_time/total_runs); + // закрываем связь с модулем + X502_Close(hnd); + // освобождаем описатель + X502_Free(hnd); + } + return err; +} diff --git a/makefile b/makefile new file mode 100644 index 0000000..6435129 --- /dev/null +++ b/makefile @@ -0,0 +1,48 @@ +# makefile для сборки примера с помощью компиляторов mingw (под Windows) +# или GCC (под Linux). Необходимо определить 3 переменные: +# +# CC - имя команды для вызова компилятора +# X502API_LIBRARIES_DIR - путь к файлм .a или .so библиотек l502api, e502api, x502api (если не стандартный) +# X502API_INCLUDE_DIR - путь к заголовочным файлам l502api.h, e502api.h, x502api.h +# +# Ниже приведено несколько примеров в закоментированном виде + +#--- Linux с заголовками и библиотекой в стандартных директориях: компилятор GCC +#CC = gcc + +#--- Вариант запуска из MSYS со стандартным 32-битным mingw +#CC = gcc +#X502API_LIBRARIES_DIR = "/c/Program Files/L-Card/lpcie/lib/mingw" +#X502API_INCLUDE_DIR = "/c/Program Files/L-Card/lpcie/include" + + +#--- 64-битный вариант mingw w64, идущий вместе с cygwin -------- +#CC = x86_64-w64-mingw32-gcc +#X502API_LIBRARIES_DIR = "/cygdrive/c/Program Files (x86)/L-Card/lpcie/lib/mingw64" +#X502API_INCLUDE_DIR = "/cygdrive/c/Program Files (x86)/L-Card/lpcie/include" + +#--- 32-битный вариант mingw w64, идущий вместе с cygwin -------- +#CC = i686-w64-mingw32-gcc +#X502API_LIBRARIES_DIR = "/cygdrive/c/Program Files (x86)/L-Card/lpcie/lib/mingw" +#X502API_INCLUDE_DIR = "/cygdrive/c/Program Files (x86)/L-Card/lpcie/include" + +#--- 32-битный вариант mingw, идущий вместе с cygwin -------- +#CC = i686-pc-mingw32-gcc +#X502API_LIBRARIES_DIR = "/cygdrive/c/Program Files (x86)/L-Card/lpcie/lib/mingw" +#X502API_INCLUDE_DIR = "/cygdrive/c/Program Files (x86)/L-Card/lpcie/include" + + +FLAGS = + +ifdef X502API_LIBRARIES_DIR + FLAGS += -L $(X502API_LIBRARIES_DIR) +endif + +ifdef X502API_INCLUDE_DIR + FLAGS += -I $(X502API_INCLUDE_DIR) +endif + + + +all: + $(CC) main.c $(FLAGS) -ll502api -le502api -lx502api -o BF_companion diff --git a/plotter.py b/plotter.py new file mode 100755 index 0000000..e747066 --- /dev/null +++ b/plotter.py @@ -0,0 +1,35 @@ +#!/usr/bin/pypy3 +import plotly.graph_objs as go +from decimal import * +from sys import argv + + +def main(): + chart = go.Figure() + chart.add_trace(go.Scatter(x=[1,2,3], y=[1,2,3])) + chart.show() + + +if __name__ == "__main__": + if len(argv) == 1: + main() + else: + f = open(argv[1], "rt") + f.readline() + X = [] + Y = [] + for line in f: + try: + i, x, y = line.split() + x = float(x) + y = float(y) + X.append(x) + Y.append(y) + except ValueError: + pass + f.close() + print("data samples:",len(X)) + chart = go.Figure() + chart.add_trace(go.Scatter(x=X, y=Y)) + chart.show() +