#include <stdio.h>
#include <string.h>
#include <chrono>
#include <windows.h>
#include <iostream>
#include <vector>
#include "htra_api.h"
#include "example.h"
using namespace std;

#define IS_USB 1 //默认使用的是USB型设备，若使用的是网口型设备则将IS_USB定义为0。

int Status = 0;      //函数的返回。
void* Device = NULL; //当前设备的内存地址。
int DevNum = 0;      //指定设备号。

void SwpMode(BootProfile_TypeDef* BootProfile, BootInfo_TypeDef *BootInfo)//SWP模式设置。
{
	SWP_Profile_TypeDef SWP_ProfileIn;  //SWP输入配置，包括起始频率、终止频率、RBW、参考电平等。
	SWP_Profile_TypeDef SWP_ProfileOut; //SWP输出配置。
	SWP_TraceInfo_TypeDef TraceInfo;    //当前配置下的迹线信息，包括迹线点数、跳频点数等。

	SWP_ProfileDeInit(&Device, &SWP_ProfileIn); //调用此函数初始化配置SWP模式的相关参数。

	SWP_ProfileIn.StartFreq_Hz = 9e3;   //起始频率。
	SWP_ProfileIn.StopFreq_Hz = 6.37e9; //终止频率。
	SWP_ProfileIn.RBWMode = RBW_Manual; //手动RBW。
	SWP_ProfileIn.RBW_Hz = 15e3;        //设置RBW。

	Status = SWP_Configuration(&Device, &SWP_ProfileIn, &SWP_ProfileOut, &TraceInfo); //下发SWP模式的相关配置。

	SWP_Configuration_ErrorHandling(Status, &Device, DevNum, BootProfile, BootInfo, &SWP_ProfileIn, &SWP_ProfileOut, &TraceInfo); //当Status不为0时，根据Status的返回值进行相对应的错误处理。

	vector<double> Frequency(TraceInfo.FullsweepTracePoints);    //创建频率数组。
	vector<float> PowerSpec_dBm(TraceInfo.FullsweepTracePoints); //创建功率数组。
	int HopIndex = 0;                                            //当前跳频点索引。
	int FrameIndex = 0;                                          //当前帧索引。
	MeasAuxInfo_TypeDef MeasAuxInfo;                             //测量数据的辅助信息，包括：功率最大值索引、功率最大值、设备温度、经纬度、绝对时间戳等。

	for (int i = 0; i < TraceInfo.TotalHops; i++) //TraceInfo.TotalHops代表当前配置下的帧数，因此循环TraceInfo.TotalHops次调用SWP_GetPartialSweep即可得到完整迹线。
	{
		Status = SWP_GetPartialSweep(&Device, Frequency.data() + i * TraceInfo.PartialsweepTracePoints, PowerSpec_dBm.data() + i * TraceInfo.PartialsweepTracePoints, &HopIndex, &FrameIndex, &MeasAuxInfo); //获取频谱数据。

		SWP_ErrorHandlingExceptOpenAndConfiguration(Status, &Device, DevNum, BootProfile, BootInfo, &SWP_ProfileIn, &SWP_ProfileOut, &TraceInfo); //当Status不为0时，根据Status的返回值进行相对应的错误处理。
	}
	cout << "SWP Data Acquisition Completed" << endl;
}

void RtaMode(BootProfile_TypeDef* BootProfile, BootInfo_TypeDef* BootInfo) //RTA模式设置。
{
	RTA_Profile_TypeDef RTA_ProfileIn;  //RTA输入配置，包括中心频率、抽取倍数、参考电平等等。
	RTA_Profile_TypeDef RTA_ProfileOut; //RTA输出配置。
	RTA_FrameInfo_TypeDef FrameInfo;    //当前配置下的迹线信息，包括起始频率、终止频率、POI等等。

	RTA_ProfileDeInit(&Device, &RTA_ProfileIn); //调用此函数初始化配置RTA模式的相关参数。

	RTA_ProfileIn.CenterFreq_Hz = 1.86e9; // 配置中心频率。
	RTA_ProfileIn.RefLevel_dBm = 0;       // 配置参考电平。
	RTA_ProfileIn.DecimateFactor = 1;	  // 配置抽取倍数。
	RTA_ProfileIn.TriggerMode = Adaptive; // 配置触发模式。
	RTA_ProfileIn.TriggerSource = Bus;    // 配置触发源。

	Status = RTA_Configuration(&Device, &RTA_ProfileIn, &RTA_ProfileOut, &FrameInfo); //通过调用此函数下发RTA模式的相关配置。

	RTA_Configuration_ErrorHandling(Status, &Device, DevNum, BootProfile, BootInfo, &RTA_ProfileIn, &RTA_ProfileOut, &FrameInfo); //当Status不为0时，根据Status的返回值进行相对应的错误处理。

	vector<uint8_t> SpectrumTrace(FrameInfo.PacketValidPoints);                    //频谱数组。
	vector<uint16_t> SpectrumBitmap(FrameInfo.FrameHeight * FrameInfo.FrameWidth); //频谱位图数组。
	vector<float> Spectrum(FrameInfo.FrameWidth);                                  //dBm单位的频谱数组。
	RTA_PlotInfo_TypeDef RTA_PlotInfo;                                             //绘图信息。
	RTA_TriggerInfo_TypeDef TriggerInfo;                                           //触发信息。
	MeasAuxInfo_TypeDef MeasAuxInfo;                                               //测量数据的辅助信息。

	Status = RTA_BusTriggerStart(&Device); //调用RTA_BusTriggerStart触发设备。

	for (uint32_t i = 0; i < FrameInfo.PacketCount; i++)
	{
		Status = RTA_GetRealTimeSpectrum(&Device, SpectrumTrace.data(), SpectrumBitmap.data(), &RTA_PlotInfo, &TriggerInfo, &MeasAuxInfo); //获取RTA数据及触发信息。

		RTA_ErrorHandlingExceptOpenAndConfiguration(Status, &Device,DevNum, BootProfile, BootInfo, &RTA_ProfileIn, &RTA_ProfileOut, &FrameInfo); //当Status不为0时，根据Status的返回值进行相对应的错误处理。

	}

	Status = RTA_BusTriggerStop(&Device); //调用RTA_BusTriggerStop停止触发设备。
	cout << "RTA Data Acquisition Completed" << endl;
}

void IQSMode(BootProfile_TypeDef* BootProfile, BootInfo_TypeDef* BootInfo) 
{
	IQS_Profile_TypeDef IQS_ProfileIn;  //IQS输入配置，包括中心频率、抽取倍数、参考电平等等。
	IQS_Profile_TypeDef IQS_ProfileOut; //IQS输出配置。
	IQS_StreamInfo_TypeDef StreamInfo;  //当前配置下迹线信息，包括IQ数据点数、时域样点的数据类型等等。

	IQS_ProfileDeInit(&Device, &IQS_ProfileIn); //调用此函数初始化配置IQS模式的相关参数。

	IQS_ProfileIn.CenterFreq_Hz = 1e9;       //配置中心频率。
	IQS_ProfileIn.RefLevel_dBm = 0;          //配置参考电平。
	IQS_ProfileIn.DecimateFactor = 2;	     //配置抽取倍数。
	IQS_ProfileIn.DataFormat = Complex16bit; //配置IQ数据格式。
	IQS_ProfileIn.TriggerMode = FixedPoints; //配置触发模式。
	IQS_ProfileIn.TriggerSource = Bus;       //配置触发源为内部总线触发。
	IQS_ProfileIn.TriggerLength = 16384;     //配置单次触发采集的点数。

	Status = IQS_Configuration(&Device, &IQS_ProfileIn, &IQS_ProfileOut, &StreamInfo); //下发IQS模式的相关配置。

	IQS_Configuration_ErrorHandling(Status, &Device, DevNum, BootProfile, BootInfo, &IQS_ProfileIn, &IQS_ProfileOut, &StreamInfo); //当Status不为0时，根据Status的返回值进行相对应的错误处理。

	IQStream_TypeDef IQStream;                         //用于存放IQ数据包数据的结构体，包括IQ数据和相关配置信息。
	vector<int16_t> I_Data(StreamInfo.StreamSamples);  //开辟用于存放I路数据的内存。
	vector<int16_t> Q_Data(StreamInfo.StreamSamples);  //开辟用于存放Q路数据的内存。

	Status = IQS_BusTriggerStart(&Device); //调用IQS_BusTriggerStart触发设备。若触发源为外部触发，则不需要调用此函数。

	for (int j = 0; j < StreamInfo.PacketCount; j++)
	{
		Status = IQS_GetIQStream_PM1(&Device, &IQStream); //获取IQ数据。

		if (Status == APIRETVAL_NoError)
		{
			//UserCode here
			//注意：实际使用IQ模式时，建议开一个线程专门调用IQS_GetIQStream获取IQ数据，不能与处理IQ数据放在同一个线程里。

			/*int16_t* IQ = (int16_t*)IQStream.AlternIQStream;
			 uint32_t Points = StreamInfo.PacketSamples;

			 if (j == StreamInfo.PacketCount - 1 && StreamInfo.StreamSamples % StreamInfo.PacketSamples != 0) //可能最后一包不满一整包（16242个点）；所以只需要循环不满一包的点数
			 {
				Points = StreamInfo.StreamSamples % StreamInfo.PacketSamples;
			 }

			 for (uint32_t i = 0; i < Points; i++)
			 {
			   I_Data[i + StreamInfo.PacketSamples * j] = IQ[i * 2];
			   Q_Data[i + StreamInfo.PacketSamples * j] = IQ[i * 2 + 1];
			  }*/

		}
		else //当Status不为0时，根据Status的返回值进行相对应的错误处理
		{
		   IQS_ErrorHandlingExceptOpenAndConfiguration(Status, &Device, DevNum, BootProfile, BootInfo, &IQS_ProfileIn, &IQS_ProfileOut, &StreamInfo); 
        }
	}

	  Status = IQS_BusTriggerStop(&Device); //调用IQS_BusTriggerStop停止触发设备。
	  cout << "IQS Data Acquisition Completed" << endl;
}

int Device_MeasureModeSwitchTime()
{
	BootProfile_TypeDef BootProfile; //启动配置结构体，包括物理接口、供电方式等。
	BootInfo_TypeDef BootInfo;       //启动信息结构体，包括设备信息、USB速率等。

	BootProfile.DevicePowerSupply = USBPortAndPowerPort; //使用USB数据端口及独立电源端口双供电。

#if IS_USB==1
	//配置USB接口。
	BootProfile.PhysicalInterface = USB;
#else 
	//配置ETH接口。
	BootProfile.PhysicalInterface = ETH;
	BootProfile.ETH_IPVersion = IPv4;
	BootProfile.ETH_RemotePort = 5000;
	BootProfile.ETH_ReadTimeOut = 5000;
	BootProfile.ETH_IPAddress[0] = 192;
	BootProfile.ETH_IPAddress[1] = 168;
	BootProfile.ETH_IPAddress[2] = 1;
	BootProfile.ETH_IPAddress[3] = 100;
#endif

	Status = Device_Open(&Device, DevNum, &BootProfile, &BootInfo); //打开设备。

	Device_Open_ErrorHandling(Status, &Device, DevNum, &BootProfile, &BootInfo); //当Status不为0时，根据Status的返回值进行相对应的错误处理

	//SWP模式 转 RTA模式。
	SwpMode(&BootProfile, &BootInfo);						 //运行SWP模式。
	auto start1 = std::chrono::high_resolution_clock::now(); //获取当前时间。
	RtaMode(&BootProfile, &BootInfo);						 //运行RTA模式。
	auto stop1 = std::chrono::high_resolution_clock::now();  //获取当前时间

	auto timeout1 = std::chrono::duration_cast<std::chrono::duration<double, ratio<1, 1000>>>(stop1 - start1); //计算所用时间。
	cout << "SWP To RTA Cost：" << timeout1.count() << "ms" << endl;
	
	//IQS模式 转 SWP模式。
	IQSMode(&BootProfile, &BootInfo);						 //运行IQS模式。
	auto start2 = std::chrono::high_resolution_clock::now(); //获取当前时间。
	SwpMode(&BootProfile, &BootInfo);						 //运行SWP模式。
	auto stop2 = std::chrono::high_resolution_clock::now();  //获取当前时间。

	auto timeout2 = std::chrono::duration_cast<std::chrono::duration<double, ratio<1, 1000>>>(stop2 - start2); //计算所用时间。
	cout << "IQS To SWP Cost：" << timeout2.count() << "ms" << endl;

	Device_Close(&Device); //关闭设备。

	return 0;
}
