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

namespace HTRA_CSharp_Examples
{
    class DSP_LPF
    {
		public void Example()
		{
			#region 1 Open device

			int Status = 0;              //Function return value.
			IntPtr Device = IntPtr.Zero; //Current device memory address.
			int DevNum = 0;              //Specify device number.

			HtraApi.BootProfile_TypeDef BootProfile = new HtraApi.BootProfile_TypeDef(); //Boot configuration structure, including physical interface, power supply, etc.
			HtraApi.BootInfo_TypeDef BootInfo = new HtraApi.BootInfo_TypeDef();          //Boot info structure, including device information, USB speed, etc.

			BootProfile.DevicePowerSupply = HtraApi.DevicePowerSupply_TypeDef.USBPortAndPowerPort; //Use both USB data port and independent power port for dual power supply.

			//If the device data interface is USB, run directly. If it's Ethernet, change #if true to #if false
#if true
			BootProfile.PhysicalInterface = HtraApi.PhysicalInterface_TypeDef.USB;
#else
			//Configure network 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 is opened successfully");
			}

			/*If device opening fails, return an error message. If the following errors occur, the device will not operate normally. It is recommended to follow the prompts and try reopening the device.*/
			else
			{
				switch (Status)
				{
					case HtraApi.APIRETVAL_ERROR_BusOpenFailed:
						System.Console.WriteLine("Error - Check the device power supply, data cable connection and driver installation");
						return;

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

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

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

					case HtraApi.APIRETVAL_ERROR_DeviceSpecFileIsMissing:
						System.Console.WriteLine("Error - Device specification file is missing");
						return;
					
					case HtraApi.APIRETVAL_ERROR_FirmwareVersionMismatch:
						System.Console.WriteLine("Error - Firmware Version Mismatch");
						break;

					default:
						System.Console.WriteLine("Return other errors! Status = " + Status);
						return;
				}
			}
			#endregion

			#region 2 Configure Parameters
			HtraApi.IQStream_TypeDef IQStream = new HtraApi.IQStream_TypeDef();				  //Parameters for retrieving IQ data under the current configuration, including IQ data, ScaleToV, I_MaxValue, etc.
			HtraApi.IQS_Profile_TypeDef IQSIn = new HtraApi.IQS_Profile_TypeDef();			  //IQS mode input configuration, including center frequency, decimation factor, reference level, etc.
			HtraApi.IQS_Profile_TypeDef IQSOut = new HtraApi.IQS_Profile_TypeDef();		      //IQS mode output configuration.
			HtraApi.IQS_StreamInfo_TypeDef StreamInfo = new HtraApi.IQS_StreamInfo_TypeDef(); //IQ data stream related information under the current configuration.

			HtraApi.IQS_ProfileDeInit(ref Device, ref IQSIn); //Initialize the configuration parameters for IQS mode by calling this function.

			IQSIn.CenterFreq_Hz = 1e9;                                   //Configure the center frequency.
			IQSIn.RefLevel_dBm = 0;                                      //Configure the reference level.
			IQSIn.DataFormat = HtraApi.DataFormat_TypeDef.Complex16bit;  //Configure IQ data format.
			IQSIn.TriggerMode = HtraApi.TriggerMode_TypeDef.FixedPoints; //Configure the trigger mode.
			IQSIn.TriggerSource = HtraApi.IQS_TriggerSource_TypeDef.Bus; //Configure trigger source as internal bus trigger.
			IQSIn.DecimateFactor = 1;                                    //Configure decimation factor.
			IQSIn.BusTimeout_ms = 5000;                                  //Configure bus timeout.
			IQSIn.TriggerLength = 16242;                                 //Configure the number of sample points, only effective when TriggerMode is set to FixedPoints.

			Status = HtraApi.IQS_Configuration(ref Device, ref IQSIn, ref IQSOut, ref StreamInfo); //Apply the IQS mode configuration by calling this function.

			Int16[] AlternIQStream = new Int16[StreamInfo.StreamSamples * 2]; //Create an array to store IQ data, stored in the format of IQIQIQ...

			//Open DSP functionality
			IntPtr DSP = IntPtr.Zero;  //Allocate memory for Digital Signal Processing address
			HtraApi.DSP_Open(ref DSP); //Enable signal processing functionality

			//Low-pass filter configuration
			HtraApi.Filter_TypeDef LPF_ProfileIn = new HtraApi.Filter_TypeDef();   //Configure parameters for generating filter coefficients, including filter taps, cutoff frequency, stopband attenuation, etc.
			HtraApi.Filter_TypeDef LPF_ProfileOut = new HtraApi.Filter_TypeDef();  //Output parameters for generating filter coefficients.
			HtraApi.IQStream_TypeDef IQStreamOut = new HtraApi.IQStream_TypeDef(); //IQ data after low-pass filtering.

			HtraApi.DSP_LPF_DeInit(ref LPF_ProfileIn); //Initialize parameters for LPF mode configuration.

			LPF_ProfileIn.As = 90;    //Configure the stopband attenuation of the filter.
			LPF_ProfileIn.fc = 0.25f; //Configure the cutoff frequency of the filter.
			LPF_ProfileIn.mu = 0;     //Configure the fractional sample offset.
			LPF_ProfileIn.n = 90;     //Configure the order of the filter.

			LPF_ProfileIn.Samples = (UInt32)StreamInfo.StreamSamples; //Configure the number of sample points.

			HtraApi.DSP_LPF_Configuration(ref DSP, ref LPF_ProfileIn, ref LPF_ProfileOut); //Configure the parameters for the Low-Pass Filter (LPF).

			//FFT Mode Configuration
			HtraApi.DSP_FFT_TypeDef IQToSpectrumIn = new HtraApi.DSP_FFT_TypeDef();   //Configure the parameters to be applied for FFT mode, including FFT points, detection method, etc.
			HtraApi.DSP_FFT_TypeDef IQToSpectrumOut = new HtraApi.DSP_FFT_TypeDef();  //Feedback the actual parameters applied for FFT mode, including FFT points, detection method, etc.
			UInt32 TracePoints = 0;													  //This parameter stores the number of spectrum points after FFT.

			HtraApi.DSP_FFT_DeInit(ref IQToSpectrumIn); //Initialize the configuration for FFT mode parameters by calling this function.

			IQToSpectrumIn.Calibration = false;													//Configure whether calibration is enabled: 0 for no calibration, other values for enabling.
			IQToSpectrumIn.DetectionRatio = 1;													//Configure the detection ratio.
			IQToSpectrumIn.TraceDetector = HtraApi.TraceDetector_TypeDef.TraceDetector_PosPeak; //Configure the detection method.
			IQToSpectrumIn.FFTSize = (UInt32)StreamInfo.StreamSamples;							//Configure the number of FFT points.
			IQToSpectrumIn.Intercept = 1;														//Configure the intercept ratio.
			IQToSpectrumIn.SamplePts = (UInt32)StreamInfo.StreamSamples;						//Configure the number of sample points.
			IQToSpectrumIn.WindowType = HtraApi.Window_TypeDef.FlatTop;							//Configure the window type.

			double RBWRatio = 0; //This parameter returns the RBW ratio, where RBW = RBWRatio * StreamInfo.IQSampleRate.

			HtraApi.DSP_FFT_Configuration(ref DSP, ref IQToSpectrumIn, ref IQToSpectrumOut, ref TracePoints, ref RBWRatio); //Apply the configuration for FFT mode.

			double[] Frequency = new double[TracePoints]; //Create an array to store frequency data.
			float[] Spectrum = new float[TracePoints];    //Create an array to store amplitude data.
			#endregion

			#region 3 Data Collection
			while (true)
			{
				Status = HtraApi.IQS_BusTriggerStart(ref Device); //Call IQS_BusTriggerStart to trigger the device. If the trigger source is external, this function does not need to be called.

				for (UInt64 i = 0; i < StreamInfo.PacketCount; i++)
				{
					Status = HtraApi.IQS_GetIQStream_PM1(ref Device, ref IQStream); //Retrieve the IQ data.
					if (Status == 0)
					{
						unsafe
						{
							System.Int16* IQ = (System.Int16*)IQStream.AlternIQStream; //Used to extract IQ data
							UInt32 points = StreamInfo.PacketSamples;                  //Number of data points in the packet
							if (i == StreamInfo.PacketCount - 1 && StreamInfo.StreamSamples % StreamInfo.PacketSamples != 0) //Check for the last packet, and if it is incomplete
							{
								points = (UInt32)StreamInfo.StreamSamples % StreamInfo.PacketSamples; //Calculate the number of data points in the last packet
							}
							for (int j = 0; j < points; j++) //Write IQ data to the array
							{
								AlternIQStream[j + StreamInfo.PacketSamples * (int)i] = IQ[j];
							}
						}
					}

					else
					{
						/*If data retrieval fails, return an error message. In the case of the following errors, it is recommended to follow the suggested actions.*/
						switch (Status)
						{
							case HtraApi.APIRETVAL_ERROR_BusError:  //If IQS_GetIQStream returns -8, it is recommended to reconfigure the parameters before retrieving again.
								System.Console.WriteLine("Error - Bus communication error");
								Status = HtraApi.IQS_Configuration(ref Device, ref IQSIn, ref IQSOut, ref StreamInfo);
								break;

							case HtraApi.APIRETVAL_ERROR_BusDataError: //If IQS_GetIQStream returns -9, it is recommended to reconfigure the parameters before retrieving again.
								System.Console.WriteLine("Error - The data content is incorrect");
								Status = HtraApi.IQS_Configuration(ref Device, ref IQSIn, ref IQSOut, ref StreamInfo);
								break;

							case HtraApi.APIRETVAL_WARNING_BusTimeOut: //If IQS_GetIQStream returns -10, it is recommended to check if the trigger source is working properly before retrieving again.
								System.Console.WriteLine("Warning - Get data timed out, check if the trigger source is triggered normally");
								break;

							case HtraApi.APIRETVAL_WARNING_IFOverflow: //If IQS_GetIQStream returns -12, it is recommended to reconfigure the parameters before retrieving again
								System.Console.WriteLine("WARNING - IF saturation is recommended to be reconfigured, reference level <= signal power");
								break;

							case HtraApi.APIRETVAL_WARNING_ReconfigurationIsRecommended: //If IQS_GetIQStream returns -14, it is recommended to reconfigure the parameters before retrieving again
								System.Console.WriteLine("Warning - The current device temperature has changed significantly relative to the configured temperature, and it is recommended to reconfigure \n");
								break;

							case HtraApi.APIRETVAL_WARNING_ClockUnlocked_SYSCLK: //If IQS_GetIQStream returns -15, it is recommended to reconfigure the parameters before retrieving again
								System.Console.WriteLine("Warning - There may be an anomaly in the hardware status of the device and reconfiguration is recommended\n");
								break;
							default:
								break;
						}
						System.Console.WriteLine("Status = " + Status);
					}

				}

				unsafe
				{
					fixed (Int16* ptr = AlternIQStream)
					{
						IQStream.AlternIQStream = (IntPtr)ptr;                                        //Point the AlternIQStream pointer to the actual IQ data address.
						HtraApi.DSP_LPF_Execute_Complex(ref DSP, ref IQStream, ref IQStreamOut);      //Perform low-pass filtering.
						HtraApi.DSP_FFT_IQSToSpectrum(ref DSP, ref IQStreamOut, Frequency, Spectrum); //Perform IQ to spectrum conversion.
					}
				}

				for (int i = 0; i < TracePoints; i++)
				{
					System.Console.WriteLine("Frequency:" + Frequency[i].ToString() + ",  " + "Spectrum:" + Spectrum[i].ToString());
				}
			}
			#endregion

			#region 4 Close
			Status = HtraApi.IQS_BusTriggerStop(ref Device); //Call IQS_BusTriggerStop to stop triggering the device. If the trigger source is external, this function does not need to be called.
			HtraApi.DSP_Close(ref DSP);
			HtraApi.Device_Close(ref Device);
			#endregion
		}
	}
}
