#include "htra_api.h"
#include "example.h"
#include <iostream>
#include <vector>
#include <algorithm>
#include <iomanip>
using namespace std;

#define IS_USB 1 // The default is to use a USB device. Set IS_USB to 0 if using an Ethernet device.

// By checking whether values in the data_arr array exceed the noise threshold noiseThreshold, this function searches for and records the indexes of peaks into peak_index.
void peak_seek(const vector<float>& data_arr, const float noiseThreshold, vector<int>& peak_index)
{
	bool flag = false; // Used to mark whether currently inside a peak region.
	int indexOfMax;    // Used to store the index of the current peak.

	// Traverse the data array.
	for (int i = 0; i < data_arr.size(); i++)
	{
		// If the current value is greater than the noise threshold, it might be part of a peak.
		if (data_arr[i] > noiseThreshold)
		{
			// If flag is false, it means we weren't in a peak region before, and now we are entering one.
			if (flag == false)
			{
				flag = true;    // Set flag to true, indicating we're now in a peak region.
				indexOfMax = i; // Record the index of the current peak.
			}
			else
			{
				// If already in a peak region and current value is greater than the previous max, update the index.
				if (data_arr[i] > data_arr[indexOfMax])
				{
					indexOfMax = i; // Update the current peak index.
				}
			}
		}
		else
		{
			// If current value is less than the threshold and we were in a peak region before, save the found peak index.
			if (flag == true)
			{
				flag = false;                     // Exit peak region, set flag to false.
				peak_index.push_back(indexOfMax); // Add the found peak index to the result list.
			}
		}
	}
}

// Find signals and spurs, and add corresponding frequency, power, and limit-exceeding power to ssfreq, ssSpec, and ssDelt.
void SigAndSpur(const vector<double>& Freq_Hz, const vector<float>& PowerSpec_dBm, const float limit, vector<double>& ssfreq, vector<float>& ssSpec, vector<float>& ssDelt)
{
	// Define a list to store peak indexes.
	vector<int> peak_index_list(0);

	// Call peak_seek to find peaks in the spectrum greater than the threshold limit, and return a list of their indexes.
	peak_seek(PowerSpec_dBm, limit, peak_index_list);

	// Iterate over each peak index.
	for (int i = 0; i < peak_index_list.size(); i++)
	{
		// Add the frequency corresponding to the peak to ssfreq.
		ssfreq.push_back(Freq_Hz[peak_index_list[i]]);

		// Add the power value corresponding to the peak to ssSpec.
		ssSpec.push_back(PowerSpec_dBm[peak_index_list[i]]);

		// Calculate the difference between current peak power and the threshold limit, and add the result to ssDelt.
		ssDelt.push_back(PowerSpec_dBm[peak_index_list[i]] - limit);
	}
}


int SWP_GetSpectrum_SigAndSpur(void)
{
	int Status = 0;      // Function return status.
	void* Device = NULL; // Memory address of the current device.
	int DevNum = 0;      // Device index number.

	BootProfile_TypeDef BootProfile; // Boot profile including physical interface, power supply method, etc.
	BootInfo_TypeDef BootInfo;       // Boot information including device info, USB rate, etc.

	BootProfile.DevicePowerSupply = USBPortAndPowerPort; // Use both USB data port and separate power supply.

#if IS_USB==1
	// Configure USB interface.
	BootProfile.PhysicalInterface = USB;
#else 
	// Configure Ethernet interface.
	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); // Open the device.

	Device_Open_ErrorHandling(Status, &Device, DevNum, &BootProfile, &BootInfo); // When Status is not 0, handle error based on return value.

	SWP_Profile_TypeDef SWP_ProfileIn;  // SWP input configuration including start/stop frequency, RBW, reference level, etc.
	SWP_Profile_TypeDef SWP_ProfileOut; // SWP output configuration.
	SWP_TraceInfo_TypeDef TraceInfo;    // Trace information under current configuration, including trace points, hop points, etc.

	SWP_ProfileDeInit(&Device, &SWP_ProfileIn); // Initialize SWP-related parameters.

	SWP_ProfileIn.StartFreq_Hz = 9e3;   // Configure start frequency.
	SWP_ProfileIn.StopFreq_Hz = 6.35e9; // Configure stop frequency.
	SWP_ProfileIn.RBW_Hz = 300e3;       // Configure RBW.

	Status = SWP_Configuration(&Device, &SWP_ProfileIn, &SWP_ProfileOut, &TraceInfo); // Send SWP configuration to device.

	SWP_Configuration_ErrorHandling(Status, &Device, DevNum, &BootProfile, &BootInfo, &SWP_ProfileIn, &SWP_ProfileOut, &TraceInfo); // Handle configuration errors if Status is not 0.

	vector<double> Frequency(TraceInfo.FullsweepTracePoints);    // Create frequency array.
	vector<float> PowerSpec_dBm(TraceInfo.FullsweepTracePoints); // Create power spectrum array.
	MeasAuxInfo_TypeDef MeasAuxInfo;                             // Auxiliary measurement info, including: max power index, value, temperature, GPS, timestamp, etc.

	SWP_GetFullSweep(&Device, Frequency.data(), PowerSpec_dBm.data(), &MeasAuxInfo); // Acquire spectrum data.

	SWP_ErrorHandlingExceptOpenAndConfiguration(Status, &Device, DevNum, &BootProfile, &BootInfo, &SWP_ProfileIn, &SWP_ProfileOut, &TraceInfo); // Handle errors except for open/config.

	vector<double> ssfreq(0); // Store frequencies of peaks (unit: Hz).
	vector<float> ssSpec(0);  // Store power values of peaks (unit: dBm).
	vector<float> ssDelt(0);  // Store differences between peak power and threshold (unit: dB).

	SigAndSpur(Frequency, PowerSpec_dBm, -76.0f, ssfreq, ssSpec, ssDelt); // Get peaks with power greater than -76.0 dBm.

	int sigIndex = distance(ssDelt.begin(), max_element(ssDelt.begin(), ssDelt.end())); // Get index of the peak with the maximum delta.

    cout << "index\t" << "frequency\t" << "power\t\t" << "delta limit" << endl; // Print table header: index, frequency, power, delta.

	for (int i = 0; i < ssDelt.size(); i++) // Loop through all peak deltas and print peak info.
	{
		if (i == sigIndex) // If current peak has the maximum delta, highlight it in yellow.
		{
			cout << "\033[1;33m"; // Set text color to yellow.
			cout << i << "\t";    // Print peak index.

			cout.setf(ios::fixed);                                         // Set fixed-point format.
			cout << fixed << setprecision(9) << ssfreq[i] / 1e9 << "GHz\t";
			cout.unsetf(ios::fixed);                                       // Unset fixed-point format.

			cout << fixed << setprecision(2) << ssSpec[i] << "dBm\t";      // Print peak power in dBm with 2 decimal places.

			cout << fixed << setprecision(2) << ssDelt[i] << "dB" << endl; // Print delta between peak and threshold with 2 decimal places.
			cout << "\033[m";  // Reset text color.
		}
		else
		{
			cout << i << "\t"; // For non-maximum peak, print in normal format.

			cout.setf(ios::fixed);                                          // Print peak frequency in GHz with 9 decimal places.
			cout << fixed << setprecision(9) << ssfreq[i] / 1e9 << "GHz\t";
			cout.unsetf(ios::fixed);                                        // Unset fixed-point format.

			cout << fixed << setprecision(2) << ssSpec[i] << "dBm\t";       // Print peak power in dBm with 2 decimal places.

			cout << fixed << setprecision(2) << ssDelt[i] << "dB" << endl;  // Print delta between peak and threshold with 2 decimal places.
		}
	}

	Device_Close(&Device); // Close the device.

	return 0;
}
