#include "ltr22api.h"
#include "ltrmodule.h"
#include "ltimer.h"
#include <math.h>
#include <stdlib.h>
#include <string.h>


#define LTR22_SRC_CLK_FREQ 20000000

#define LTR22CMD_MEASURE_ADC_ZERO                       (LTR010CMD_INSTR|1)
#define LTR22CMD_AC_AC_DC_STATE                         (LTR010CMD_INSTR|2)
#define LTR22CMD_SET_ADC_RANGE                          (LTR010CMD_INSTR|3)

#define LTR22CMD_SET_ADC_MAIN_DIV                       (LTR010CMD_INSTR|4)
#define LTR22CMD_SET_ADC_EXTRA_DIV                      (LTR010CMD_INSTR|5)

#define LTR22CMD_GET_MODULE_CONFIG                      (LTR010CMD_INSTR|6)
#define LTR22CMD_GET_CALIBR_COEFS                       (LTR010CMD_INSTR|7)

#define LTR22CMD_GET_MODULE_DESCRIPTION                 (LTR010CMD_INSTR|8)

#define LTR22CMD_PREPARE_READ_WRITE_EEPROM_ADDRESS      (LTR010CMD_INSTR|9)
#define LTR22CMD_WRITE_EEPROM                           (LTR010CMD_INSTR|10)
#define LTR22CMD_READ_EEPROM                            (LTR010CMD_INSTR|11)

#define LTR22CMD_SET_ADC_CHANNEL                        (LTR010CMD_INSTR|12)

#define LTR22CMD_SET_ADC1234_REGISTER                   (LTR010CMD_INSTR|14)
#define LTR22CMD_SET_ADC_FREQ_REGISTER                  (LTR010CMD_INSTR|15)
#define LTR22CMD_SET_ADC_SCOPE_REGISTER                 (LTR010CMD_INSTR|16)
#define LTR22CMD_SET_ADC_CH_SYNC_REGISTER               (LTR010CMD_INSTR|17)

#define LTR22CMD_SYNC_PHAZE                             (LTR010CMD_INSTR|18)
#define LTR22CMD_SET_SYNC_PRIORITY                      (LTR010CMD_INSTR|19)


#define LTR22CMD_ACK_ADC1234_REGISTER                   (31)
#define LTR22CMD_ACK_ADC_FREQ_REGISTER                  (32)
#define LTR22CMD_ACK_ADC_SCOPE_REGISTER                 (33)
#define LTR22CMD_ACK_ADC_CH_SYNC_REGISTER               (34)


#define LTR22CMD_START_ADC                              (LTR010CMD_INSTR|40)
#define LTR22CMD_STOP_ADC                               (LTR010CMD_INSTR|41)

#define LTR22CMD_TEST_RUNNING_UNIT                      (LTR010CMD_INSTR|50)
#define LTR22CMD_TEST_RUNNING_ZERO                      (LTR010CMD_INSTR|51)
#define LTR22CMD_TEST_ECHO                              (LTR010CMD_INSTR|52)

#define LTR22CMD_GET_ECHO                               (LTR010CMD_INSTR|63)

#define LTR22_RANGE_OVERFLOW 7

#define TIMEOUT_CMD_SEND                                2000
#define TIMEOUT_CMD_RECIEVE                             4000

#define LTR22_CLEAR_BUFFER_TOUT        5000

#define RBUF_SIZE (sizeof(rbuf)/sizeof(rbuf[0]))
#define SBUF_SIZE (sizeof(sbuf)/sizeof(sbuf[0]))

#define LTR22_CONFIG_SIZE           4800
#define LTR22_DESCR_SIZE             128
#define LTR22_INTERNAL_AVR_BUFFER    100
#define LTR22_EEPROM_SIZE            512


static const TLTR_ERROR_STRING_DEF f_err_tbl[] = {
    {LTR22_ERROR_SEND_DATA,  "Данные не отослались"},
    {LTR22_ERROR_RECV_DATA, "Данные не пришли"},
    {LTR22_ERROR_NOT_LTR22, "Заданный модуль - не модуль LTR22"},
    {LTR22_ERROR_OVERFLOW, "Буффер в крейте переполнен"},
    {LTR22_ERROR_ADC_RUNNING, "Невозможно выполнить, пока запущен сбор данных"},
    {LTR22_ERROR_MODULE_INTERFACE, "Ошибка интерфейса обмена коммандами с модулем"},
    {LTR22_ERROR_INVALID_FREQ_DIV, "Недопустимый делитель частоты"},
    {LTR22_ERROR_INVALID_TEST_HARD_INTERFACE, "Ошибка тестирования интерфейса модуля с ПЛИС"},
    {LTR22_ERROR_INVALID_DATA_RANGE_FOR_THIS_CHANNEL,
     "Несовпадают диапазоны каналов между полученными данными и текущими значениями"},
    {LTR22_ERROR_INVALID_DATA_COUNTER, "Счетчик полученных данных - неверен"},
    {LTR22_ERROR_PRERARE_TO_WRITE, "Ошибка подготовка AVR к записи"},
    {LTR22_ERROR_WRITE_AVR_MEMORY, "Ошибка записи памяти AVR"},
    {LTR22_ERROR_READ_AVR_MEMORY, "Ошибка чтения памяти AVR"},
    {LTR22_ERROR_PARAMETERS, "Неправильные параметры в функции LTR22"},
    {LTR22_ERROR_CLEAR_BUFFER_TOUT, "Очистка буфера завершилась по таймауту"},
    {LTR22_ERROR_SYNC_FHAZE_NOT_STARTED, "Не запущена синхронизация фазы АЦП"},
    {LTR22_ERROR_INVALID_CH_NUMBER, "Неверный номер канала АЦП в принятых данных"},
    {LTR22_ERROR_AVR_MEMORY_COMPARE, "Прочитанные данные из памяти AVR не соответствуют записанным"}
};

LTR22API_Export const INT LTR22_DISK_FREQ_ARRAY[LTR22_ADC_FREQ_CNT] = {
    3472,  3720,   4006,  4340,  4734,  5208,  5580,  5787,
    6009,  6510,   7102,  7440,  7812,  8680,  9765,  10416,
    11160, 13020, 15625,  17361, 19531, 26041, 39062, 52083,
    78125
};

static struct {
    BYTE Fdiv_rg;                        // делитель частоты клоков 1..15
    BOOLEAN Adc384;
} f_adc_freq_params[LTR22_ADC_FREQ_CNT] = {
    {15, TRUE}, //45
    {14, TRUE}, //42
    {13, TRUE}, //39
    {12, TRUE}, //36
    {11, TRUE}, //33
    {10, TRUE}, //30
    {14, FALSE},//28
    {9,  TRUE}, //27
    {13, FALSE},//26
    {8,  TRUE}, //24
    {11, FALSE},//22
    {7,  TRUE}, //21
    {10, FALSE},//20
    {6,  TRUE}, //18
    {8,  FALSE},//16
    {5,  TRUE}, //15
    {7,  FALSE},//14
    {4,  TRUE}, //12
    {5,  FALSE},//10
    {3,  TRUE}, //9
    {4,  FALSE},//8
    {2,  TRUE},//6
    {2,  FALSE},//4
    {1,  TRUE}, //3
    {1,  FALSE} //2
};

#ifdef _WIN32
BOOL APIENTRY DllMain( HMODULE hModule, DWORD  ul_reason_for_call, LPVOID lpReserved) {
    switch (ul_reason_for_call) {
        case DLL_PROCESS_ATTACH:
        case DLL_THREAD_ATTACH:
        case DLL_THREAD_DETACH:
        case DLL_PROCESS_DETACH:
        break;
    }
    return TRUE;
}
#endif




static INT f_send_cmd_with_ack(TLTR22 *module, const DWORD *cmd, DWORD *acks, DWORD cmd_cnt) {
    INT res = ltr_module_send_cmd(&module->Channel, cmd, cmd_cnt);
    /* ответ принимаем только если не запущен сбор данных с АЦП */
    if (!module->DataReadingProcessed) {
        if (res == LTR_OK) {
            res = ltr_module_recv_cmd_resp(&module->Channel, acks, cmd_cnt);
        }
        if (res == LTR_OK) {
            DWORD i;
            for (i=0; (i < cmd_cnt) && (res==LTR_OK); i++) {
                if ((acks[i]>>16)!=(cmd[i]&0x3f)) {
                    res = LTR22_ERROR_MODULE_INTERFACE;
                }
            }
        }
    }
    return res;
}

/* тестирование новой частоты - на правильность */
static INT f_test_freq(TLTR22 *module, BOOLEAN adc384, BYTE Freq_dv) {
    float const UpFreq = 20840000.0, DownFreq = 1280000.0;
    float ClkFreq;
    INT res = Freq_dv ? LTR_OK : LTR22_ERROR_INVALID_FREQ_DIV;

    if (res == LTR_OK) {
        ClkFreq=(float)LTR22_SRC_CLK_FREQ/Freq_dv;

        if ((ClkFreq < DownFreq) && (ClkFreq > UpFreq))
            res = LTR22_ERROR_INVALID_FREQ_DIV;
        if (res == LTR_OK) {
            int fnd, i, FreqDisc;
            FreqDisc=(int)(ClkFreq/(128.0f*(adc384 ? 3 : 2)));
            for (i=0, fnd=FALSE; !fnd && (i<LTR22_ADC_FREQ_CNT); i++) {
                if (FreqDisc==LTR22_DISK_FREQ_ARRAY[i]) {
                    module->FreqDiscretizationIndex=i;
                    fnd = TRUE;
                }
            }
            if (!fnd)
                res = LTR22_ERROR_INVALID_FREQ_DIV;
        }
    }
    return res;
}

static void f_info_init(TLTR22 *hnd) {
    unsigned f,i;

    memset(&hnd->ModuleInfo, 0, sizeof(hnd->ModuleInfo));
    strcpy((CHAR*)hnd->ModuleInfo.Description.DeviceName, "LTR22");

    // первоначальные калибровки для того, чтобы преобразовать в вольты
    for (f = 0; f < LTR22_ADC_FREQ_CNT; ++f) {
        for (i = 0; i < LTR22_CHANNEL_CNT; ++i) {

            hnd->ADCCalibration[i][f].FactoryCalibScale[0] = 1;
            hnd->ADCCalibration[i][f].FactoryCalibScale[1] = 1;
            hnd->ADCCalibration[i][f].FactoryCalibScale[2] = 1;
            hnd->ADCCalibration[i][f].FactoryCalibScale[3] = 1;
            hnd->ADCCalibration[i][f].FactoryCalibScale[4] = 1;
            hnd->ADCCalibration[i][f].FactoryCalibScale[5] = 1;

            hnd->ADCCalibration[i][f].UserCalibScale[0] = (float)(2./65536.0);
            hnd->ADCCalibration[i][f].UserCalibScale[1] = (float)(0.6/65536.0);
            hnd->ADCCalibration[i][f].UserCalibScale[2] = (float)(0.2/65536.0);
            hnd->ADCCalibration[i][f].UserCalibScale[3] = (float)(0.06/65536.0);
            hnd->ADCCalibration[i][f].UserCalibScale[4] = (float)(20./65536.0);
            hnd->ADCCalibration[i][f].UserCalibScale[5] = (float)(6./65536.0);
        }
    }

    hnd->DataReadingProcessed = FALSE;
}


LTR22API_DllExport(INT) LTR22_Init(TLTR22 *hnd) {
    INT err = hnd==NULL ? LTR_ERROR_INVALID_MODULE_DESCR : LTR_OK;
    if (err == LTR_OK) {
        memset(hnd,0, sizeof(TLTR22));
        err = LTR_Init(&hnd->Channel);
        if (err == LTR_OK) {
            unsigned i;

            hnd->size = sizeof(TLTR22);
            hnd->MeasureADCZero = FALSE;
            hnd->AC_DC_State = TRUE;
            hnd->Fdiv_rg = 4;
            hnd->FreqDiscretizationIndex = 1;

            // в начале все каналы включены
            for (i = 0; i < LTR22_CHANNEL_CNT; ++i) {
                hnd->ChannelEnabled[i] = TRUE;
                hnd->ADCChannelRange[i] = LTR22_ADC_RANGE_10;
            }

            // синхронизация - внутренняя
            hnd->SyncType=0;

            f_info_init(hnd);
        }
    }
    return err;
}


LTR22API_DllExport(INT) LTR22_Close(TLTR22 *module) {
    INT err = module==NULL ? LTR_ERROR_INVALID_MODULE_DESCR : LTR_OK;
    if (err == LTR_OK) {
        INT close_err;
        if (module->DataReadingProcessed)
            err = LTR22_StopADC(module);
        close_err=LTR_Close(&module->Channel);
        if (err == LTR_OK)
            err = close_err;
    }
    return err;
}

LTR22API_DllExport(INT) LTR22_Open(TLTR22 *module, DWORD saddr, WORD sport, const CHAR *csn, WORD cc) {
    DWORD flags = 0;
    INT warning = LTR_OK;
    INT res = module == NULL ? LTR_ERROR_INVALID_MODULE_DESCR : LTR_OK;
    if (res==LTR_OK) {
        res = ltr_module_open(&module->Channel, saddr, sport, csn, cc,
                              LTR_MID_LTR22, &flags, NULL, &warning);
    }

    if (res == LTR_OK) {
        f_info_init(module);

        if (!(flags & LTR_MOPEN_OUTFLAGS_DONT_INIT)) {
            /* ждем таймаут, пока завершится автокалибровка АЦП, так как
               в этот период все команды игнорируются*/
            LTRAPI_SLEEP_MS(250);

            /* получаем информацию о модуле  */
            res = LTR22_GetCalibrCoeffs(module);
            if (res == LTR_OK) {
                res=LTR22_GetModuleDescription(module);
                if(res==LTR_OK) {
                    res=LTR22_GetConfig(module);
                }
            }
        }
    }
    return res == LTR_OK ? warning : res;
}

LTR22API_DllExport(INT) LTR22_IsOpened(TLTR22 *module) {
    return module == NULL ? LTR_ERROR_INVALID_MODULE_DESCR : LTR_IsOpened(&module->Channel);
}



/* Чтение текущей конфигурации модуля и заполнение полей описателя в соответствии
   с полученными занчениями */
LTR22API_DllExport(INT) LTR22_GetConfig(TLTR22 *module) {
    INT res = LTR22_IsOpened(module);
    if ((res == LTR_OK) && module->DataReadingProcessed)
        res = LTR22_ERROR_ADC_RUNNING;

    if (res == LTR_OK) {
        DWORD Command = LTR22CMD_GET_MODULE_CONFIG, ack;
        res = f_send_cmd_with_ack(module, &Command, &ack, 1);
    }

    if (res == LTR_OK) {
        DWORD rbuf[4];
        res = ltr_module_recv_cmd_resp(&module->Channel, rbuf, RBUF_SIZE);
        if (res == LTR_OK) {
            if (((rbuf[0]&0xff)==LTR22CMD_ACK_ADC1234_REGISTER)&& // команда данных по статусу
                ((rbuf[1]&0xff)==LTR22CMD_ACK_ADC_FREQ_REGISTER)&& // команда данных по частоте
                ((rbuf[2]&0xff)==LTR22CMD_ACK_ADC_SCOPE_REGISTER)&& // команда данных по ограничению для каждого канала
                ((rbuf[3]&0xff)==LTR22CMD_ACK_ADC_CH_SYNC_REGISTER)) { // команда настройки синхронизации и включения АЦП

                int i;
                /* если совпадают все коды команд, то сохраняем полученную
                   в ответных командах информацию в полях описателя модуля */
                module->AC_DC_State=(rbuf[0]>>(16+0))&1;
                module->MeasureADCZero=!((rbuf[0]>>(16+1))&1);
                module->SyncMaster=(rbuf[0]>>(16+3))&1;
                module->DataReadingProcessed=((rbuf[0]>>(16+7))&1);


                module->Adc384=(rbuf[1]>>(16+7))&1;
                module->Fdiv_rg=(BYTE)((rbuf[1]>>16)&0xf);

                for (i = 0; i < LTR22_CHANNEL_CNT; i++) {
                    module->ADCChannelRange[i]=(BYTE)((rbuf[2]>>(16+(i*4)))&0xf);
                    module->ChannelEnabled[i]=(BYTE)((rbuf[3]>>(16+i))&0x1);
                }

                module->SyncType=(BYTE)((rbuf[3]>>(16+4))&0x3);
            } else {
                res = LTR22_ERROR_MODULE_INTERFACE;
            }
        }
    }
    return res;
}

/*  Записываем настройки всего устройства в модуль разом */
LTR22API_DllExport(INT) LTR22_SetConfig(TLTR22 *module) {
    INT res = LTR22_IsOpened(module);

    if ((res == LTR_OK) && module->DataReadingProcessed)
        res = LTR22_ERROR_ADC_RUNNING;

    if (res == LTR_OK)
        res = f_test_freq(module, module->Adc384, module->Fdiv_rg);

    if(res == LTR_OK) {
        int i;
        DWORD sbuf[4], rbuf[4];
        int ADC1234_Register=0, ADC_Freq_Register=0, ADC_Scope_Register=0, ADC_Ch_Sync_Register=0;

        ADC1234_Register=(((DWORD)module->AC_DC_State)) |
            ((!module->MeasureADCZero)<<1) |
            ((module->SyncMaster)<<3) |
            ((module->DataReadingProcessed)<<7);

        ADC_Freq_Register=    (((DWORD)module->Fdiv_rg)) |
            ((module->Adc384)<<7);

        for (i = 0; i < LTR22_CHANNEL_CNT; i++) {
            ADC_Scope_Register = ADC_Scope_Register | (module->ADCChannelRange[i]<<(i*4));
            ADC_Ch_Sync_Register = ADC_Ch_Sync_Register | (module->ChannelEnabled[i]<<i);
        }

        ADC_Ch_Sync_Register = ADC_Ch_Sync_Register | (module->SyncType<<4);

        sbuf[0] = LTR22CMD_SET_ADC1234_REGISTER | (ADC1234_Register<<16);
        sbuf[1] = LTR22CMD_SET_ADC_FREQ_REGISTER | (ADC_Freq_Register<<16);
        sbuf[2] = LTR22CMD_SET_ADC_SCOPE_REGISTER | (ADC_Scope_Register<<16);
        sbuf[3] = LTR22CMD_SET_ADC_CH_SYNC_REGISTER | (ADC_Ch_Sync_Register<<16);

        res = f_send_cmd_with_ack(module, sbuf, rbuf, SBUF_SIZE);
    }

    return res;
}

/*  Очистка внутреннего буффера LTR */
LTR22API_DllExport(INT) LTR22_ClearBuffer(TLTR22 *module, BOOLEAN wait_response) {
    INT res = LTR22_IsOpened(module);

    /* если ждем ответ - то как обычная команда стоп, только без посылки команды */
    if (wait_response) {
        const DWORD ack_code = 0xf2f380FE;
        DWORD recv_ack;
        res = ltr_module_stop(&module->Channel, NULL, 0, ack_code, 0, 0, &recv_ack);
        /* так как LTR_ModuleStop проверяет только код команды в ответе,
           а нам по хорошему надо проверить и данные - дополнительно проверяем
           на полное совпадение */
        if ((res==LTR_OK) && ((recv_ack&0xfffff0ff) != ack_code)) {
            res = LTR22_ERROR_MODULE_INTERFACE;
        }
    } else {
        /* если очистка без подтверждения - то просто вычитываем данные, пока
           не примем 0 байт. При этом сделан таймаут, чтобы не остаться в
           цикле до бесконечности */
        t_ltimer tmr;
        DWORD rbuf[400];
        BOOLEAN zero_return = FALSE;

        ltimer_set(&tmr, LTIMER_MS_TO_CLOCK_TICKS(LTR22_CLEAR_BUFFER_TOUT));

        while ((res == LTR_OK) && !zero_return && !ltimer_expired(&tmr)) {
            res = LTR_Recv(&module->Channel, rbuf, NULL, sizeof(rbuf)/sizeof(rbuf[0]), 100);
            if (res == 0)
                zero_return = TRUE;
        }

        if ((res == LTR_OK) && (!zero_return))
            res = LTR22_ERROR_CLEAR_BUFFER_TOUT;
    }
    return res;
}

/* установка мастер/слейв */
LTR22API_DllExport(INT) LTR22_SetSyncPriority(TLTR22 *module, BOOLEAN SyncMaster) {
    INT res = LTR22_IsOpened(module);
    if (res == LTR_OK) {
        DWORD ack, Command=LTR22CMD_SET_SYNC_PRIORITY;
        if (SyncMaster)
            Command |= (1<<16);
        res = f_send_cmd_with_ack(module, &Command, &ack, 1);
    }

    if (res == LTR_OK) {
        module->SyncMaster=SyncMaster;
    }
    return res;
}


/* Запуск процесса фазировки, посылается запрос на фазировку.
   При этом второй ответ не ождается. Следующей функцией должна быть
   LTR22_SyncPhazeWaitDone() для ожидания завершения фазировки */
LTR22API_DllExport(INT) LTR22_SyncPhazeStart(TLTR22 *module) {
    INT res = LTR22_IsOpened(module);
    if ((res==LTR_OK) && module->DataReadingProcessed)
        res = LTR22_ERROR_ADC_RUNNING;

    if (res == LTR_OK) {
        DWORD ack, command=LTR22CMD_SYNC_PHAZE;
        res = f_send_cmd_with_ack(module, &command, &ack, 1);
    }

    if (res == LTR_OK) {
        module->SyncType=1;
    }
    return res;
}

/* Ошидание завершения фазировки. По завершению модуль передает второе подтверждение,
  которые и ожидаем. Должно вызываться после LTR22_SyncPhazeStart() */
LTR22API_DllExport(INT) LTR22_SyncPhazeWaitDone(TLTR22 *module, DWORD timeout) {
    INT res = LTR22_IsOpened(module);
    if ((res == LTR_OK) && module->DataReadingProcessed)
        res = LTR22_ERROR_ADC_RUNNING;

    if ((res == LTR_OK) && (module->SyncType != 1))
        res = LTR22_ERROR_SYNC_FHAZE_NOT_STARTED;

    if (res == LTR_OK) {
        DWORD ack;
        res = LTR_Recv(&module->Channel, &ack, NULL, 1, timeout);
        if (res == 0) {
            res = LTR_ERROR_NO_CMD_RESPONSE;
        } else if (res == 1) {
            if ((ack >> 16) != (LTR22CMD_SYNC_PHAZE & 0x3f)) {
                res = LTR22_ERROR_MODULE_INTERFACE;
            } else {
                res = LTR_OK;
            }
        }
    }
    return res;
}


/*  Запуск процесса синхронизации фаз между модулями. Если модуль является
    мастером то генерирует дополнительно сигнал на выходе.
    В любом случае модуль ждет сигнала на входе, после чего ждет пока АЦП
    синхронизцется и после этого выдает второе подтверждение команды */
LTR22API_DllExport(INT) LTR22_SyncPhaze(TLTR22 *module, DWORD timeout) {
    INT res = LTR22_SyncPhazeStart(module);
    if (res == LTR_OK)
        res = LTR22_SyncPhazeWaitDone(module, timeout);
    return res;
}

/* Запуск сбора данных с АЦП */
LTR22API_DllExport(INT) LTR22_StartADC(TLTR22 *module, BOOLEAN WaitSync) {
    INT res = LTR22_IsOpened(module);
    if ((res == LTR_OK) && module->DataReadingProcessed)
        res = LTR22_ERROR_ADC_RUNNING;
    if (res == LTR_OK) {
        DWORD ack, cmd = LTR22CMD_START_ADC;

        /* если установлена внешняя синхронизации - устанавливаем соотвественным
          обарзом поле SyncType */
        if (WaitSync) {
            module->SyncType = 2;
            cmd |= (1<<16);
        }
        res = f_send_cmd_with_ack(module, &cmd, &ack, 1);
    }

    if (res == LTR_OK) {
        module->DataReadingProcessed=TRUE;
    }
    return res;
}

LTR22API_DllExport(INT) LTR22_StopADC(TLTR22 *module) {
    INT res = LTR22_IsOpened(module);
    if (res == LTR_OK) {
        DWORD ack, cmd = LTR22CMD_STOP_ADC;
        /** @note если идет сбор данных то ответ не анализируется, так как
            раельно ответ придет только после слов данных, которые сейчас в буфере.
            если сбора нет, то ответ вычитывается и проверяется.
            Это соответствует стандартной обработке команд от LTR22*/
        res = f_send_cmd_with_ack(module, &cmd, &ack, 1);
    }

    if (res == LTR_OK) {
        module->DataReadingProcessed=FALSE;
    }
    return res;
}

/*  Включение/выключение измерения нуля АЦП (TRUE - включение FALSE - выключение) */
LTR22API_DllExport(INT) LTR22_SwitchMeasureADCZero(TLTR22 *module, BOOLEAN SetMeasure) {
    INT res = LTR22_IsOpened(module);
    if ((res == LTR_OK) && module->DataReadingProcessed)
        res = LTR22_ERROR_ADC_RUNNING;
    if (res == LTR_OK) {
        DWORD ack, cmd = LTR22CMD_MEASURE_ADC_ZERO;
        /* в команде инвертированный признак измерения нуля */
        if (!SetMeasure)
            cmd |= (1<<16);
        res = f_send_cmd_with_ack(module, &cmd, &ack, 1);
    }

    if (res == LTR_OK) {
        module->MeasureADCZero=SetMeasure;
    }

    return res;
}

/* Установка частоты клоков АЦП с посощью основного делителя 0..16 и
    дополнительного (TRUE = 3 FALSE =2)
    частота Fclk=20*10^6/adc384*FREQ_dv
    частота должна быть в пределах 1.28-20.84 МГц
*/
LTR22API_DllExport(INT) LTR22_SetFreq(TLTR22 *module, BOOLEAN adc384, BYTE Freq_dv) {
    INT res = LTR22_IsOpened(module);
    if ((res == LTR_OK) && module->DataReadingProcessed)
        res = LTR22_ERROR_ADC_RUNNING;
    if (res == LTR_OK)
        res=f_test_freq(module, adc384, Freq_dv);

    if (res == LTR_OK) {
        DWORD sbuf[2], rbuf[2];
        sbuf[0]=LTR22CMD_SET_ADC_MAIN_DIV|(Freq_dv<<16);
        sbuf[1]=LTR22CMD_SET_ADC_EXTRA_DIV;
        if (adc384)
            sbuf[1] |= (1<<16);
        res = f_send_cmd_with_ack(module, sbuf, rbuf, 2);
    }

    if (res == LTR_OK) {
        module->Adc384=adc384;
        module->Fdiv_rg=Freq_dv;
    }

    return res;
}

LTR22API_DllExport(INT) LTR22_FindAdcFreqParams(double adcFreq, BYTE *divider, BOOLEAN *adc384,
                                                INT *adcFreqIndex, double *resultAdcFreq) {
    INT freq_idx;
    INT fnd_freq_idx = -1;
    double fnd_df= 0;

    for (freq_idx = 0; freq_idx < LTR22_ADC_FREQ_CNT; freq_idx++) {
        double df = fabs(LTR22_DISK_FREQ_ARRAY[freq_idx] - adcFreq);
        if ((fnd_freq_idx < 0) || (df < fnd_df)) {
            fnd_freq_idx = freq_idx;
            fnd_df = df;
        }
    }

    if (divider)
        *divider = f_adc_freq_params[fnd_freq_idx].Fdiv_rg;
    if (adc384)
        *adc384 = f_adc_freq_params[fnd_freq_idx].Adc384;
    if (adcFreqIndex)
        *adcFreqIndex = fnd_freq_idx;
    if (resultAdcFreq) {
        *resultAdcFreq = LTR22_CalcAdcFreq(f_adc_freq_params[fnd_freq_idx].Fdiv_rg,
                                           f_adc_freq_params[fnd_freq_idx].Adc384);
    }
    return  LTR_OK;
}

LTR22API_DllExport(double) LTR22_CalcAdcFreq(BYTE freqDiv, BOOLEAN adc384) {
    return freqDiv == 0 ? 0 : (double)LTR22_SRC_CLK_FREQ/(128 * freqDiv * (adc384 ? 3 : 2));
}


/* Переключение режимов TRUE =AC+DC FALSE=AC */
LTR22API_DllExport(INT) LTR22_SwitchACDCState(TLTR22 *module, BOOLEAN ACDCState) {
    INT res = LTR22_IsOpened(module);
    if (res == LTR_OK) {
        DWORD ack, cmd =LTR22CMD_AC_AC_DC_STATE;
        if (ACDCState)
            cmd |= (1<<16);
        res = f_send_cmd_with_ack(module, &cmd, &ack, 1);
    }

    if (res == LTR_OK) {
        module->AC_DC_State=ACDCState;
    }
    return res;
}

/*
    Установка диапазона для канала АЦП.
    Значения диапазонов:
    0 - 1В 1 - 0.3В 2 - 0.1В 3 - 0.03В 4 - 10В 5 - 3В
    */
LTR22API_DllExport(INT) LTR22_SetADCRange(TLTR22 *module, BYTE ADCChannel, BYTE ADCChannelRange) {
    INT res = LTR22_IsOpened(module);
    /* проверка на правильне параметры */
    if ((res==LTR_OK) && ((ADCChannelRange>5) || (ADCChannel>3)))
        res = LTR_ERROR_PARAMETERS;
    if (res == LTR_OK) {
        DWORD ack, cmd=LTR22CMD_SET_ADC_RANGE|
                    (((DWORD)ADCChannel)<<24)|
                    (((DWORD)ADCChannelRange)<<16);
        res = f_send_cmd_with_ack(module, &cmd, &ack, 1);
    }

    if (res == LTR_OK) {
        module->ADCChannelRange[ADCChannel]=ADCChannelRange;
    }
    return res;
}

/*  Включение/выключение канала АЦП */
LTR22API_DllExport(INT) LTR22_SetADCChannel(TLTR22 *module, BYTE ADCChannel, BOOLEAN EnableADC) {
    INT res = LTR22_IsOpened(module);
    /* проверка на правильне параметры */
    if ((res == LTR_OK) && (ADCChannel > 3))
        res = LTR_ERROR_PARAMETERS;
    if (res == LTR_OK) {
        DWORD ack, cmd = LTR22CMD_SET_ADC_CHANNEL | (((DWORD)ADCChannel)<<24);
        if (EnableADC)
            cmd |= (1<<16);
        res = f_send_cmd_with_ack(module, &cmd, &ack, 1);
    }

    if (res == LTR_OK) {
        module->ChannelEnabled[ADCChannel]=EnableADC;
    }

    return res;
}


/*******************************************************************************
    Считывание значений калибровочных коэффициентов и запись их в
    структуру описателя модуля
*******************************************************************************/
LTR22API_DllExport(INT) LTR22_GetCalibrCoeffs(TLTR22 *module) {
    INT res = LTR22_IsOpened(module);
    if ((res==LTR_OK) && (module->DataReadingProcessed))
        res = LTR22_ERROR_ADC_RUNNING;

    if (res == LTR_OK) {
        DWORD Command=LTR22CMD_GET_CALIBR_COEFS;
        res = ltr_module_send_cmd(&module->Channel, &Command, 1);
    }

    if (res == LTR_OK) {
        DWORD *rbuf = malloc((LTR22_CONFIG_SIZE+1)*sizeof(rbuf[0]));
        char *TempConfArray = malloc(LTR22_CONFIG_SIZE);
        if ((rbuf == NULL) || (TempConfArray == NULL))
            res = LTR_ERROR_MEMORY_ALLOC;

        if (res == LTR_OK)
            res = ltr_module_recv_cmd_resp(&module->Channel, rbuf, LTR22_CONFIG_SIZE + 1);
        if ((res == LTR_OK) && ((rbuf[0]>>16)!=(LTR22CMD_GET_CALIBR_COEFS&0x3f))) {
            res = LTR22_ERROR_MODULE_INTERFACE;
        }

        if (res == LTR_OK) {
            /* проверка на соответствие принятых значений тому, что должно быть
               и преобразование их байтовый массив */

            int i;
            for(i = 0; (res == LTR_OK) && (i < LTR22_CONFIG_SIZE); i++) {
                TempConfArray[i]= ((rbuf[i+1] >> 16) & 0xff);
                if (((rbuf[i+1] >> 24) & 0xff) != (BYTE)i)
                    res = LTR22_ERROR_MODULE_INTERFACE;
            }

            if (res == LTR_OK) {
                float *pcfg = (float *)TempConfArray;
                int ADCCount;
                for (ADCCount=0; ADCCount < LTR22_CHANNEL_CNT; ADCCount++) {
                    int FreqCount;
                    for (FreqCount = 0; FreqCount < LTR22_ADC_FREQ_CNT; FreqCount++) {
                        int RangeCount;
                        // занесение данных в калибровочную структуру
                        for(RangeCount = 0; RangeCount < LTR22_RANGE_CNT; RangeCount++) {
                            module->ADCCalibration[ADCCount][FreqCount].FactoryCalibOffset[RangeCount]=*pcfg++;
                            module->ADCCalibration[ADCCount][FreqCount].FactoryCalibScale[RangeCount]=*pcfg++;
                        }
                    }
                }
            }
        }

        free(rbuf);
        free(TempConfArray);
    }
    return res;
}

/* получение данных из модуля */
LTR22API_DllExport(INT) LTR22_Recv(TLTR22 *module, DWORD *data, DWORD *tstamp,
                                   DWORD size, DWORD timeout) {
    int res = LTR22_IsOpened(module);
    if (res == LTR_OK)
        res=LTR_Recv(&module->Channel, data, tstamp, size, timeout);
    if ((res >= 0) && (module->Channel.flags & LTR_FLAG_RBUF_OVF))
        res = LTR_ERROR_RECV_OVERFLOW;
    return res;
}


/* получение описания модуля из flash и заполнение структуры
    module->ModuleInfo.Description */
LTR22API_DllExport(INT) LTR22_GetModuleDescription(TLTR22 *module) {
    int res = LTR22_IsOpened(module);
    if ((res==LTR_OK) && module->DataReadingProcessed)
        res = LTR22_ERROR_ADC_RUNNING;
    if (res == LTR_OK) {
        DWORD ack, Command = LTR22CMD_GET_MODULE_DESCRIPTION;
        res = f_send_cmd_with_ack(module, &Command, &ack, 1);
    }

    if (res == LTR_OK) {
        DWORD rbuf[LTR22_DESCR_SIZE];
        res = ltr_module_recv_cmd_resp(&module->Channel, rbuf, RBUF_SIZE);
        if (res == LTR_OK) {
            /* проверка на соответствие принятых значений тому, что должно быть
               и преобразование их байтовый массив */
            BYTE TempDescArray[LTR22_DESCR_SIZE];
            unsigned int i;
            for (i=0; (i < LTR22_DESCR_SIZE) && (res == LTR_OK); i++) {
                TempDescArray[i] = (BYTE)((rbuf[i]>>16) & 0xff);
                if (((rbuf[i]>>24)&0xff) != i)
                    res=LTR22_ERROR_MODULE_INTERFACE;
            }

            if (res == LTR_OK) {
                int DescriptionArrayCounter=0;
                DWORD SizeCopy=0;
                DWORD Clock=0;
                WORD CRC=0;

                memset(&module->ModuleInfo.Description, 0,
                       sizeof(module->ModuleInfo.Description));

                // имя компании
                SizeCopy=sizeof(module->ModuleInfo.Description.CompanyName);
                memcpy(module->ModuleInfo.Description.CompanyName,
                       TempDescArray+DescriptionArrayCounter, SizeCopy);
                DescriptionArrayCounter+=SizeCopy;
                // имя устройства
                SizeCopy=sizeof(module->ModuleInfo.Description.DeviceName);
                memcpy(module->ModuleInfo.Description.DeviceName,
                       TempDescArray+DescriptionArrayCounter, SizeCopy);
                DescriptionArrayCounter+=SizeCopy;
                // имя серийный номер
                SizeCopy=sizeof(module->ModuleInfo.Description.SerialNumber);
                memcpy(module->ModuleInfo.Description.SerialNumber,
                       TempDescArray+DescriptionArrayCounter, SizeCopy);
                DescriptionArrayCounter+=SizeCopy;
                // имя название CPU
                SizeCopy=sizeof(module->ModuleInfo.CPU.Name);
                memcpy(module->ModuleInfo.CPU.Name,
                       TempDescArray+DescriptionArrayCounter,SizeCopy);
                DescriptionArrayCounter+=SizeCopy;

                // CPU Clock
                SizeCopy=sizeof(Clock);
                memcpy((BYTE*)&Clock,TempDescArray+DescriptionArrayCounter,SizeCopy);
                DescriptionArrayCounter+=SizeCopy;
                module->ModuleInfo.CPU.ClockRate=Clock;

                // FirmwareVersion
                SizeCopy=sizeof(module->ModuleInfo.CPU.FirmwareVersion);
                memcpy((BYTE*)&module->ModuleInfo.CPU.FirmwareVersion,TempDescArray+DescriptionArrayCounter,SizeCopy);
                DescriptionArrayCounter+=SizeCopy;

                // DeviceRevision
                SizeCopy=sizeof(module->ModuleInfo.Description.Revision);
                module->ModuleInfo.Description.Revision=TempDescArray[DescriptionArrayCounter];
                DescriptionArrayCounter+=SizeCopy;

                // Комментарий
                SizeCopy=53;
                memcpy(module->ModuleInfo.Description.Comment,TempDescArray+DescriptionArrayCounter,SizeCopy);
                DescriptionArrayCounter+=SizeCopy;

                // CRC
                SizeCopy=sizeof(CRC);
                memcpy((BYTE*)&CRC,TempDescArray+DescriptionArrayCounter,SizeCopy);

                /* хм, странное CRC -) */
                if (CRC != 0xffff)
                    res=LTR22_ERROR_MODULE_INTERFACE;
            }
        }
    }
    return res;
}

/********************************************************************
OverflowFlags - массив из 4-х байт если возникает переполнение - то 
байт, соответствующий каналу будет равен 1
если все нормально - то в массив записываются нули
если тестируется TestDataCounter то проверяется еще и последовательность данных
*********************************************************************/
LTR22API_DllExport(INT) LTR22_ProcessData(TLTR22 *module, const DWORD *src_data,
                                          double *dst_data, DWORD size,
                                          BOOLEAN calibrMainPset,
                                          BOOLEAN calibrExtraVolts,
                                          BYTE *OverflowFlags) {
    DWORD flags = LTR22_PROC_FLAG_PER_CH_ORDER;
    if (calibrMainPset)
        flags |= LTR22_PROC_FLAG_CALIBR;
    if (calibrExtraVolts)
        flags |= LTR22_PROC_FLAG_VOLT;
    INT procsize = (INT)size;
    return LTR22_ProcessDataEx(module, src_data, dst_data, &procsize, flags, OverflowFlags, NULL);
}


LTR22API_DllExport(INT) LTR22_ProcessDataEx(TLTR22 *module, const DWORD *src_data, double *dst_data,
                                            INT *size, DWORD flags, BYTE * OverflowFlags, void *reserved) {
    int res = (module == NULL) || (size == NULL) ? LTR_ERROR_INVALID_MODULE_DESCR : LTR_OK;
    BYTE en_ch_cnt=0; //кол-во разрешенных каналов
    BYTE ch_numbers[LTR22_CHANNEL_CNT], fnd_ch[LTR22_CHANNEL_CNT]; //номер разрешенных каналов по порядку
    INT proc_size = 0;

    if ((res==LTR_OK) && (src_data==NULL))
        res = LTR_ERROR_PARAMETERS;

    if (res==LTR_OK) {
        /* Считаем количество включенных каналов */
        BYTE i;
        for (i = 0; i < LTR22_CHANNEL_CNT; i++) {
            if (module->ChannelEnabled[i] == TRUE)
                ch_numbers[en_ch_cnt++]=i;
        }

        if (!en_ch_cnt)
            res = LTR_ERROR_PARAMETERS;

        if (*size % en_ch_cnt)
            res = LTR_ERROR_PARAMETERS;
    }

    if (res == LTR_OK) {
        BYTE    ch_num=0, // номер канала АЦП
                ch_range=0,    // номер диапазона
                ch_cur_cntr=0,    // счетчик получаемых данных
                ch_cntr;

        DWORD ch_size= *size / en_ch_cnt; // размер массива под каждый канал
        DWORD src_pos=0, dst_cntr = 0, i;
        int ltable_pos = 0, ltable_ch=0;

        if (OverflowFlags!=NULL)
            memset(OverflowFlags,0,LTR22_CHANNEL_CNT);

        memset(fnd_ch, 0, sizeof(fnd_ch));

        for (dst_cntr=0, ch_cntr=((src_data[0]>>5)&7), src_pos=0;
             (dst_cntr < ch_size) && (res==LTR_OK); src_pos++) {

            ch_num=(BYTE)(src_data[src_pos]&3);
            ch_range=(BYTE)((src_data[src_pos]>>2)&7);
            ch_cur_cntr=(BYTE)((src_data[src_pos]>>5)&7);

//                OffsetIndex=(ChannelIncrimentNumber*OffsetArray)+countData;
            /* проверка циклического счетчика в принимаемом массиве данных */
            if (ch_cur_cntr != ch_cntr)
                res = LTR22_ERROR_INVALID_DATA_COUNTER;

            for (ltable_pos = -1, i = 0; (res == LTR_OK) && (ltable_pos == -1) && (i < en_ch_cnt); i++) {
                if (ch_num == ch_numbers[i]) {
                    ltable_pos = i;
                    ltable_ch++;
                    if (fnd_ch[i]==0) {
                        fnd_ch[i] = 1;
                    } else {
                        res = LTR22_ERROR_INVALID_CH_NUMBER;
                    }
                }
            }

            if (ltable_pos == -1)
                res = LTR22_ERROR_INVALID_CH_NUMBER;



            /* проверка на соответствие номера диапазона номеру канала
               а также диапазон может быть от 0 до 5, и принимать при переполнении значение 7 */
            if ((res==LTR_OK) &&  (ch_range!=module->ADCChannelRange[ch_num])) {
                if (ch_range == LTR22_RANGE_OVERFLOW) {
                    if (OverflowFlags!=NULL)
                        OverflowFlags[ch_num]=1;
                } else {
                    res = LTR22_ERROR_INVALID_DATA_RANGE_FOR_THIS_CHANNEL;
                }
                ch_range=module->ADCChannelRange[ch_num];
            }

            if (res == LTR_OK) {
                double proc_data;

                proc_data = (short)((src_data[src_pos]>>16)&0xffff);

                if (flags & LTR22_PROC_FLAG_CALIBR) {
                    // если задан был перевод в основные коэффициенты
                    double    scale=0,        // масштаб канала - калибровка
                            offset=0;    // сдвиг канала - калибровка
                    scale=module->ADCCalibration[ch_num][module->FreqDiscretizationIndex].FactoryCalibScale[ch_range];
                    offset=module->ADCCalibration[ch_num][module->FreqDiscretizationIndex].FactoryCalibOffset[ch_range];
                    proc_data=(proc_data*scale)+offset;
                }

                if (flags & LTR22_PROC_FLAG_VOLT) {
                    // если задан был перевод в дополнительные коэффициенты
                    proc_data = (proc_data*module->ADCCalibration[ch_num][module->FreqDiscretizationIndex].UserCalibScale[ch_range])
                        + module->ADCCalibration[ch_num][module->FreqDiscretizationIndex].UserCalibOffset[ch_range];
                }


                if (dst_data != NULL) {
                    if (flags & LTR22_PROC_FLAG_PER_CH_ORDER) {
                        dst_data[ltable_pos*ch_size+dst_cntr] = proc_data;
                    } else {
                        dst_data[dst_cntr * en_ch_cnt + ltable_pos] = proc_data;
                    }
                }

                // инкримент счетчиков, поставил в конец, поскольку не всегда все каналы включены,
                // и следовательно некоторые игнорируются
                if (++ch_cntr>6)
                    ch_cntr=0;

                if (ltable_ch == en_ch_cnt) {
                    ltable_ch = 0;
                    dst_cntr++;
                    memset(fnd_ch, 0, sizeof(fnd_ch));
                    proc_size+=en_ch_cnt;
                }
            }
        }
    }

    if (!(flags & LTR22_PROC_FLAG_PER_CH_ORDER)) {
        *size = proc_size;
    }
    return res;
}

/*
---------------------------------------------------------------------------------------
Запись EEPROM AVR указанного размера побайтово
сначала отсылаем первый бит,
чтобы перессылка шла быстрее
*/
LTR22API_DllExport(INT) LTR22_WriteAVREEPROM(TLTR22 *module, const BYTE *Data, DWORD BeginAddress, DWORD size) {
    unsigned int const EEPROMSize=512;
    int res = ((BeginAddress+size)>EEPROMSize) || (module==NULL) ?
                LTR_ERROR_PARAMETERS : LTR_OK;

    if (res == LTR_OK) {
        if (!module->DataReadingProcessed) {
            DWORD sbuf[LTR22_INTERNAL_AVR_BUFFER];
            DWORD rbuf[LTR22_INTERNAL_AVR_BUFFER];
            int DataCounter = 0,  BufferDataSend = 0;
            // подготовка к отсылке
            sbuf[0] = LTR22CMD_PREPARE_READ_WRITE_EEPROM_ADDRESS | (BeginAddress << 16);

            if (LTR_Send(&module->Channel, sbuf, 1, TIMEOUT_CMD_SEND) == 1) {
                if (LTR_Recv(&module->Channel, rbuf, NULL, 1, TIMEOUT_CMD_RECIEVE) == 1) {
                    if ((rbuf[0] >> 16) == (LTR22CMD_PREPARE_READ_WRITE_EEPROM_ADDRESS & 0x3f)) {
                        while ((size > 0)&&(res == LTR_OK)) {
                            int i;
                            BufferDataSend = MIN(size, LTR22_INTERNAL_AVR_BUFFER);
                            for (i = 0; i < BufferDataSend; i++, DataCounter++) {
                                sbuf[i]=LTR22CMD_WRITE_EEPROM|(Data[DataCounter]<<16);
                            }

                            // сама отсылка
                            if (LTR_Send(&module->Channel, sbuf, BufferDataSend, TIMEOUT_CMD_SEND) == BufferDataSend) {
                                if (LTR_Recv(&module->Channel, rbuf, NULL, BufferDataSend, 5000)==BufferDataSend) {
                                    for (i = 0; i < BufferDataSend; i++) {
                                        if ((rbuf[i]>>16) != (LTR22CMD_WRITE_EEPROM&0x3f))
                                            res = LTR22_ERROR_MODULE_INTERFACE;
                                    }
                                } else {
                                    res=LTR_ERROR_RECV;
                                }
                            }else {
                                res=LTR_ERROR_SEND;
                            }
                            size -= BufferDataSend;
                        }
                    } else {
                        res = LTR22_ERROR_MODULE_INTERFACE;
                    }
                } else {
                    res = LTR_ERROR_RECV;
                }
            } else {
                res=LTR_ERROR_SEND;
            }
        } else {
            res = LTR22_ERROR_ADC_RUNNING;
        }
    }
    return res;
}

/*
---------------------------------------------------------------------------------------
Считывание EEPROM AVR указанного размера побайтово
*/
LTR22API_DllExport(INT) LTR22_ReadAVREEPROM(TLTR22 *module, BYTE *Data, DWORD BeginAddress, DWORD size) {

    int res = ((BeginAddress+size) > LTR22_EEPROM_SIZE) ? LTR_ERROR_PARAMETERS : LTR22_IsOpened(module);;

    if (res == LTR_OK) {
        if (!module->DataReadingProcessed) {
            DWORD sbuf[2];
            DWORD rbuf[LTR22_EEPROM_SIZE+2];

            sbuf[0]=LTR22CMD_PREPARE_READ_WRITE_EEPROM_ADDRESS | (BeginAddress<<16);
            sbuf[1]=LTR22CMD_READ_EEPROM | (size<<16);

            if (LTR_Send(&module->Channel, sbuf, SBUF_SIZE, TIMEOUT_CMD_SEND) == SBUF_SIZE) {
                if (LTR_Recv(&module->Channel, rbuf, NULL, size+2, 300000) == (INT)(size+2)) {
                    if (((rbuf[0]>>16) == (LTR22CMD_PREPARE_READ_WRITE_EEPROM_ADDRESS&0x3f)) &&
                        ((rbuf[1]>>16) == (LTR22CMD_READ_EEPROM&0x3f))) {
                        DWORD i;
                        for (i=0; (i<size) && (res==LTR_OK); i++) {
                            if ( (rbuf[i+2]>>24) != (i&0xFF)) {
                                res=LTR22_ERROR_MODULE_INTERFACE;
                            } else {
                                Data[i]=(BYTE)(rbuf[i+2]>>16);
                            }
                        }
                    } else {
                        res = LTR22_ERROR_MODULE_INTERFACE;
                    }
                } else {
                    res = LTR_ERROR_RECV;
                }
            } else {
                res = LTR_ERROR_SEND;
            }
        } else {
            res = LTR22_ERROR_ADC_RUNNING;
        }
    }
    return res;
}

/*
-----------------------------------------------------------------------------------
Получение описания ошибки
*/
LTR22API_DllExport(LPCSTR) LTR22_GetErrorString(INT err) {
    size_t i;
    for (i = 0; (i < sizeof(f_err_tbl) / sizeof(f_err_tbl[0])); i++) {
        if (f_err_tbl[i].code == err)
            return f_err_tbl[i].message;
    }
    return LTR_GetErrorString(err);
}



/*
-----------------------------------------------------------------------------------
тестирование 
*/
/*
    тестирование хардварного интерфейса - путем бегущего нуля и бегущей единицы
*/
LTR22API_DllExport(INT) LTR22_TestHardwareInterface(TLTR22 *module) {
    unsigned int i;
    int res = LTR22_IsOpened(module);
    if (res == LTR_OK) {
        if (!module->DataReadingProcessed) {
            DWORD sbuf[1];
            DWORD rbuf[17];
            sbuf[0]=LTR22CMD_TEST_RUNNING_UNIT;
            if (LTR_Send(&module->Channel, sbuf, SBUF_SIZE, TIMEOUT_CMD_SEND) == SBUF_SIZE) {
                if (LTR_Recv(&module->Channel, rbuf, NULL, RBUF_SIZE, TIMEOUT_CMD_RECIEVE) == RBUF_SIZE) {
                    if ((rbuf[0]>>16) == (LTR22CMD_TEST_RUNNING_UNIT & 0x3f)) {
                        for (i=0; (i<16) && (res==LTR_OK);i++) {
                            if ((rbuf[i+1]>>16) != (1UL<<i)) {
                                res = LTR22_ERROR_INVALID_TEST_HARD_INTERFACE;
                            }
                        }
                    } else {
                        res = LTR22_ERROR_MODULE_INTERFACE;
                    }
                } else {
                    res = LTR_ERROR_RECV;
                }
            } else {
                res = LTR_ERROR_SEND;
            }
        } else {
            res = LTR22_ERROR_ADC_RUNNING;
        }
    }

    if (res == LTR_OK) {
        DWORD sbuf[1];
        DWORD rbuf[17];
        sbuf[0] = LTR22CMD_TEST_RUNNING_ZERO;
        if (LTR_Send(&module->Channel, sbuf, SBUF_SIZE, TIMEOUT_CMD_SEND) == SBUF_SIZE) {
            if (LTR_Recv(&module->Channel, rbuf, NULL, RBUF_SIZE, TIMEOUT_CMD_RECIEVE) == RBUF_SIZE) {
                if ((rbuf[0]>>16) == (LTR22CMD_TEST_RUNNING_ZERO & 0x3f)) {
                    for (i=0; (i<16) && (res==LTR_OK); i++) {
                        if ((rbuf[i+1]>>16)!=((~(1<<i))&0xffff)) {
                            res=LTR22_ERROR_INVALID_TEST_HARD_INTERFACE;
                        }
                    }
                } else {
                    res = LTR22_ERROR_MODULE_INTERFACE;
                }
            } else{
                res = LTR_ERROR_RECV;
            }
        } else {
            res = LTR_ERROR_SEND;
        }
    }
    return res;
}



LTR22API_DllExport(INT) LTR22_GetADCData(TLTR22 *module, double * Data, DWORD Size, DWORD tout,
                                        BOOLEAN calibrMainPset, BOOLEAN calibrExtraVolts) {
    int res = LTR22_IsOpened(module);
    int stoperr = LTR_OK;
    if (res == LTR_OK) {
        DWORD *rbuf = malloc(Size *sizeof(rbuf[0]));

        res = LTR22_StartADC(module,FALSE);
        if (res == LTR_OK) {
            INT recvd = LTR_Recv(&module->Channel, rbuf, NULL, Size, tout);
            if (recvd < 0) {
                res = recvd;
            } else if (recvd ==(INT)Size) {
                res=LTR22_ProcessData(module, rbuf, Data, Size,calibrMainPset,calibrExtraVolts,NULL);
            } else {
                res=LTR_ERROR_RECV_INSUFFICIENT_DATA;
            }
        }
        free(rbuf);
    }

    stoperr = res=LTR22_StopADC(module);
    if (res==LTR_OK)
        res = stoperr;

    stoperr=LTR22_ClearBuffer(module, TRUE);
    if (res==LTR_OK)
        res = stoperr;

    return res;
}

LTR22API_DllExport(INT) LTR22_ReopenModule(TLTR22 *module) {
    int res = module==NULL ? LTR_ERROR_INVALID_MODULE_DESCR : LTR_OK;
    if (res==LTR_OK)
        res=LTR_Close(&module->Channel);
    if (res==LTR_OK)
        res=LTR_Open(&module->Channel);
    return res;
}
