#include <stdio.h>
#include <conio.h>
#include <math.h>
#include "Rtusbapi.h"

// аварийное завершение программы
void TerminateApplication(char *ErrorString, bool TerminationFlag = true);

// максимально возможное кол-во опрашиваемых виртуальных слотов
const WORD MaxVirtualSoltsQuantity = 127;

// текущая версия библиотеки Rtusbapi.dll
DWORD DllVersion;
// указатель на интерфейс модуля
IRTUSB3000 *pModule;
// название модуля
char ModuleName[10];
// скорость работы шины USB
BYTE UsbSpeed;
// серийный номер модуля
char ModuleSerialNumber[9];
// версия драйвера AVR
char AvrVersion[5];
// структура, содержащая информацию о версии драйвера DSP
RTUSB3000::DSP_INFO di;
// структура информации в ППЗУ модуля
RTUSB3000::FLASH fi;
// структура параметров работы АЦП
RTUSB3000::OUTPUT_PARS dp;
// значение отсчёта для однократного вывода на ЦАП
SHORT DacSample;

UINT Temp;

//------------------------------------------------------------------------
// основная программа
//------------------------------------------------------------------------
void main(void)
{
	WORD i;

	// зачистим экран монитора
	clrscr();

	printf(" *******************************************\n");
	printf(" Console example for USB3000 WRITE_SAMPLE() \n");
	printf(" *******************************************\n\n");

	// проверим версию используемой библиотеки Rtusbapi.dll
	if((DllVersion = RtGetDllVersion()) != CURRENT_VERSION_RTUSBAPI)
	{
		char String[128];
		sprintf(String, " Rtusbapi.dll Version Error!!!\n   Current: %1u.%1u. Required: %1u.%1u",
											DllVersion >> 0x10, DllVersion & 0xFFFF,
											CURRENT_VERSION_RTUSBAPI >> 0x10, CURRENT_VERSION_RTUSBAPI & 0xFFFF);

		TerminateApplication(String);
	}
	else printf(" Rtusbapi.dll Version --> OK\n");

	// получим указатель на интерфейс модуля USB3000
	pModule = static_cast<IRTUSB3000 *>(RtCreateInstance("usb3000"));
	if(pModule == NULL)  TerminateApplication(" Module Interface --> Bad\n");
	else printf(" Module Interface --> OK\n");

	// попробуем обнаружить модуль USB3000 в первых 127 виртуальных слотах
	for(i = 0x0; i < MaxVirtualSoltsQuantity; i++) if(pModule->OpenDevice(i)) break;
	// что-нибудь обнаружили?
	if(i == MaxVirtualSoltsQuantity) TerminateApplication(" Can't find module USB3000 in first 127 virtual slots!\n");
	else printf(" OpenDevice(%u) --> OK\n", i);

	// прочитаем название модуля в нулевом виртуальном слоте
	if(!pModule->GetModuleName(ModuleName)) TerminateApplication(" GetModuleName() --> Bad\n");
	else printf(" GetModuleName() --> OK\n");
	// проверим, что это 'USB3000'
	if(strcmp(ModuleName, "USB3000")) TerminateApplication(" The module is not 'USB3000'\n");
	else printf(" The module is 'USB3000'\n");

	// узнаем текущую скорость работы шины USB20
	if(!pModule->GetUsbSpeed(&UsbSpeed)) { printf(" GetUsbSpeed() --> Bad\n"); exit(1); }
	else printf(" GetUsbSpeed() --> OK\n");
	// теперь отобразим версию драйвера AVR
	printf(" USB Speed is %s\n", UsbSpeed ? "HIGH (480 Mbit/s)" : "FULL (12 Mbit/s)");

	// прочитаем серийный номер модуля
	if(!pModule->GetModuleSerialNumber(ModuleSerialNumber)) TerminateApplication(" GetModuleSerialNumber() --> Bad\n");
	else printf(" GetModuleSerialNumber() --> OK\n");
	// теперь отобразим серийный номер модуля
	printf(" Module Serial Number is %s\n", ModuleSerialNumber);

	// прочитаем версию драйвера AVR
	if(!pModule->GetAvrVersion(AvrVersion)) TerminateApplication(" GetAvrVersion() --> Bad\n");
	else printf(" GetAvrVersion() --> OK\n");
	// теперь отобразим версию драйвера AVR
	printf(" Avr Driver Version is %s\n", AvrVersion);

	// код драйвера DSP возьмём из соответствующего ресурса штатной DLL библиотеки
	if(!pModule->LOAD_DSP()) TerminateApplication(" LOAD_DSP() --> Bad\n");
	else printf(" LOAD_DSP() --> OK\n");

	// проверим загрузку модуля
 	if(!pModule->MODULE_TEST()) TerminateApplication(" MODULE_TEST() --> Bad\n");
	else printf(" MODULE_TEST() --> OK\n");

	// получим версию загруженного драйвера DSP
	if(!pModule->GET_DSP_INFO(&di)) TerminateApplication(" GET_DSP_INFO() --> Bad\n");
	else printf(" GET_DSP_INFO() --> OK\n");
	// теперь отобразим версию загруженного драйвера DSP
	printf(" DSP Driver version is %1u.%1u\n", di.DspMajor, di.DspMinor);

	// обязательно проинициализируем поле size структуры RTUSB3000::FLASH
	fi.size = sizeof(RTUSB3000::FLASH);
	// получим информацию из ППЗУ модуля
	if(!pModule->GET_FLASH(&fi)) TerminateApplication(" GET_FLASH() --> Bad\n");
	else printf(" GET_FLASH() --> OK\n");

	// проверим установлен ли на обнаруженном модуле ЦАП
	if(!fi.IsDacPresented) TerminateApplication("\n DAC is absent on this module USB3000!\n");

	// обязательно проинициализируем поле size структуры OUTPUT_PARS
	dp.size = sizeof(RTUSB3000::OUTPUT_PARS);
	// получим текущие параметры работы ЦАП
	if(!pModule->GET_OUTPUT_PARS(&dp)) TerminateApplication(" GET_OUTPUT_PARS() --> Bad\n");
	else printf(" GET_OUTPUT_PARS() --> OK\n");

	// установим желаемые параметры ЦАП
	dp.OutputRate = 100.0;			// в кГц
	dp.OutputFifoLength = 0xF80;	// 0xF80(3968) - максимальный размер FIFO буфера ЦАП в DSP модуля
	if(!pModule->SET_OUTPUT_PARS(&dp)) TerminateApplication(" SET_OUTPUT_PARS() --> Bad\n");
	else printf(" SET_OUTPUT_PARS() --> OK\n");

	// теперь отобразим параметры ЦАП на дисплее
	printf(" \n");
	printf(" Module USB3000 (S/N %s) is ready ... \n", fi.SerialNumber);
	printf(" Dac parameters:\n");
	printf("   OutputRate = %7.2f kHz\n", dp.OutputRate);
	printf("   DacFifoLength = %d\n", dp.OutputFifoLength);
	printf("\n Now SINUS signal is on the first DAC channel and \n");
	printf("    -SINUS signal is on the second DAC channel.\n");
	printf("     (Press any key to terminate the program)\n");

	// Цикл перманетной работы функции DAC_SAMPLE().
	// При этом на первом канале ЦАП будет выводится синус,
	// а на втором канале ЦАП будет выводится -синус.
	// Т.е. сигналы на каналах ЦАП будут выводится в противофазе ;)
	double CurrentTime = 0.0;
	for(i = 0x0 ; ; i++)
	{
		DacSample = (SHORT)(2000*sin(2.*M_PI*2.5*CurrentTime));
		CurrentTime += 1.0/dp.OutputRate;

		// если была нажата какая-либо клавиша, то тупо выйдем из цикла
		if(kbhit()) break;
		// выведем очередной отсчёт на первый канал ЦАП
		else if(!pModule->WRITE_SAMPLE(WORD(0x0), &DacSample)) TerminateApplication(" WRITE_SAMPLE(0) --> Bad\n");

		Sleep(50);

		// выведем очередной отсчёт на второй канал ЦАП
		DacSample *= (SHORT)-1;
		if(!pModule->WRITE_SAMPLE(WORD(0x1), &DacSample)) TerminateApplication(" WRITE_SAMPLE(1) --> Bad\n");

		// изредка моргнём строчкой с точками
		if(!(i%10))
		{
			static flag;

			flag ^= -0x1;
			if(flag) printf("                                             \r");
			else printf(" ............................................\r");
		}
	}

	// выставим нулевые значения на выходах обоих каналов ЦАП
	DacSample = (SHORT)(0x0);
	if(!pModule->WRITE_SAMPLE(WORD(0x0), &DacSample)) printf(" WRITE_SAMPLE(0) --> Bad\n");
	else if(!pModule->WRITE_SAMPLE(WORD(0x1), &DacSample)) printf(" WRITE_SAMPLE(1) --> Bad\n");

	// освободим интерфейс модуля
	printf("                                             \n");
	TerminateApplication("\n The program was completed successfully!!!\n", false);
}

//------------------------------------------------------------------------
// вывод сообщения об ошибке и аварийный выход из программы
//------------------------------------------------------------------------
void TerminateApplication(char *ErrorString, bool TerminationFlag)
{
	// подчищаем интерфейс модуля
	if(pModule)
	{
		// освободим интерфейс модуля
		if(!pModule->ReleaseInstance()) printf(" ReleaseInstance() --> Bad\n");
		else printf(" ReleaseInstance() --> OK\n");
		// обнулим указатель на интерфейс модуля
		pModule = NULL;
	}

	// выводим текст сообщения
	if(ErrorString) printf(ErrorString);

	// если нужно - аварийно завершаем программу
	if(TerminationFlag) exit(1);
	else return;
}

