#include <stdio.h>
#include <string.h>
#include <vector>
#include <complex>
#ifdef _WIN32
#define _LIBCPP_COMPLEX
#endif // DEBUG
#include "liquid.h"
#include "htra_api.h" 
#include "example.h"
using namespace std;

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

void DSP_GetWindow(Window_TypeDef Window, int n, double Window_Coefficient[])
{
	if (n >= 0)
	{
		switch (Window)
		{
		case 0:/*flat top window*/
		{
			const double a0 = 0.21557895;
			const double a1 = 0.41663158;
			const double a2 = 0.277263158;
			const double a3 = 0.083578947;
			const double a4 = 0.006947368;
			for (int i = 0; i < n; i++)
			{
				double b1 = cos(1 * (2 * PI * i / n));
				double b2 = cos(2 * (2 * PI * i / n));
				double b3 = cos(3 * (2 * PI * i / n));
				double b4 = cos(4 * (2 * PI * i / n));
				Window_Coefficient[i] = (a0 - a1 * b1 + a2 * b2 - a3 * b3 + a4 * b4);//Normalized
			}
			break;
		}
		default:
			for (int i = 0; i < n; i++)
			{
				Window_Coefficient[i] = 1;
			}
			break;
		}
	}
}

int FFT_IQSToSpectrum(const IQStream_TypeDef* IQStream, double Freq_Hz[], float PowerSpec_dBm[])
{
	int Status = 0;

	DataFormat_TypeDef DataFormat;
	DataFormat = Complex16bit;

	Window_TypeDef Window;
	Window = FlatTop;
	vector<double> Window_Coefficient(IQStream->IQS_StreamInfo.StreamSamples);

	DSP_GetWindow(Window, IQStream->IQS_StreamInfo.StreamSamples, Window_Coefficient.data());

	int16_t* IQ = (int16_t*)IQStream->AlternIQStream;
	vector<double> IQ_Windowing(IQStream->IQS_StreamInfo.StreamSamples * 2);
	vector<double> I_Windowed(IQStream->IQS_StreamInfo.StreamSamples);
	vector<double> Q_Windowed(IQStream->IQS_StreamInfo.StreamSamples);

	for (uint32_t i = 0; i < IQStream->IQS_StreamInfo.StreamSamples; i++)
	{
		I_Windowed[i] = IQ[i * 2] * Window_Coefficient[i];
		Q_Windowed[i] = IQ[i * 2 + 1] * Window_Coefficient[i];
	}

	//FFT
	vector<std::complex<float>> x(IQStream->IQS_StreamInfo.StreamSamples);
	vector<std::complex<float>> y(IQStream->IQS_StreamInfo.StreamSamples);

	for (uint32_t i = 0; i < IQStream->IQS_StreamInfo.StreamSamples; i++)
	{
		x[i]._Val[0] = I_Windowed[i];
		x[i]._Val[1] = Q_Windowed[i];
	}

	fftplan plan = fft_create_plan(IQStream->IQS_StreamInfo.StreamSamples, x.data(), y.data(), LIQUID_FFT_FORWARD, 0); //下发配置
	fft_execute(plan);                                                                                                 //执行FFT

	for (uint32_t i = 0; i < IQStream->IQS_StreamInfo.StreamSamples; i++)
	{
		PowerSpec_dBm[i] = 10 * log10(pow(y[i].real(), 2) + pow(y[i].imag(), 2)) + 10 * log10(20) - 20 * log10(IQStream->IQS_StreamInfo.StreamSamples) + 13.3279 + 20 * log10(IQStream->IQS_ScaleToV);
		double step = IQStream->IQS_StreamInfo.IQSampleRate / (IQStream->IQS_StreamInfo.StreamSamples - 1);
		Freq_Hz[i] = IQStream->IQS_Profile.CenterFreq_Hz - (IQStream->IQS_StreamInfo.IQSampleRate / 2) + i * step;
	}

	double temp = 0;
	for (int i = 0; i < IQStream->IQS_StreamInfo.StreamSamples / 2; i++)
	{
		temp = PowerSpec_dBm[i];
		PowerSpec_dBm[i] = PowerSpec_dBm[IQStream->IQS_StreamInfo.StreamSamples / 2 + i];
		PowerSpec_dBm[IQStream->IQS_StreamInfo.StreamSamples / 2 + i] = temp;
	}

	fft_destroy_plan(plan);
	return Status;
}


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

	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的返回值进行相对应的错误处理

	IQStream_TypeDef IQStream;         //存放IQ数据相关的参数，包括IQ数据、ScaleToV、I_MaxValue等等。
	IQS_Profile_TypeDef IQSIn;         //IQS输入配置，包括起始频率、终止频率、RBW、参考电平等。
	IQS_Profile_TypeDef IQSOut;        //IQS输出配置。
	IQS_StreamInfo_TypeDef StreamInfo; //当前配置下IQ数据信息，包括带宽,IQ单路采样率等。

	Status = IQS_ProfileDeInit(&Device, &IQSIn); //初始化配置IQS模式下的相关参数。

	IQSIn.CenterFreq_Hz = 1e9;					//配置中心频率。        
	IQSIn.RefLevel_dBm = 0;						//配置参考电平。
	IQSIn.DataFormat = Complex16bit;			//配置IQ数据格式。
	IQSIn.TriggerMode = FixedPoints;			//配置触发模式。
	IQSIn.TriggerSource = Bus;					//配置触发源为内部总线触发。
	IQSIn.DecimateFactor = 2;					//配置抽取倍数。
	IQSIn.BusTimeout_ms = 5000;					//配置总线超时时间。
	IQSIn.TriggerLength = 16242;				//配置采样点数。
	//IQSIn.DCCancelerMode = DCCAutoOffsetMode;	//1抽取模式下时，请设置此参数开启高通滤波器并自动偏置以避免直漂影响

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

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

	vector<int16_t> AlternIQStream(StreamInfo.StreamSamples * 2); //创建数组存放IQ数据，数据存放格式为IQIQIQ...。
	vector<double> Frequency(StreamInfo.StreamSamples);           //创建I路数据数组。
	vector<float> Spectrum(StreamInfo.StreamSamples);             //创建Q路数据数组。

	//循环获取并FFT
	while (1)
	{
		Status = IQS_BusTriggerStart(&Device); //调用IQS_BusTriggerStart触发设备。

		uint32_t IQPoints = 0;
		for (int i = 0; i < StreamInfo.PacketCount; i++)
		{
			Status = IQS_GetIQStream_PM1(&Device, &IQStream); //获取IQ数据。
			if (Status == APIRETVAL_NoError)
			{
				if (i != StreamInfo.PacketCount - 1) //当前包不为最后一包时。
				{
					memcpy(AlternIQStream.data() + IQPoints, IQStream.AlternIQStream, sizeof(int16_t) * 2 * StreamInfo.PacketSamples);
					IQPoints += StreamInfo.PacketSamples * 2;
				}
				else //当前包为最后一包时。
				{
					if (StreamInfo.StreamSamples % StreamInfo.PacketSamples != 0) //最后一包IQ数据不满一整包时。
					{
						memcpy(AlternIQStream.data() + IQPoints, IQStream.AlternIQStream, sizeof(int16_t) * 2 * (StreamInfo.StreamSamples % StreamInfo.PacketSamples));
					}
					else //最后一包IQ数据为一整包时。
					{
						memcpy(AlternIQStream.data() + IQPoints, IQStream.AlternIQStream, sizeof(int16_t) * 2 * StreamInfo.PacketSamples);
					}
				}
			}

			else //当Status不为0时，根据Status的返回值进行相对应的错误处理
			{

				IQS_ErrorHandlingExceptOpenAndConfiguration(Status, &Device, DevNum, &BootProfile, &BootInfo, &IQSIn, &IQSOut, &StreamInfo);

			}
		}
		float ScaleToV = IQStream.IQS_ScaleToV;
		IQStream.AlternIQStream = AlternIQStream.data();                 //让AlternIQStream指针指向实际存在IQ数据的地址。
		FFT_IQSToSpectrum(&IQStream, Frequency.data(), Spectrum.data()); //执行IQ转频谱函数。
	}

	Status = IQS_BusTriggerStop(&Device); //调用IQS_BusTriggerStop停止触发设备。

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

	return 0;
}
