using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace HTRA_CSharp_Examples
{
    class TraceAnalysis_PhaseNoise
	{

		public static int findMaxPowerIndex(float[] powerSpec, int size)
		{
			int maxIndex = 0;
			float maxValue = powerSpec[0];

			for (int i = 1; i < size; ++i)
			{
				if (powerSpec[i] > maxValue)
				{
					maxValue = powerSpec[i];
					maxIndex = i;
				}
			}

			return maxIndex;
		}
		public static void copyArrayF(double[] source, double[] destination, int startIndex, int size)
		{
			for (int i = 0; i < size; ++i)
			{
				destination[i] = source[startIndex + i];
			}
		}

		public static void copyArrayP(float[] source, float[] destination, int startIndex, int size)
		{
			for (int i = 0; i < size; ++i)
			{
				destination[i] = source[startIndex + i];
			}
		}
		public void Example()
		{
			#region 1 打开设备

			int Status = 0;                  //函数的返回值，返回值表示当前的错误代码：错误代码=0表示无错误，其他错误的代码的详细信息请查看htra_api.h文件最下方的错误代码对照表 或 查看API指南中的附录1。
			IntPtr Device = IntPtr.Zero;     //此参数为频谱仪存储频谱仪设备信息的内存地址，调用此函数后，函数将返回当前打开的设备内存地址。后续在调用其他API时，必须通过此地址参数来索引此次打开的设备。
			int DevNum = 0;                  //指定设备号，当主机连接有多台设备时，可通过此设备号来选择相应的设备，设备号从0开始累加。

			//启动配置结构体，用于配置与设备启动相关参数。
			HtraApi.BootProfile_TypeDef BootProfile = new HtraApi.BootProfile_TypeDef();
			//反馈启动配置信息结构体，告知用户当前设备的设备号、固件版本等信息。
			HtraApi.BootInfo_TypeDef BootInfo = new HtraApi.BootInfo_TypeDef();

			BootProfile.DevicePowerSupply = HtraApi.DevicePowerSupply_TypeDef.USBPortAndPowerPort;         //使用USB数据端口及独立电源端口双供电。
			//设备数据接口为USB时直接运行，为网口时将 #if true 改为 #if false
#if true
			BootProfile.PhysicalInterface = HtraApi.PhysicalInterface_TypeDef.USB;
#else
			//配置网络相关参数
			BootProfile.PhysicalInterface = HtraApi.PhysicalInterface_TypeDef.ETH;
			BootProfile.ETH_IPVersion = HtraApi.IPVersion_TypeDef.IPv4;
			BootProfile.ETH_RemotePort = 5000;
			BootProfile.ETH_ReadTimeOut = 5000;
			BootProfile.ETH_IPAddress = new byte[16];
			BootProfile.ETH_IPAddress[0] = 192;
			BootProfile.ETH_IPAddress[1] = 168;
			BootProfile.ETH_IPAddress[2] = 1;
			BootProfile.ETH_IPAddress[3] = 100;
#endif

			Status = HtraApi.Device_Open(ref Device, DevNum, ref BootProfile, ref BootInfo);          //打开设备

			if (Status == 0)
			{
				System.Console.WriteLine("设备打开成功");
			}

			/*设备打开失败，返回错误提示，发生以下错误时，设备无法正常运行，建议按照提示操作后重新打开设备*/
			else
			{
				switch (Status)
				{
					case HtraApi.APIRETVAL_ERROR_BusOpenFailed:
						System.Console.WriteLine("错误-检查设备供电、数据线连接，检查驱动程序是否正确安装");
						return;

					case HtraApi.APIRETVAL_ERROR_RFACalFileIsMissing:
						System.Console.WriteLine("错误-射频校准文件丢失");
						return;

					case HtraApi.APIRETVAL_ERROR_IFACalFileIsMissing:
						System.Console.WriteLine("错误-中频校准文件丢失");
						return;

					case HtraApi.APIRETVAL_ERROR_DeviceConfigFileIsMissing:
						System.Console.WriteLine("错误-配置文件丢失");
						return;

					case HtraApi.APIRETVAL_ERROR_DeviceSpecFileIsMissing:
						System.Console.WriteLine("错误-设备规格文件丢失");
						return;

					default:
						System.Console.WriteLine("其他错误！ \n");
						return;
				}
			}
			#endregion

			#region 2 配置参数
			HtraApi.SWP_Profile_TypeDef SWP_ProfileIn = new HtraApi.SWP_Profile_TypeDef();       //此结构体用于配置需要下发的SWP模式参数，包括起始频率、终止频率、RBW、参考电平等等
			Status = HtraApi.SWP_ProfileDeInit(ref Device, ref SWP_ProfileIn);            //调用此函数初始化配置SWP模式的相关参数

			SWP_ProfileIn.CenterFreq_Hz = 1e9;
			SWP_ProfileIn.FreqAssignment = HtraApi.SWP_FreqAssignment_TypeDef.CenterSpan;

			HtraApi.SWP_Profile_TypeDef SWP_ProfileOut = new HtraApi.SWP_Profile_TypeDef();      //此结构体用于反馈实际下发的SWP模式参数，包括起始频率、终止频率、RBW、参考电平等等
			HtraApi.SWP_Profile_TypeDef SWP_ProfileSetOut = new HtraApi.SWP_Profile_TypeDef();
			HtraApi.SWP_TraceInfo_TypeDef TraceInfo = new HtraApi.SWP_TraceInfo_TypeDef();       //此结构体用于反馈当前配置下的迹线信息，包括迹线点数、调频点数等等
			byte IfDoConfig = 1;
			HtraApi.SWP_AutoSet(ref Device, HtraApi.SWPApplication_TypeDef.SWPPhaseNoiseMeas, ref SWP_ProfileIn, ref SWP_ProfileSetOut, ref TraceInfo, IfDoConfig);

			SWP_ProfileSetOut.RBW_Hz = 200;

			Status = HtraApi.SWP_Configuration(ref Device, ref SWP_ProfileSetOut, ref SWP_ProfileOut, ref TraceInfo); //通过调用此函数下发SWP模式的相关配置。

			HtraApi.DeviceState_TypeDef DeviceState = new HtraApi.DeviceState_TypeDef();                                           //存放设备状态的结构体，包括设备温度、设备射频状态等等
			if (Status == 0)
			{
				System.Console.WriteLine("配置下发成功");
				Status = HtraApi.Device_QueryDeviceState_Realtime(ref Device, ref DeviceState);      //获取设备状态，包括设备温度、设备射频状态等等
			}

			/*若返回值为-8，说明总线通信错误，多次重新下发配置*/
			if (Status == HtraApi.APIRETVAL_ERROR_BusError)
			{
				System.Console.WriteLine("错误-总线通信错误");

				int CallCount = 0;

				while (Status == HtraApi.APIRETVAL_ERROR_BusError)
				{
					CallCount++;
					Status = HtraApi.SWP_Configuration(ref Device, ref SWP_ProfileIn, ref SWP_ProfileOut, ref TraceInfo);
					if (CallCount == 20)                                                                     //若重配20次，依然返回-8，请联系技术支持人员
					{
						System.Console.WriteLine("20次调用SWP_Configuration仍然总线通信错误, 请联系技术支持人员");
						return;
					}
				}
			}

			#endregion

			#region 3 数据采集
			double[] Frequency = new double[TraceInfo.FullsweepTracePoints];         //存放整条频率数据
			float[] PowerSpec_dBm = new float[TraceInfo.FullsweepTracePoints];       //存放整条幅度数据
			HtraApi.MeasAuxInfo_TypeDef MeasAuxInfo = new HtraApi.MeasAuxInfo_TypeDef(); //此结构体用于存放测量数据的辅助信息

			while (true)
			{
				Status = HtraApi.SWP_GetFullSweep(ref Device, Frequency, PowerSpec_dBm, ref MeasAuxInfo);
				int maxPowerIndex = findMaxPowerIndex(PowerSpec_dBm, TraceInfo.FullsweepTracePoints);// 查找功率数组中的峰值索引
				int newSize = TraceInfo.FullsweepTracePoints - maxPowerIndex;       // 确定新的数组大小，从峰值到末尾
				double[] NewFrequencyArray = new double[newSize];                   //建立动态数组，存放新的频率数据
				float[] NewPowerSpec_dBm = new float[newSize];                       //建立动态数组，存放新的幅度数据
				copyArrayF(Frequency, NewFrequencyArray, maxPowerIndex, newSize);// 复制频率数组中的峰值及其后续点到新数组
				copyArrayP(PowerSpec_dBm, NewPowerSpec_dBm, maxPowerIndex, newSize);//复制幅度数组中的峰值及后续点数到新数组

				HtraApi.SmoothMethod_TypeDef SmoothMethod = new HtraApi.SmoothMethod_TypeDef();
				SmoothMethod = HtraApi.SmoothMethod_TypeDef.MovingAvrage;               //选择平滑去噪技术
				float smoothing_start_frequency = 10e3F;      //设置起始平滑的位置
				uint IndexOffset = 0;         //初始化偏移载波的点数
				float min_difference = float.MaxValue;  // 初始化绝对差值
				double difference = 0;
				// 遍历频率数组，寻找最接近目标的差值
				for (uint m = 1; m < newSize; m++)
				{
					difference = (NewFrequencyArray[m] - NewFrequencyArray[0]);

					// 检查差值是否小于等于 smoothing_start_frequency 且与目标最接近
					if (difference <= smoothing_start_frequency)
					{
						IndexOffset = m; // 更新偏移载波的点数
					}
				}

				uint WindowLength = (uint)((1 / 100) * newSize);    //选取百分之1的点数为窗的长度
				uint PolynomialOrder = 0;          //多项式阶数
				HtraApi.DSP_TraceSmooth(NewPowerSpec_dBm, (UInt32)newSize, SmoothMethod, IndexOffset, WindowLength, PolynomialOrder); // 迹线平滑

				double[] OffsetFreqs = { 100, 1000, 10000, 100000, 1000000, 10000000 };//频率偏移数组
				const int OffsetFreqsToAnalysis = 6;                                //需要分析的频偏频点数
				double[] CarrierFreqOut = new double[OffsetFreqsToAnalysis] { 0, 0, 0, 0, 0, 0 };//实际的载波频率
				float[] PhaseNoiseOut_dBc = new float[OffsetFreqsToAnalysis] { 0, 0, 0, 0, 0, 0 };                  //频偏数组对应的相位噪声

				Status = HtraApi.DSP_TraceAnalysis_PhaseNoise(NewFrequencyArray, NewPowerSpec_dBm, OffsetFreqs, (uint)newSize, OffsetFreqsToAnalysis, CarrierFreqOut, PhaseNoiseOut_dBc);//分析迹线的相位噪声

				//结果归一化,归一化到每Hz
				for (int i = 0; i < 6; i++)
				{
					PhaseNoiseOut_dBc[i] -= (float)(10 * Math.Log10(SWP_ProfileOut.RBW_Hz));
				}
				Console.WriteLine("相位噪声 (dBc/Hz):");
				for (int i = 0; i < 6; i++)
				{
					Console.WriteLine($"[{i}] = {PhaseNoiseOut_dBc[i]:F3} dBc/Hz");
				}
			}
			#endregion

			#region 4 关闭
			HtraApi.Device_Close(ref Device);
			#endregion
		}
	}
}
