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

Чтение данных из E-502

Вы не вошли.

 Поиск | Регистрация | Вход 

Владислав654654
04.08.2019 11:44:46
#1

Гость

Чтение данных из E-502

Добрый день!

Необходимо в ОС Raspbian с помощью консольной программы записывать данные с АЦП E-502.

В то время как при записи данных с использованием LQMeasStudio графики, построенные по сохранённым данным, получаются хорошими, графики, построенные по данным, сохранённым с помощью консольной программы - совершенно не совпадают с реальностью.

Сохраняем в файл значения переменной adc-data и, воспроизводя графики в иной программе, получаем чушь (кривую синусоиду), хотя график должен быть совсем другим.

Подскажите пожалуйста, как устранить проблему?

05.08.2019 10:38:51
#2

Сотрудник "Л Кард"
Здесь с 17.04.2014
Сообщений: 1,292

Re: Чтение данных из E-502

Добрый день!

Т.к. c LQMeasStudio получаете правильные данные, то сам модуль и библиотеки-драйвера работают корректно.
Из возможных проблем, как видится, могут быть следующие:
1. Настройки модуля в консольной программе отличаются от LQMeasStudio (если выложите скрин настроек в LQMeasStudio и Ваш код настройки, то можно сравнить)
2. Проблема в сохранении принятых от модуля данных в файл (т.к. в примере сохранения нет, то это Ваш код, если опять же выложите сюда его, то может будет понятно в чем дело)

Владислав654654
06.08.2019 05:31:45
#3

Гость

Re: Чтение данных из E-502

Доброго времени суток!
Отправляем скрины того, как в программе Матлаб выглядят эти графики и коды программы матлаба:
https://ibb.co/dtHwmSY

 
A=fread((fopen('19_08_06_13-19-18-549.dat', 'rb')), inf, 'double');
plot(A);
 

Отправляем скрины с параметрами LQMeasStudio и код программы:

https://ibb.co/LxQ4DqW
https://ibb.co/hM9Bgp9


#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include <sys/time.h>

#define DATEFMT "YYYY.MM.DD//hh:mm:ss//MMM"

#include "l502api.h"
#include "e502api.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <malloc.h>
#include <time.h>
#include <errno.h>


#ifdef _WIN32
#include <locale.h>
#include <conio.h>
#else
#include <signal.h>
#include <unistd.h>
#include <string.h>
#endif

#include <stdio.h>
#include <stdlib.h>

/* количество используемых логических каналов */
#define ADC_LCH_CNT  1

/* частота сбора АЦП в Гц*/
#define ADC_FREQ          50000
/* частота кадров (на логический канал). При ADC_FREQ/ADC_LCH_CNT - межкадровой задержки нет */
#define ADC_FRAME_FREQ    (ADC_FREQ/ADC_LCH_CNT)
/* частота синхронного ввода в Гц*/
#define DIN_FREQ          1000000


#define TCP_CONNECTION_TOUT 5000


/* сколько отсчетов считываем за блок */
#define READ_BLOCK_SIZE   5000
/* таймаут на прием блока (мс) */
#define READ_TIMEOUT    100

/* номера используемых физических каналов */
static uint32_t f_channels[ADC_LCH_CNT] = {17};
/* режимы измерения для каналов */
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_10};

/* признак небходимости завершить сбор данных */
static int f_out = 0;

#ifndef _WIN32
/* Обработчик сигнала завершения для Linux */
static void f_abort_handler(int sig) {
    f_out = 1;
}
#endif




/* Функция находит все подключенные модули по интерфейсам PCI-Express и USB и
 * сохраняет записи о этих устройствах в выделенный массив.
 * Также создаются записи по переданным IP-адресам модулей и добавляются в конец
 * массива.
 * Указатель на выделенный массив, который должен быть потом очищен, сохраняется
 * в pdevrec_list, а количество действительных элементов (память которых должна
 * быть в дальнейшем освобождена с помощью X502_FreeDevRecordList()) возвращается
 * как результат функции */
static uint32_t f_get_all_devrec(t_x502_devrec **pdevrec_list, uint32_t *ip_addr_list, unsigned ip_cnt) {
    int32_t fnd_devcnt = 0;
    uint32_t pci_devcnt = 0;
    uint32_t 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);

    if ((pci_devcnt + usb_devcnt + ip_cnt) != 0) {
        /* выделяем память для массива для сохранения найденного количества записей */
        rec_list = malloc((pci_devcnt + 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;
                }
            }

            /* создаем записи для переданного массива ip-адресов */
            for (i=0; i < ip_cnt; i++) {
                if (E502_MakeDevRecordByIpAddr(&rec_list[fnd_devcnt], ip_addr_list[i],0, TCP_CONNECTION_TOUT) == X502_ERR_OK) {
                    fnd_devcnt++;
                }
            }
        }
    }

    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;
    uint32_t *ip_addr_list = NULL;
    uint32_t ip_cnt = 0;

    /* если есть аргументы командной строки, то предполагаем, что это могут быть
       ip-адреса интересующих устройств. */
    if (argc > 1) {
        ip_addr_list = malloc((argc-1) * sizeof(ip_addr_list[0]));
        if (ip_addr_list == NULL) {
            fprintf(stderr, "\n");
        } else {
            for (i=1; (int)i < argc; i++) {
                int a[4];
                if (sscanf(argv[i], "Oshibka vy`deleniya pamyati!%d.%d.%d.%d", &a[0], &a[1], &a[2], &a[3])==4) {
                    ip_addr_list[ip_cnt++] = ((a[0] & 0xFF) << 24) |
                                             ((a[1] & 0xFF) << 16) |
                                             ((a[2] & 0xFF) <<  8) |
                                             (a[3] & 0xFF);
                }
            }
        }
    }

    /* получаем список модулей для выбора */
    fnd_devcnt = f_get_all_devrec(&devrec_list, ip_addr_list, ip_cnt);

    if (fnd_devcnt == 0) {
        printf("Ne najdeno ni odnogo modulya\n");
    } else {
        /* выводим информацию по списку модулей */
        printf("Dostupny` sleduyushhie moduli:\n");
        for (i=0; i < fnd_devcnt; i++) {
            printf("Modul` # %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("Ser. nomer: %s\n", devrec_list[i].serial);
            } else {
                printf("Adres: %s\n", devrec_list[i].location);
            }
        }

        /* выбираем нужный по введенному номеру модуля по порядку с клавиатуры */
        printf("Vvedite nomer modulya, s kotory`m khotite rabotat` (ot 0 do %d)\n", fnd_devcnt-1);
        fflush(stdout);
        scanf("%d", &dev_ind);

        if (dev_ind >= fnd_devcnt) {
            printf("Neverno ukazan nomer modulya...\n");
        } else {
            /* если ввели номер правильно - создаем описатель */
            hnd = X502_Create();
            if (hnd==NULL) {
                fprintf(stderr, "Oshibka sozdaniya opisatelya modulya!");
            } else {
                /* устанавливаем связь с модулем по записи */
                int32_t err = X502_OpenByDevRecord(hnd, &devrec_list[dev_ind]);
                if (err != X502_ERR_OK) {
                    fprintf(stderr, "Oshibka ustanovleniya svyazi s modulem %s!", X502_GetErrorString(err));
                    X502_Free(hnd);
                    hnd = NULL;
                }
            }
        }

        /* освобождение ресурсов действительных записей из списка */
        X502_FreeDevRecordList(devrec_list, fnd_devcnt);
        /* очистка памяти самого массива */
        free(devrec_list);
    }

    /* освобождаем выделенный массив под IP-адреса (если был выделен) */
    free(ip_addr_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("Ustanovleny` chastoty`:\n    Chastota sbora ACzP = %0.0f\n"
                "    Chastota na log. kanal = %0.0f\n    Chastota czifrovogo vvoda = %0.0f\n",
                f_adc, f_frame, f_din);
        }
    }

    /* записываем настройки в модуль */
    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;
    static char stroka;

#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("Versiya biblioteki %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, "Oshibka polucheniya serijnogo informaczii o module %s!", X502_GetErrorString(err));
        } else {
            /* выводим полученную информацию */
            printf("Ustanovlena svyaz` so sleduyushhim modulem:\n");
            printf(" Serijny`j nomer          : %s\n", info.serial);
            printf(" Nalichie CzAP             : %s\n", info.devflags & X502_DEVFLAGS_DAC_PRESENT ? "Да" : "Нет");
            printf(" Nalichie BlackFin        : %s\n", info.devflags & X502_DEVFLAGS_BF_PRESENT ? "Да" : "Нет");
            printf(" Nalichie gal`vanorazvyazki: %s\n", info.devflags & X502_DEVFLAGS_GAL_PRESENT ? "Да" : "Нет");
            printf(" Industrial`noe isp.    : %s\n", info.devflags & X502_DEVFLAGS_INDUSTRIAL ? "Да" : "Нет");
            printf(" Nalichie interf. PCI/PCIe: %s\n", info.devflags & X502_DEVFLAGS_IFACE_SUPPORT_PCI ? "Да" : "Нет");
            printf(" Nalichie interf. USB     : %s\n", info.devflags & X502_DEVFLAGS_IFACE_SUPPORT_USB ? "Да" : "Нет");
            printf(" Nalichie interf. Ethernet: %s\n", info.devflags & X502_DEVFLAGS_IFACE_SUPPORT_ETH ? "Да" : "Нет");
            printf(" Versiya PLIS             : %d.%d\n", (info.fpga_ver >> 8) & 0xFF, info.fpga_ver & 0xFF);
            printf(" Versiya PLDA             : %d\n", info.plda_ver);
            if (info.mcu_firmware_ver != 0) {
                printf(" Versiya proshivki 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, "Oshibka nastrojki modulya: %s!", X502_GetErrorString(err));
        }


        /* запуск синхронного ввода-вывода */
        if (err == X502_ERR_OK) {
            err = X502_StreamsStart(hnd);
            if (err != X502_ERR_OK)
                fprintf(stderr, "Oshibka zapuska sbora danny`kh: %s!\n", X502_GetErrorString(err));
        }


        if (err == X502_ERR_OK) {
            int block;
            int32_t stop_err;

            printf("Sbor danny`kh zapushhen. Dlya ostanova nazhmite %s\n",
#ifdef _WIN32
                   "lyubuyu klavishu"
#else
                   "CTRL+C"
#endif
                   );
            fflush(stdout);




            for (block = 0; (err == X502_ERR_OK) && !f_out && block!=90; block++) {

               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);
                /* результат меньше нуля означает ошибку */
                if (rcv_size < 0) {
                    err = rcv_size;
                    fprintf(stderr, "Oshibka priema danny`kh: %s\n", X502_GetErrorString(err));
                } else if (rcv_size > 0) {
                    uint32_t first_lch;
                    /* получаем номер логического канала, которому соответствует первый отсчет АЦП в массиве */
                    X502_GetNextExpectedLchNum(hnd, &first_lch);

                    adc_size = 1000;
                    din_size = 0;

                    /* обрабатываем принятые данные, распределяя их на данные АЦП и цифровых входов */
                    err = X502_ProcessData(hnd, rcv_buf, rcv_size, X502_PROC_FLAGS_VOLT,
                                           adc_data, &adc_size, din_data, &din_size);

{

FILE *mf;
int posix_code;

struct timeval tv;
  struct timezone tz;
  struct tm tm;
  char yu="/media/pi/Elements/";
  gettimeofday(&tv, &tz);  // вместо tz можно 0, если сами не собираетесь с TZ играться

  //  gmtime_r(&tv.tv_sec, &tm); // UTC
  localtime_r(&tv.tv_sec, &tm);
  char now[sizeof(DATEFMT) + 16] = "strftime() error";
  size_t l = strftime(now, sizeof(now), "/media/pi/Elements/%g_%m_%d_%H-%M-%S-", &tm );
  sprintf(now + l, "%03d.dat", (int)tv.tv_usec / 1000); // я думаю, что в таких задачах округление излишне

//const char filename_format[] = "/home/pi/Desktop/testmat.bin";
//char filename_buffer[256];
//snprintf(filename_buffer, sizeof(filename_buffer), "/home/pi/Desktop/%d:%d:%d,%03.3d",now[0],2,3,4);
if (block==0){
mf=fopen(now,"a+b");
}
if (!mf){
printf("oshibka posix %d (%s)", errno, strerror(errno));
goto file_done;
}
if (fwrite(adc_data, sizeof(uint32_t), adc_size,mf)!=adc_size)
    printf("oshibka posix %d (%s)", errno, strerror(errno));

if (block==89){
fclose (mf);
}
}
file_done:
                    if (err != X502_ERR_OK) {
                        fprintf(stderr, "Oshibka obrabotki danny`kh: %s\n", X502_GetErrorString(err), err);
                    } else {
                        uint32_t lch;

                        printf("Blok %3d. Obrabotano danny`kh ACzP =%d, czifrovy`kh vkhodov =%d\n",
                               block, adc_size, din_size);
                        /* если приняли цифровые данные - выводим первый отсчет */
                        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", lch, adc_data[pos]);
                            } else {
                                printf("    lch[%d]= ---- \n", lch);
                            }
                        }
                        printf("\n");
                        fflush(stdout);
                    }
                }
#ifdef _WIN32
                /* проверка нажатия клавиши для выхода */
                if (err == X502_ERR_OK) {
                    if (_kbhit())
                        f_out = 1;
                }
#endif
if (block==89){
block=-1;
}

            }

            /* останавливаем поток сбора данных (независимо от того, была ли ошибка) */
            stop_err = X502_StreamsStop(hnd);
            if (stop_err != X502_ERR_OK) {
                fprintf(stderr, "Oshibka ostanova sbora danny`kh: %s\n", X502_GetErrorString(err));

                if (err == X502_ERR_OK)
                    err = stop_err;
            } else {
                printf("Sbor danny`kh ostanovlen uspeshno\n");


            }


        }

        /* закрываем связь с модулем */
        X502_Close(hnd);
        /* освобождаем описатель */
        X502_Free(hnd);

    }
    return err;

}
06.08.2019 15:57:41
#4

Сотрудник "Л Кард"
Здесь с 17.04.2014
Сообщений: 1,292

Re: Чтение данных из E-502

Ну во-первых, в API нумерация каналов идет от нуля (т.е. код 0 - это первый канал, 1 - 2-ой и т.д.), т.е. если у Вас используется 17 канал (как в студии), то номер канала должен быть задан как

 static uint32_t f_channels[ADC_LCH_CNT] = {16}; 

Во-вторых, при записи в файл у Вас идет

 fwrite(adc_data, sizeof(uint32_t), adc_size,mf) 

но adc_data имеет тип массива double, т.е. должно быть

 fwrite(adc_data, sizeof(double), adc_size,mf) 

иначе пишете только половину блока и будут разрывы на границе.

Владислав654654
07.08.2019 02:19:19
#5

Гость

Re: Чтение данных из E-502

Хорошо,  спасибо,  с этим разобрались.

Теперь,  когда программа работает,  она записывает несколько блоков в файл,  причём блоков может быть как 2, так и 50, программа вылетает с кодом ошибки 116.

07.08.2019 10:47:52
#6

Сотрудник "Л Кард"
Здесь с 17.04.2014
Сообщений: 1,292

Re: Чтение данных из E-502

Можете подробнее объяснить, что подразумевается под "вылетает с кодом ошибки 116"? Это код возвращаемый какой-то функцией модуля (если да, то какой, на каком этапе?). При этом если это возвращаемое из функций значение, то все коды ошибки отрицательные, если имеется ввиду -116, то этот код может быть только при конфигурировании устройства...

Контакты

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

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

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

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