/* * Данный пример демонстрирует возможность вывода отсчетов сигнала из файла на * вывод в модуль E16 / LTA37 / E502 / L502 (ввод не используется вообще). * * Любое число во входном файле трактуется как значение в Вольтах выхода ЦАП * Два числа разделенные любым символом кроме перевода строки трактуются как значения двух каналов ЦАП * Перевод строки тарактуется как переход к следующему отсчету */ #include "l502api.h" #include "e502api.h" #include #include #include #include #include #include #include #include #include "dev_funcs.h" #include "timespec_funcs.h" #ifndef _WIN32 #include #include #include #include #endif //#define DEBUG #ifdef DEBUG #define dbg_printf(...) fprintf(stderr, __VA_ARGS__) #else #define dbg_printf(...) #endif /* признак необходимости завершить сбор данных */ static volatile int f_out = 0; #define DAC_BUF_SIZE (1024 * 1024) /* таймаут на отправку блока (мс) */ #define SEND_TOUT 500 double dac_freq = 200000; // задержка вывода в секундах между циклами double cycle_delay; // Выводить из файла циклически int cycle_mode = 1; #ifndef _WIN32 /* Обработчик сигнала завершения для Linux */ static void f_abort_handler(int sig) { f_out = 1; } #endif int32_t f_setup_params(t_x502_hnd hnd) { int32_t err; X502_SetSyncMode(hnd, X502_SYNC_INTERNAL); X502_StreamsStop(hnd); X502_StreamsDisable(hnd, X502_STREAM_ALL_IN|X502_STREAM_ALL_OUT); err = X502_SetOutFreq(hnd, &dac_freq); if (err) { fprintf(stderr, "X502_SetOutFreq err=%d dout_freq = %.1f\n", err, dac_freq); } /* записываем настройки в модуль */ if (err == X502_ERR_OK) { err = X502_Configure(hnd, 0); } if (err == X502_ERR_OK) { /* выводим реально установленные значения - те что вернули функции */ fprintf(stderr, "Установленная частота ЦАП: %0.0f Hz\n", dac_freq); } return err; } void parse_params(int argc, char **argv) { for (int i = 1; (int)i < argc - 1; i++) { if (sscanf(argv[i], "dac_freq:%lf", &dac_freq) == 1) { fprintf(stderr, "Запрашиваемая частота ЦАП: %0.0f Hz\n", dac_freq); } else if (sscanf(argv[i], "cycle_delay:%lf", &cycle_delay) == 1) { fprintf(stderr, "cycle_delay=%lf sec\n", cycle_delay); } else if (strcmp(argv[i], "once") == 0) { cycle_mode = 0; fprintf(stderr, "cycle_mode=0\n"); } } } #define BUF_SIZE (64 * 1024) #define CH_ENABLED 2 void show_help(char *name) { fprintf(stderr, "Usage: %s [dac_freq:500000] [cycle_delay:0.0001] [once] input.file\n" "\tdac_freq: DAC freq in Hz\n" "\tcycle_delay: delay DAC output in seconds before new cycle start\n" "Select module by ip: %s [E16:192.168.0.1] [dac_freq:500000] [once] input.file\n" "\nTest input file (2 DAC):\n" "\t0.000000; -5.000000\n" "\t1.000000; -4.000000\n" "\t2.000000; -3.000000\n" "\t3.000000; -2.000000\n" "\t4.000000; -1.000000\n" "\t5.000000; -0.000000\n" "\t4.000000; -1.000000\n" "\t3.000000; -2.000000\n" "\t2.000000; -3.000000\n" "\t1.000000; -4.000000\n" , name, name); } int out_dac_from_file(t_x502_hnd hnd, FILE* f); int main(int argc, char **argv) { int32_t err = 0; uint32_t ver; t_x502_hnd hnd = NULL; FILE* f; #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 /* устанавливаем локаль, чтобы можно было выводить по-русски в CP1251 без перевода в OEM */ setlocale(LC_CTYPE, ""); #endif if (argc == 1) { show_help(argv[0]); return 1; } /********** Получение списка устройств и выбор, с каким будем работать ******************/ hnd = select_dev_from_list(argc, argv, 0); if (hnd == NULL) { fprintf(stderr, "Error! Device not found!\n"); return 1; } parse_params(argc, argv); /* если успешно выбрали модуль и установили с ним связь - продолжаем работу */ err = f_setup_params(hnd); if (err) { return err; } /********************************** Работа с модулем **************************/ /* Разрешаем все каналы на вывод (в этом примере мы не будем использовать асинхронный вывод), а какие каналы реально будут использованы определяем когда подаем данные в L502_PrepareData */ err = X502_StreamsEnable(hnd, X502_STREAM_ALL_OUT); if (err != X502_ERR_OK) { fprintf(stderr, "Ошибка разрешения потоков (%d): %s!", err, X502_GetErrorString(err)); } f = fopen(argv[argc - 1], "rb"); if (!f) { fprintf(stderr, "Could not open file '%s' error %d\n", argv[argc - 1], errno); show_help(argv[0]); return 1; } err = out_dac_from_file(hnd, f); fclose(f); /* закрываем связь с модулем */ X502_Close(hnd); /* освобождаем описатель */ X502_Free(hnd); return err; } static struct timespec start_time; static uint32_t* out_buf; static int dac_size; static double *dac_data[CH_ENABLED]; int send_data(t_x502_hnd hnd, uint32_t *send_ptr, uint32_t send_size) { static int started = 0; static uint32_t g_snd_cnt = 0; struct timespec cur_time; double spent_secs; struct timespec spent_time; while (send_size && !f_out) { int32_t sent; #ifdef _WIN32 /* проверка нажатия клавиши для выхода */ if (_kbhit()) { f_out = 1; } #endif sent = X502_Send(hnd, send_ptr, send_size, SEND_TOUT); if (sent < 0) { fprintf(stderr, "Errror: X502_Send ret = %d\n", sent); return sent; } if (f_out) { return 0; } g_snd_cnt += sent; send_size -= sent; send_ptr += sent; if (started == 0) { int err; started = 1; err = X502_StreamsStart(hnd); if (err) { fprintf(stderr, "Error: X502_StreamsStart ret = %d\n", err); return err; } } } clock_gettime(CLOCK_MONOTONIC, &cur_time); timespec_diff(&cur_time, &start_time, &spent_time); spent_secs = timespec_to_double(&spent_time); if (spent_secs > 5) { fprintf(stderr, "snd speed=%f wrds/sec\n", (g_snd_cnt) / (spent_secs)); g_snd_cnt = 0; start_time = cur_time; } return send_size; } int send_dac_data(t_x502_hnd hnd) { int err; if (dac_size != DAC_BUF_SIZE) { return 0; } #if 0 for (int i = 0; i < dac_size; i++){ fprintf(stderr, "[%d] %f; %f\n", i, dac_data[0][i], dac_data[1][i]); } #endif // TODO: для LTA37 надо использовать PrepareData2() err = X502_PrepareData(hnd, dac_data[0], dac_data[1], NULL, dac_size, X502_DAC_FLAGS_VOLT | X502_DAC_FLAGS_CALIBR, out_buf); if (err) { fprintf(stderr, "Error: X502_PrepareData ret = %d\n", err); return err; } err = send_data(hnd, out_buf, dac_size * CH_ENABLED); dac_size = 0; return err; } int out_dac_from_file(t_x502_hnd hnd, FILE* f) { int32_t err; char *buf; int items_read; double dac_val; char *read_start; int cntr = 0; int dac_ch = 0; bool newline = false; bool comment_start = false; bool data_added = false; char *last_endptr; double last_dac_data[CH_ENABLED]; for (int ch = 0; ch < CH_ENABLED; ch++) { dac_data[ch] = (double*)malloc(sizeof(double) * DAC_BUF_SIZE); if (!dac_data[ch]) { fprintf(stderr, "Out of memory\n"); return 1; } } out_buf = (uint32_t*)malloc(sizeof(uint32_t) * DAC_BUF_SIZE * CH_ENABLED); buf = (char*)malloc(BUF_SIZE); if (!out_buf || !buf) { fprintf(stderr, "Out of memory\n"); return 1; } read_start = buf; last_endptr = &buf[BUF_SIZE]; clock_gettime(CLOCK_MONOTONIC, &start_time); while (!f_out) { size_t buf_ready = (read_start - buf); #ifdef _WIN32 /* проверка нажатия клавиши для выхода */ if (_kbhit()) { f_out = 1; } #endif if (feof(f) && cycle_mode) { int delay_cntr = cycle_delay * dac_freq; do { for (; (dac_size != DAC_BUF_SIZE) && delay_cntr; dac_size++, delay_cntr--) { for (int ch = 0; ch < CH_ENABLED; ch++) { dac_data[ch][dac_size] = last_dac_data[ch]; } } if ((err = send_dac_data(hnd)) < 0) { return err; } } while(delay_cntr); } if (feof(f)) { if (cycle_mode) { fseek(f, 0, SEEK_SET); } else { break; } } items_read = fread(read_start, 1, BUF_SIZE - buf_ready, f); dbg_printf("read_start=::::%.*s::::\n", (int)buf_ready, buf); dbg_printf("read=%d '''''%.*s'''''\n", items_read, items_read, read_start); for (char *ptr = buf, *endptr = buf; ; ptr = endptr) { char *buf_end = &buf[buf_ready + items_read]; dac_val = strtod(ptr, &endptr); if (endptr >= buf_end) { memmove(buf, last_endptr, buf_end - last_endptr); read_start = buf + (buf_end - last_endptr); break; } else if (endptr == ptr) { endptr++; if (*ptr == '\n') { newline = true; comment_start = false; } if (*ptr == '#') { comment_start = true; } } else { last_endptr = endptr; for (char *n_ptr = ptr; n_ptr < endptr; n_ptr++) { if (*n_ptr == '\n') { newline = true; comment_start = false; break; } } if (comment_start) { continue; } if (newline) { dac_ch = 0; } if (dac_ch >= CH_ENABLED) { fprintf(stderr, "dac_ch = %d >= %d (CH_ENABLED)\n", dac_ch, CH_ENABLED); dac_ch %= CH_ENABLED; } if (newline && data_added) { data_added = false; dac_size++; for (int ch = 0; ch < CH_ENABLED; ch++) { dac_data[ch][dac_size] = dac_data[ch][dac_size - 1]; } } data_added = true; newline = false; dbg_printf("dac_val=%lf cntr=%d\n", dac_val, cntr); if ((err = send_dac_data(hnd)) < 0) { return err; } dac_data[dac_ch][dac_size] = dac_val; last_dac_data[dac_ch] = dac_val; dac_ch++; } } } return 0; }