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

/*README
有关EIO拓展板的连接和使用请参考《SA NX系列频谱分析仪用户指南》中的GNSS模块的使用相关章节
*/

#define IS_USB 1 // 默认假设是 USB，若接口为ETH，请为此行代码添加注释

int IQS_Enable_GNSS_10MHz()
{
	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的返回值进行相对应的错误处理。

	/*设置GNSS外部天线*/
	GNSSAntennaState_TypeDef GNSSAntennaState; //启动GNSS天线信息。
	GNSSAntennaState = GNSS_AntennaExternal;   //配置外部天线。

	//设置GNSS天线状态
	Status = Device_SetGNSSAntennaState(&Device, GNSSAntennaState); //下发GNSS天线相关配置。

	GNSSInfo_TypeDef GNSSInfo = { 0 }; //当前天线配置下的GNSS信息。

	//每隔1s输出一次当前GNSS的锁定状态，直至设备成功锁定GNSS模块。
	while (!GNSSInfo.GNSS_LockState)
	{
		Status = Device_GetGNSSInfo_Realtime(&Device, &GNSSInfo);				//实时获取GNSS设备状态。
		cout << "GNSS_LockState: " << (int16_t)GNSSInfo.GNSS_LockState << endl; //输出GNSS天线锁定状态 GNSS_LockState=1 为锁定。
		this_thread::sleep_for(chrono::milliseconds(1000));					    //延时1s。
	}

	DOCXOWorkMode_TypeDef DOCXOWorkMode; //配置DOCXO天线状态。
	DOCXOWorkMode = DOCXO_LockMode;

	/*设置和获取DOCXO的状态*/
	uint16_t HardwareVersion = BootInfo.DeviceInfo.HardwareVersion;
	uint16_t OCXO_Enable = (HardwareVersion >> 10) & 0x3;

	if (OCXO_Enable)
	{
		Status = Device_SetDOCXOWorkMode(&Device, DOCXOWorkMode); //设置GNSS中DOCXO工作状态

		auto start_time = std::chrono::high_resolution_clock::now(); // 获取当前时间

		Status = Device_GetGNSSInfo_Realtime(&Device, &GNSSInfo); //获取GNSS设备状态

		// 检查锁定状态
		if (GNSSInfo.DOCXO_LockState)
		{
			cout << endl;
			// 打印当前GNSS_LockState状态
			cout << "GNSS_LockState: " << (int16_t)GNSSInfo.GNSS_LockState << endl;
			// 打印当前DOCXO_LockState状态
			cout << "DOCXO_LockState: " << (int16_t)GNSSInfo.DOCXO_LockState << endl;
		}
		else
		{
			while (1)
			{
				Status = Device_GetGNSSInfo_Realtime(&Device, &GNSSInfo); //获取GNSS设备状态

				// 检查锁定状态
				if (GNSSInfo.DOCXO_LockState == 1 && GNSSInfo.GNSS_LockState == 1)
				{
					cout << endl;
					// 打印当前GNSS_LockState状态
					cout << "GNSS_LockState: " << (int16_t)GNSSInfo.GNSS_LockState << endl;
					// 打印当前DOCXO_LockState状态
					cout << "DOCXO_LockState: " << (int16_t)GNSSInfo.DOCXO_LockState << endl;
					break;
				}
				if (GNSSInfo.GNSS_LockState == 0)
				{
					DOCXOWorkMode = DOCXO_LockMode;
					Status = Device_SetDOCXOWorkMode(&Device, DOCXOWorkMode); //设置GNSS中DOCXO工作状态
				}
				// 打印当前GNSS_LockState状态
				cout << "GNSS_LockState: " << (int16_t)GNSSInfo.GNSS_LockState << endl;

				// 打印当前DOCXO_LockState状态
				cout << "DOCXO_LockState: " << (int16_t)GNSSInfo.DOCXO_LockState << endl;

				// 检查超时：是否超过180秒
				auto current_time = std::chrono::high_resolution_clock::now();
				auto duration = std::chrono::duration_cast<std::chrono::duration<double, std::ratio<1, 1>>>(current_time - start_time);
				// 如果超时超过600秒，退出循环
				if (duration.count() > 600)
				{
					cout << "The DOCXO is not locked, please check whether the cable connection is normal." << endl;
					break;
					return 0;
				}
				this_thread::sleep_for(chrono::milliseconds(1000)); //延时1s
			}
			cout << "DOCXO is locked！" << endl;
		}
	}

	/*初始化配置*/
	IQS_Profile_TypeDef IQS_ProfileIn;  //IQS模式输入配置，包括中心频率、抽取倍数、参考电平等。
	IQS_Profile_TypeDef IQS_ProfileOut; //IQS模式输出配置。
	IQS_StreamInfo_TypeDef StreamInfo;  //当前配置下的IQ数据流相关信息。

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

	/*下发IQS模式的相关参数*/
	IQS_ProfileIn.CenterFreq_Hz = 1e9;						                    //配置中心频率。
	IQS_ProfileIn.RefLevel_dBm = 0;							                    //配置参考电平。
	IQS_ProfileIn.DecimateFactor = 2;								            //配置抽取倍数。
	IQS_ProfileIn.DataFormat = Complex16bit;							        //配置IQ数据格式。
	IQS_ProfileIn.TriggerMode = FixedPoints;							        //配置触发模式。FixedPoints模式是触发信号的上升沿开始采样。采到TriggerLength个点后结束采样。Adaptive模式是触发信号的上升沿开始采样，下降沿结束采样。
	IQS_ProfileIn.TriggerSource = Bus;									        //配置触发源。
	IQS_ProfileIn.ReferenceClockSource = ReferenceClockSource_Internal_Premium; //使用GNSS模块的10MHz参考时钟（仅高品质GNSS模块可用）
	IQS_ProfileIn.ReferenceClockFrequency = 10e6;					            //将参考时钟频率设置为10MHz
	IQS_ProfileIn.TriggerLength = 16242;					                    //配置单次触发采集的点数。仅当TriggerMode设置为FixedPoints时生效。

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

	/*获取IQ数据-*/
	IQStream_TypeDef IQStream;						  //用于存放IQ数据包数据的结构体，包括IQ数据和相关配置信息。
	bool tag = false;		                          //当IQS_ProfileIn.TriggerMode = Adaptive时，获取数据之前控制触发1次。
	vector<int16_t> I_Data(StreamInfo.StreamSamples); //创建I路数据数组。
	vector<int16_t> Q_Data(StreamInfo.StreamSamples); //创建Q路数据数组。

	if (IQS_ProfileIn.TriggerMode == Adaptive)
	{
		I_Data.resize(StreamInfo.PacketSamples); //重新调整I_Data的大小，用于存放Adaptive模式下I路数据。
		Q_Data.resize(StreamInfo.PacketSamples); //重新调整Q_Data的大小，用于存放Adaptive模式下Q路数据。
	}

	while (1)
	{
		if (IQS_ProfileOut.TriggerMode == Adaptive && tag != true) //当IQS_ProfileIn.TriggerMode = Adaptive时仅触发一次。
		{
			Status = IQS_BusTriggerStart(&Device);			       //调用IQS_BusTriggerStart触发设备。若触发源为外部触发，则不需要调用此函数。
			tag = true;
		}
		if (IQS_ProfileOut.TriggerMode == FixedPoints)             //当RTA_ProfileOut.TriggerMode = FixedPoints时每次循环均触发。
		{
			Status = IQS_BusTriggerStart(&Device);                 //若触发源为外部触发，则不需要调用此函数。
		}
		/*获取IQ数据*/
		for (int j = 0; j < StreamInfo.PacketCount; j++)           //仅当TriggerMode为FixedPoints时生效，当TriggerMode为Adaptive不生效，StreamInfo.PacketCount为1。
		{
			Status = IQS_GetIQStream_PM1(&Device, &IQStream);      //获取IQ数据包、触发信息、I路数据最大值及最大值数组下标。

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

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

				//当TriggerMode设置为FixedPoints时，判断最后一包是不是16242个点。
				if (j == StreamInfo.PacketCount - 1 && StreamInfo.StreamSamples % StreamInfo.PacketSamples != 0 && IQS_ProfileOut.TriggerMode == FixedPoints) //可能最后一包不满一整包（16242个点）；所以只需要循环不满一包的点数
				{
					Points = StreamInfo.StreamSamples % StreamInfo.PacketSamples;
				}
				//分别取出IQ两路数据。
				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停止触发设备。若触发源为外部触发，则不需要调用此函数。

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