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

#define IS_USB 1 // The default device is USB type. If using Ethernet type, define IS_USB as 0.

//This example requires API version 0.55.54 or above, and MCU version 0.55.43 or above.

int IQS_SetFreqScan()
{
    int Status = 0;      // The function return status.
    void* Device = NULL; // The memory address of the current device.
    int DevNum = 0;      // Specify the device number.

    BootProfile_TypeDef BootProfile; // Boot configuration structure, including physical interface, power supply, etc.
    BootInfo_TypeDef BootInfo;       // Boot information structure, including device information, USB speed, etc.

    BootProfile.DevicePowerSupply = USBPortAndPowerPort; // Dual power supply using USB data port and independent power port.

#if IS_USB==1
    // Configure USB interface.
    BootProfile.PhysicalInterface = USB;
#else 
    // Configure ETH 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); // Handle errors based on the Status if it is not 0.

    // Configure the start frequency, stop frequency, and number of frequency points for IQ (effective only in FixedPoints mode).
    double StartFreq = 400e6; // Set the start frequency for IQ.
    double StopFreq = 8e9;    // Set the stop frequency for IQ.
    uint16_t SweepPts = 380; // Set the number of frequency points, with a 20M step.

    Status = Device_SetFreqScan(&Device, StartFreq, StopFreq, SweepPts); // Apply the relevant configuration.

    IQS_Profile_TypeDef IQS_ProfileIn;  // IQS input configuration, including start frequency, stop frequency, RBW, reference level, etc. 
    IQS_Profile_TypeDef IQS_ProfileOut; // IQS output configuration.                 
    IQS_StreamInfo_TypeDef StreamInfo;  // IQ data information under the current configuration, including bandwidth, IQ single-channel sample rate, etc.                

    Status = IQS_ProfileDeInit(&Device, &IQS_ProfileIn); // Initialize IQS mode-related parameters.

    IQS_ProfileIn.CenterFreq_Hz = 400e6;             // Set the center frequency.
    IQS_ProfileIn.RefLevel_dBm = -40;                // Set the reference level.
    IQS_ProfileIn.NativeIQSampleRate_SPS = 122.88e6; // Set the device's native sample rate. Range: 110MSPS-130MSPS.
    IQS_ProfileIn.DecimateFactor = 4;			     // Set the decimation factor.
    IQS_ProfileIn.TriggerMode = FixedPoints;         // Set the trigger mode. FixedPoints mode starts sampling at the rising edge of the trigger signal and ends after collecting TriggerLength points. Adaptive mode starts sampling at the rising edge of the trigger signal and ends at the falling edge.
    IQS_ProfileIn.TriggerSource = FreeRun;           // Set the trigger source to free run.
    IQS_ProfileIn.DataFormat = Complex16bit;         // Set the IQ data format.

    int sample_rate = IQS_ProfileIn.NativeIQSampleRate_SPS / IQS_ProfileIn.DecimateFactor; // Actual sample rate, the number of points collected per second.
    IQS_ProfileIn.TriggerLength = sample_rate * 25e-3;                                     // Set the number of points collected per trigger, each frequency point collects 25ms of data. This only takes effect when TriggerMode is set to FixedPoints.

    Status = IQS_Configuration(&Device, &IQS_ProfileIn, &IQS_ProfileOut, &StreamInfo); // Apply the IQS mode-related configuration.

    IQS_Configuration_ErrorHandling(Status, &Device, DevNum, &BootProfile, &BootInfo, &IQS_ProfileIn, &IQS_ProfileOut, &StreamInfo); // Handle errors based on the Status if it is not 0.

    DeviceState_TypeDef DeviceState;                                  // Device status, including device temperature, RF status, etc.

    Status = Device_QueryDeviceState_Realtime(&Device, &DeviceState); // Get the real-time device status, including device temperature, RF status, etc.

    IQStream_TypeDef IQStream; // Stores the IQ data packet, including IQ data and configuration information.

    vector<std::vector<int16_t>> IQ_Data_all(SweepPts, std::vector<int16_t>(StreamInfo.StreamSamples * 2));  // Create a 2D array to store all IQ data for each sampling.

    // Loop to collect IQ data for multiple frequency points.
    while (1)
    {
        for (uint16_t sweep = 0; sweep < SweepPts; ++sweep)
        {
            for (int j = 0; j < StreamInfo.PacketCount; j++)
            {
                Status = IQS_GetIQStream_PM1(&Device, &IQStream); // Get IQ data packets, trigger information, maximum I data, and the maximum value index in the array.

                Device_QueryDeviceState(&Device, &DeviceState); // Get non-real-time device status, which provides the RF center frequency used for the current data packet.
                int64_t RFCFreq = DeviceState.RFCFreq;

                if (Status == APIRETVAL_NoError)
                {
                    // UserCode here
                    // Note: When using IQ mode, it is recommended to open a separate thread for calling IQS_GetIQStream to get IQ data. It should not be in the same thread as processing the IQ data.

                    int16_t* IQstream = (int16_t*)IQStream.AlternIQStream;
                    int32_t Points = StreamInfo.PacketSamples;

                    if (j == StreamInfo.PacketCount - 1 && StreamInfo.StreamSamples % StreamInfo.PacketSamples != 0) // The last packet may not be a full packet (16242 points). Therefore, only loop through the number of points in the last incomplete packet.
                    {
                        Points = StreamInfo.StreamSamples % StreamInfo.PacketSamples;
                    }

                    for (uint32_t i = 0; i < Points; i++) // Interleave and store all IQ data for each frequency point.
                    {
                        IQ_Data_all[sweep][i + StreamInfo.PacketSamples * j] = IQstream[i * 2];         // Store I data.
                        IQ_Data_all[sweep][i + 1 + StreamInfo.PacketSamples * j] = IQstream[i * 2 + 1]; // Store Q data.

                    }
                }

                else // Handle errors based on the Status if it is not 0.
                {
                    IQS_ErrorHandlingExceptOpenAndConfiguration(Status, &Device, DevNum, &BootProfile, &BootInfo, &IQS_ProfileIn, &IQS_ProfileOut, &StreamInfo);
                }
            }
        }
    }

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

    return 0;
}
