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 Open Device

			int Status = 0;                  //Return value indicating current error code: 0 means no error. Refer to htra_api.h or API manual appendix for other codes.
			IntPtr Device = IntPtr.Zero;     //Pointer to memory address storing device info. Required for all subsequent API calls.
			int DevNum = 0;                  //Device number. When multiple devices are connected, use this to select the target. Starts from 0.

			//Device number. When multiple devices are connected, use this to select the target. Starts from 0.
			HtraApi.BootProfile_TypeDef BootProfile = new HtraApi.BootProfile_TypeDef();
			//Structure to receive boot info such as serial number, firmware version, etc.
			HtraApi.BootInfo_TypeDef BootInfo = new HtraApi.BootInfo_TypeDef();

			BootProfile.DevicePowerSupply = HtraApi.DevicePowerSupply_TypeDef.USBPortAndPowerPort;         //Use both USB and external power supply.
			//If the device interface is USB, run directly. If it is Ethernet, change #if true to #if false.
#if true
			BootProfile.PhysicalInterface = HtraApi.PhysicalInterface_TypeDef.USB;                         //Use USB interface for data transmission.
#else
			//Configure network-related parameters
			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);          //Open the device

			if (Status == 0)
			{
				System.Console.WriteLine("Device opened successfully");
			}

			/*If the device fails to open, the following errors may occur. Please follow the instructions to resolve them and try reopening the device.*/
			else
			{
				switch (Status)
				{
					case HtraApi.APIRETVAL_ERROR_BusOpenFailed:
						System.Console.WriteLine("Error - Check power, USB cable, and driver installation");
						return;

					case HtraApi.APIRETVAL_ERROR_RFACalFileIsMissing:
						System.Console.WriteLine("Error - RF calibration file missing");
						return;

					case HtraApi.APIRETVAL_ERROR_IFACalFileIsMissing:
						System.Console.WriteLine("Error - IF calibration file missing");
						return;

					case HtraApi.APIRETVAL_ERROR_DeviceConfigFileIsMissing:
						System.Console.WriteLine("Error - Device configuration file missing");
						return;

					case HtraApi.APIRETVAL_ERROR_DeviceSpecFileIsMissing:
						System.Console.WriteLine("Error - Device specification file missing");
						return;

					case HtraApi.APIRETVAL_ERROR_FirmwareVersionMismatch:
						System.Console.WriteLine("Error - Firmware Version Mismatch");
						break;

					default:
						System.Console.WriteLine("Unknown error! \n");
						return;
				}
			}
			#endregion

			#region 2 Parameter Configuration
			HtraApi.SWP_Profile_TypeDef SWP_ProfileIn = new HtraApi.SWP_Profile_TypeDef();       //Structure to configure SWP mode parameters
			Status = HtraApi.SWP_ProfileDeInit(ref Device, ref SWP_ProfileIn);            //Initialize SWP mode parameters

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

			HtraApi.SWP_Profile_TypeDef SWP_ProfileOut = new HtraApi.SWP_Profile_TypeDef();      //Structure to receive applied SWP settings
			HtraApi.SWP_Profile_TypeDef SWP_ProfileSetOut = new HtraApi.SWP_Profile_TypeDef();
			HtraApi.SWP_TraceInfo_TypeDef TraceInfo = new HtraApi.SWP_TraceInfo_TypeDef();       //Structure to receive trace info such as point count, sweep steps
			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); //Apply SWP mode configuration

			HtraApi.DeviceState_TypeDef DeviceState = new HtraApi.DeviceState_TypeDef();                                           //Structure for device status, including temperature, RF status
			if (Status == 0)
			{
				System.Console.WriteLine("Configuration successful");
				Status = HtraApi.Device_QueryDeviceState_Realtime(ref Device, ref DeviceState);      //Get device status, including temperature, RF status, etc.
			}

			/*If the return value is -8, it indicates a bus communication error. Retry configuration multiple times.*/
			if (Status == HtraApi.APIRETVAL_ERROR_BusError)
			{
				System.Console.WriteLine("Error - Bus communication failure");

				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)                                                                     //If after 20 retries the bus communication error persists, please contact technical support.
					{
						System.Console.WriteLine("20 calls to SWP_Configuration still result in bus communication error. Please contact technical support");
						return;
					}
				}
			}

			#endregion

			#region 3 Data Acquisition
			double[] Frequency = new double[TraceInfo.FullsweepTracePoints];         //Allocate array for storing full frequency sweep data
			float[] PowerSpec_dBm = new float[TraceInfo.FullsweepTracePoints];       //Allocate array for storing full power spectrum data
			HtraApi.MeasAuxInfo_TypeDef MeasAuxInfo = new HtraApi.MeasAuxInfo_TypeDef(); // Structure for storing auxiliary information of the measurement

			while (true)
			{
				Status = HtraApi.SWP_GetFullSweep(ref Device, Frequency, PowerSpec_dBm, ref MeasAuxInfo);
				int maxPowerIndex = findMaxPowerIndex(PowerSpec_dBm, TraceInfo.FullsweepTracePoints);//  Find the index of the peak power in the array
				int newSize = TraceInfo.FullsweepTracePoints - maxPowerIndex;       //  Determine the new array size from peak to end
				double[] NewFrequencyArray = new double[newSize];                   //Create dynamic array for storing trimmed frequency data
				float[] NewPowerSpec_dBm = new float[newSize];                       //Create dynamic array for storing trimmed power data
				copyArrayF(Frequency, NewFrequencyArray, maxPowerIndex, newSize);// Copy the frequency values from the peak index onwards
				copyArrayP(PowerSpec_dBm, NewPowerSpec_dBm, maxPowerIndex, newSize);//Copy the power values from the peak index onwards

				HtraApi.SmoothMethod_TypeDef SmoothMethod = new HtraApi.SmoothMethod_TypeDef();
				SmoothMethod = HtraApi.SmoothMethod_TypeDef.MovingAvrage;               //Select smoothing/denoising method
				float smoothing_start_frequency = 10e3F;      //Set starting frequency for smoothing
				uint IndexOffset = 0;         //Initialize index offset from carrier
				float min_difference = float.MaxValue;  // Initialize minimum absolute difference
				double difference = 0;
				// Traverse the frequency array to find the closest value to the target difference
				for (uint m = 1; m < newSize; m++)
				{
					difference = (NewFrequencyArray[m] - NewFrequencyArray[0]);

					// Check if the difference is less than or equal to smoothing_start_frequency and is the closest to the target
					if (difference <= smoothing_start_frequency)
					{
						IndexOffset = m; // Update the offset index from the carrier frequency
					}
				}

				uint WindowLength = (uint)((1 / 100) * newSize);    //Select 1% of the total points as the smoothing window length
				uint PolynomialOrder = 0;          //Set the polynomial order for smoothing
				HtraApi.DSP_TraceSmooth(NewPowerSpec_dBm, (UInt32)newSize, SmoothMethod, IndexOffset, WindowLength, PolynomialOrder); // Perform trace smoothing

				double[] OffsetFreqs = { 100, 1000, 10000, 100000, 1000000, 10000000 };// Array of frequency offsets for phase noise analysis
				const int OffsetFreqsToAnalysis = 6;                                //Number of offset points to analyze
				double[] CarrierFreqOut = new double[OffsetFreqsToAnalysis] { 0, 0, 0, 0, 0, 0 };//Array to hold the actual carrier frequencies
				float[] PhaseNoiseOut_dBc = new float[OffsetFreqsToAnalysis] { 0, 0, 0, 0, 0, 0 };                  //Array to hold phase noise values corresponding to each offset

				Status = HtraApi.DSP_TraceAnalysis_PhaseNoise(NewFrequencyArray, NewPowerSpec_dBm, OffsetFreqs, (uint)newSize, OffsetFreqsToAnalysis, CarrierFreqOut, PhaseNoiseOut_dBc);//Perform phase noise analysis on the trace

				//Normalize the results to per Hz
				for (int i = 0; i < 6; i++)
				{
					PhaseNoiseOut_dBc[i] -= (float)(10 * Math.Log10(SWP_ProfileOut.RBW_Hz));
				}
				Console.WriteLine("Phase Noise (dBc/Hz):");
				for (int i = 0; i < 6; i++)
				{
					Console.WriteLine($"[{i}] = {PhaseNoiseOut_dBc[i]:F3} dBc/Hz");
				}
			}
			#endregion

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