/*  Данный пример демонстрирует вывод на ЦАП LTR34 в потоковом режиме.
    Для примера используется генерация синуса.
    Количество каналов задается через определение DAC_CHANNEL_CNT, а параметры
    заданны в массивах sig_amps (Амплитуда в В) и sig_freq (частота в Гц).
    Каждый отсчет вычисляется на основе текущей фазы с рассчетом приращения
    фазы следующей точки и позволяет выводить сигналы периодом не кратным
    периоду дискретизации ЦАП.

    По умолчанию идет работа с первым слотом первого крейта, но это можно изменить
        через параметры командной строки:
    ltr34_stream_sin  slot crate_serial srvip
    где:
        slot         - номер слота (от 1 до 16)
        crate_serial - серийный номер крейта (если крейтов больше одного)
        srvip        - ip-адрес программы ltrd или LtrServer (если он запущен на другой машине)
    Параметры опциональны: можно указать только слот, слот и серийный номер крейта или все

    Сборка в VisualStudio:
    Для того чтобы собрать проект в Visual Studio, измените путь к заголовочным файлам
    (Проект (Project) -> Свойства (Properties) -> Свойства конфигурации (Configuration Properties)
    -> С/С++ -> Общие (General) -> Дополнительные каталоги включения (Additional Include Directories))
    на нужный в зависимаости от пути установки библиотек (ltr34api.h  и остальные заголовочные
    файлы должны находится в поддиректории ltr/include относительно указанного пути)
    и измените путь к .lib файлам на <путь установки библиотек>/lib/msvc
    (Проект (Project) -> Свойства (Properties) -> Свойства конфигурации (Configuration Properties) ->
    Компоновщик (Linker) -> Общие (General) -> Дополнительные каталоги библиотек (Additional Library Directories)).

    Внимание!: Если Вы собираете проект под Visual Studio и у Вас некорректно
    отображается вывод русских букв, то нужно изменить кодировку:
    выберите Файл (File) -> Дополнительные параметры сохранения (Advanced Save Options)...
    и в поле Кодировка (Encoding) выберите Юникод (UTF8, с сигнатурой)/Unicode (UTF-8 with signature)
    и сохраните изменения в файле.
*/

#include "ltr/include/ltr34api.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>

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

#ifndef M_PI
    #define M_PI   3.1415926535897932384626433832795   /* pi */
#endif


#define DAC_BLOCK_SEND_SIZE   5000
#define DAC_PREPARE_SIZE     40000

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

/* параметры выставляемых сигналов (используются тольк первые DAC_CHANNEL_CNT элементов) */
static const double sig_amps[LTR34_DAC_NUMBER_MAX]   = {10.,      2.,   0.,   0.,   0.,   0.,   0.,   0.};
static const double sig_freq[LTR34_DAC_NUMBER_MAX]   = {4000.1, 4000, 4000, 4000, 4000, 4000, 4000, 4000};




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

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



typedef struct {
    int slot;
    const char *serial;
    DWORD addr;
} t_open_param;

/* Разбор параметров командной строки. Если указано меньше, то используются
 * значения по умолчанию:
 * 1 параметр - номер слота (от 1 до 16)
 * 2 параметр - серийный номер крейта
 * 3 параметр - ip-адрес сервера */
static int f_get_params(int argc, char** argv, t_open_param* par) {
    int err = 0;
    par->slot = LTR_CC_CHNUM_MODULE1;
    par->serial = "";
    par->addr = LTRD_ADDR_DEFAULT;


    if (argc > 1)
        par->slot = atoi(argv[1]);
    if (argc > 2)
        par->serial = argv[2];
    if (argc > 3) {
        int a[4],i;
        if (sscanf(argv[3], "%d.%d.%d.%d", &a[0], &a[1], &a[2], &a[3])!=4) {
            fprintf(stderr, "Неверный формат IP-адреса!!\n");
            err = -1;
        }

        for (i=0; (i < 4) && !err; i++) {
            if ((a[i]<0) || (a[i] > 255)) {
                fprintf(stderr, "Недействительный IP-адрес!!\n");
                err = -1;
            }
        }

        if (!err) {
            par->addr = (a[0] << 24) | (a[1]<<16) | (a[2]<<8) | a[3];
        }
    }
    return err;
}

/* Генерация и передача ch_size точек сигнала на канал в модуль.
 * в ch_cur_phases передается массив с фазами синусов по каждому каналу,
 * который изменяется по генерации */
static INT f_send_data(TLTR34 *hnd, unsigned ch_size, double *ch_cur_phases) {

    DWORD snd_size = hnd->ChannelQnt*ch_size;
    INT err=LTR_OK;
    unsigned ch, i;

    double *data = malloc(snd_size*sizeof(data[0]));
    DWORD  *wrds = malloc(snd_size*sizeof(wrds[0]));

    if ((data == NULL) || (wrds == NULL)) {
        err = LTR_ERROR_MEMORY_ALLOC;
    } else {
        /* Генерация сигнала для вывода в виде значений в Вольтах */
        for (ch=0; ch < hnd->ChannelQnt; ch++) {
            for (i=0; i < ch_size; i++) {
                data[hnd->ChannelQnt*i + ch] = sig_amps[ch]*sin(ch_cur_phases[ch]);
                ch_cur_phases[ch] += 2*M_PI *sig_freq[ch]/hnd->FrequencyDAC;
            }
        }

        err = LTR34_ProcessData(hnd, data, wrds, snd_size, TRUE);
        if (err!=LTR_OK) {
            fprintf(stderr, "Ошибка подготовки данных (%d) : %s!\n", err, LTR34_GetErrorString(err));
        }


        if (err == LTR_OK) {
            INT sent_size = 0;

            /* передаем массив, либо пока не передадим все данные, либо пока не
             * произойдет ошибка, либо не будет признака выхода.
             * Передача с небольшим таймаутом в цикле идет для того, чтобы можно было
             * быстрее среагировать на признак выхода. Иначе можно передавать
             * за один Send с достаточным таймаутом
             */
            while (!f_out && (err == LTR_OK) && (sent_size < (INT)snd_size)) {
                INT cur_sent = LTR34_Send(hnd, &wrds[sent_size], snd_size-sent_size, 100);
                if (cur_sent < 0) {
                    err = cur_sent;
                    fprintf(stderr, "Ошибка передачи данных (%d) : %s!\n", err, LTR34_GetErrorString(err));
                } else {
                    sent_size += cur_sent;
                }
#ifdef _WIN32
                /* проверка нажатия клавиши для выхода */
                if (err==LTR_OK) {
                    if (_kbhit())
                        f_out = 1;
                }
#endif
            }
        }
    }
    free(data);
    free(wrds);
    return err;
}



int main(int argc, char** argv) {
    INT err = LTR_OK;
    TLTR34 hltr34;
    t_open_param par;
    /* текущие фазы сигналов, используются для генерации каждого синуса */
    double ch_cur_phases[LTR34_DAC_NUMBER_MAX]    = {0,0,0,0,0,0,0,0};

#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
    /* для вывода русских букв в консоле... */
    setlocale(LC_ALL, "");
#endif


    err = f_get_params(argc, argv, &par);

    LTR34_Init(&hltr34);

    err = LTR34_Open(&hltr34, par.addr, LTRD_PORT_DEFAULT, par.serial, par.slot);
    if (err != LTR_OK) {
        fprintf(stderr, "Не удалось установить соединение с модулем. Ошибка %d: %s\n",
                err, LTR34_GetErrorString(err));
    } else {
        int ch = 0;
        printf("Модуль открыт успешно. Информация о модуле:\n  Название %s\n  Серийный %s\n  Версия ПЛИС %s\n  Максимальное число каналов - %d\n",
               hltr34.ModuleInfo.Name, hltr34.ModuleInfo.Serial, hltr34.ModuleInfo.FPGA_Version, hltr34.ModuleInfo.MaxChannelQnt);
        fflush(stdout);

        hltr34.ChannelQnt = DAC_CHANNEL_CNT;
        for (ch=0; ch < hltr34.ChannelQnt; ch++) {
            hltr34.LChTbl[ch] = LTR34_CreateLChannel(ch + 1, 0);
        }

        LTR34_FindDacFreqDivisor(500000, hltr34.ChannelQnt, &hltr34.FrequencyDivisor, NULL);
        hltr34.AcknowledgeType = LTR34_ACKTYPE_STATUS;
        hltr34.RingMode = FALSE;
        hltr34.UseClb = TRUE;
        hltr34.ExternalStart = FALSE;

        err = LTR34_Reset(&hltr34);
        if (err != LTR_OK) {
            fprintf(stderr, "Не удалось выполнить сброс модуля LTR34. Ошибка %d: %s\n",
                    err, LTR34_GetErrorString(err));
        } else {
            err = LTR34_Config(&hltr34);
            if (err != LTR_OK) {
                fprintf(stderr, "Не удалось сконфигурировать модуль LTR34. Ошибка %d: %s\n",
                        err, LTR34_GetErrorString(err));
            }
        }

        /* вначале подгружаем часть данных в буфер, чтобы на момент запуска
           генерации данные были уже готовы к выдаче */
        if (err == LTR_OK)
            err = f_send_data(&hltr34, DAC_PREPARE_SIZE, ch_cur_phases);


        if (err == LTR_OK) {
            INT stop_err;
            /* запуск генерации */
            err = LTR34_DACStart(&hltr34);
            if (err != LTR_OK) {
                fprintf(stderr, "Не удалось запустить генерацию данных. Ошибка %d: %s\n",
                        err, LTR34_GetErrorString(err));
            }

            if (err == LTR_OK) {
                printf("Запущена генерация данных. Для останова нажмите %s\n",
#ifdef _WIN32
                       "любую клавишу"
#else
                       "CTRL+C"
#endif
                       );
                fflush(stdout);

                /* передаем данные в модуль постоянно до момента прихода
                 * сигнала завершения генерации */
                while (!f_out && (err == LTR_OK)) {
                    err = f_send_data(&hltr34, DAC_BLOCK_SEND_SIZE, ch_cur_phases);
                }
            }


            /* останов генерации данных */
            printf("Останавливаем генерацию...\n");
            fflush(stdout);
            stop_err = LTR34_DACStop(&hltr34);
            if (stop_err == LTR_OK) {
                printf("Останов генерации завершен успешно!\n");
                fflush(stdout);
            } else {
                fprintf(stderr, "Не удалось остановить генерацию данных. Ошибка %d: %s\n",
                        stop_err, LTR34_GetErrorString(stop_err));
                if (err == LTR_OK)
                    err = stop_err;
            }
        }


        LTR34_Close(&hltr34);
    }
    return err;
}
