% Configure the compilation environment
% setenv('MW_MINGW64_LOC','C:\msys64\ucrt64');
%  mex -setup C++

filePath = fullfile(pwd, 'htra_api_mat');  

% Check if the directory exists, if not, create it
if ~exist(filePath, 'dir')

    run('htra_api.m');
    filePath = fullfile(pwd, 'htra_api_mat');  
end

% Load htra_api.dll
if not(libisloaded('htra_api.dll'))   
   % Ensure the correct path to the .dll and .h files
    loadlibrary('.\htra_api\htra_api.dll','.\htra_api\htra_api.h'); 
end

% View all functions in the API
libfunctions('htra_api'); 

% Open the device
Device = libpointer; % Create a Device pointer
DevNum = 0;
Status = 0;

% Directly load BootProfile_TypeDef structure
load(fullfile(filePath, 'BootProfile.mat'));  
% Directly load BootInfo_TypeDef structure
load(fullfile(filePath, 'BootInfo.mat'));  

IS_USB = 1;  % Default to USB device, set to 0 for Ethernet-based device

if IS_USB == 1
    % Configure USB interface
    BootProfile.PhysicalInterface = 0;
else
    % Configure Ethernet interface
    BootProfile.PhysicalInterface = 3;
    BootProfile.ETH_IPVersion = 0;
    BootProfile.ETH_RemotePort = 5000;
    BootProfile.ETH_ReadTimeOut = 5000;
    BootProfile.ETH_IPAddress = [192, 168, 1, 100];
end

% Call the Device_Open function
Status = calllib('htra_api', 'Device_Open', Device, DevNum, BootProfile, BootInfo);

if Status == 0
    get(BootProfile); % Print BootInfo values
    get(BootInfo); % Print BootInfo values    
    fprintf('Device successfully');
else
    % Device opening failed, handle errors based on different status codes
    switch Status
        case -1
            fprintf('Error - Check the power supply of the device, the connection of the data cable, and check that the driver is installed correctly\n');
        case -3
            fprintf('Error - RF calibration file is missing, please copy the RF calibration file to the CalFile folder\n');
        case -4
            fprintf('Error - IF calibration file is missing, please copy the IF calibration file to the CalFile folder\n');
        case -5
            fprintf('Error - Profile is missing, please copy the profile to the CalFile folder\n');
        case -6
            fprintf('Error - The device spec file is missing, please copy the device spec file to the CalFile folder\n');
        otherwise
            fprintf('Device turn on failed: Returns another error Status = %d', Status);
    end

    % Return error status value
    return ;
end

% Directly load IQS_Profile_TypeDef structure
load(fullfile(filePath, 'IQS_Profile.mat')); 

% Define IQS_ProfileIn and IQS_ProfileOut pointers
IQS_ProfileIn = IQS_Profile;
IQS_ProfileOut = IQS_Profile;

% Call IQS_ProfileDeInit function
Status = calllib('htra_api', 'IQS_ProfileDeInit', Device, IQS_ProfileIn);
get(IQS_ProfileIn); % Print IQS_ProfileIn values

% Modify parameters of the IQS_ProfileIn structure
IQS_ProfileIn.CenterFreq_Hz = 1e9+100000;    % Configure center frequency
IQS_ProfileIn.RefLevel_dBm = 0;       % Configure reference level
IQS_ProfileIn.DecimateFactor = 2;     % Configure decimation factor
IQS_ProfileIn.TriggerMode= 0;         % Configure trigger mode (0 for FixedPoints mode, 1 for Adaptive mode)
IQS_ProfileIn.TriggerLength = 16243; % Configure the number of points per trigger, effective only in FixedPoints mode

% Directly load IQS_StreamInfo_TypeDef structure
load(fullfile(filePath, 'IQS_StreamInfo.mat'));  

% Call IQS_Configuration function
Status = calllib('htra_api', 'IQS_Configuration', Device, IQS_ProfileIn, IQS_ProfileOut,IQS_StreamInfo);

% If the configuration is successfully applied (Status == 0)
if Status == 0
    get(IQS_ProfileOut); % Print IQS_ProfileOut values
    get(IQS_StreamInfo); % Print StreamInfo values
    fprintf('Configuration successfully\n');
else
    % If an error occurs, print error message and close the device
    fprintf('IQS_Configuration Call error Status = %d\n', Status);
    
    % Call the function to close the device (Device_Close)
    Device_Close(Device); 
    
    % Return 0 to end function execution
    return;
end

% Directly load IQS_TriggerInfo_TypeDef structure 
load(fullfile(filePath, 'TriggerInfo.mat'));  
% Directly load MeasAuxInfo_TypeDef structure 
load(fullfile(filePath, 'MeasAuxInfo.mat')); 

IQ_data = zeros(1, IQS_StreamInfo.StreamSamples * 2, 'int16');

 % Interleaved IQ time-domain data, single path may be i8, i16, or i32 format
 AlternIQStream = libpointer('int16Ptr',IQ_data);

% Conversion factor from integer type to absolute voltage (V)
ScaleToV = single(0);                                           
ScaleToV_p = libpointer('singlePtr',ScaleToV);

% Create I and Q arrays to store I and Q data separately
I_data = zeros(1, IQS_StreamInfo.StreamSamples, 'single');
Q_data = zeros(1, IQS_StreamInfo.StreamSamples, 'single');
IQdata = zeros(1, IQS_StreamInfo.StreamSamples*2, 'single');

I_plot = IQS_StreamInfo.StreamSamples;
Q_plot = IQS_StreamInfo.StreamSamples;

% Acquire IQ data for m iterations
m = 10000;

% Determine when to call IQS_BusTriggerStart based on TriggerMode value and allocate array space accordingly
if strcmp(IQS_ProfileOut.TriggerMode, 'Adaptive')
    % If TriggerMode == Adaptive, execute IQS_BusTriggerStart once at the beginning
    Status = calllib('htra_api', 'IQS_BusTriggerStart', Device);
    
    IQ_data = zeros(1, IQS_StreamInfo.PacketSamples * 2, 'int16');
    
    % Interleaved IQ time-domain data, single path may be i8, i16, or i32 format
    AlternIQStream = libpointer('int16Ptr',IQ_data);
    
    I_data = zeros(1, IQS_StreamInfo.PacketSamples, 'single');
    Q_data = zeros(1, IQS_StreamInfo.PacketSamples, 'single');
   
    I_plot = IQS_StreamInfo.PacketSamples;
    Q_plot = IQS_StreamInfo.PacketSamples;
end

for k = 1:m
   
    % If TriggerMode == FixedPoints, execute IQS_BusTriggerStart in each loop iteration
    if strcmp(IQS_ProfileOut.TriggerMode, 'FixedPoints')
        % Acquire and display IQ data in FixedPoints mode

        Status = calllib('htra_api', 'IQS_BusTriggerStart', Device);
    
        for j = 1:IQS_StreamInfo.PacketCount
            
            Status = calllib('htra_api', 'IQS_GetIQStream', Device, AlternIQStream, ScaleToV_p, TriggerInfo, MeasAuxInfo);
    
            IQ.Value = AlternIQStream.value;
    
            if Status == 0 
                % Process a full packet by default
                Points = IQS_StreamInfo.PacketSamples;
        
                if j < IQS_StreamInfo.PacketCount
                    % Process complete packets
                    for i = 1:Points
                        IQdata((j-1) * IQS_StreamInfo.PacketSamples + i) = single(AlternIQStream.Value(i))* ScaleToV_p.Value;

%                         I_data((j-1) * IQS_StreamInfo.PacketSamples + i) = single(AlternIQStream.Value((i-1) * 2 + 1))* ScaleToV_p.Value;
%                         Q_data((j-1) * IQS_StreamInfo.PacketSamples + i) = single(AlternIQStream.Value((i-1) * 2 + 2))* ScaleToV_p.Value;
                    end
                else
                    % Process the last packet if it is not full
                    RemainingPoints = mod(IQS_StreamInfo.StreamSamples, IQS_StreamInfo.PacketSamples);
                    if RemainingPoints > 0
                        Points = RemainingPoints;
                    end
                    for i = 1:Points
                        IQdata((j-1) * IQS_StreamInfo.PacketSamples + i) = single(AlternIQStream.Value(i))* ScaleToV_p.Value;
                        %Q_data((j-1) * IQS_StreamInfo.PacketSamples + i) = single(AlternIQStream.Value((i-1) * 2 + 2))* ScaleToV_p.Value;
                    end
                end
                for i = 1:IQS_StreamInfo.StreamSamples
                    I_data(i)=IQdata(i*2);
                    Q_data(i-1)=IQdata(i*2-1);
                end
            else
                % If an error occurs, print error message and close the device
                fprintf('IQS_GetIQStream Call error Status = %d\n', Status);
    
                % Return 0 to end function execution
                return;
            end
        end

    else 
        % Acquire and display IQ data in Adaptive mode
        strcmp(IQS_ProfileOut.TriggerMode, 'Adaptive');

        Status = calllib('htra_api', 'IQS_GetIQStream', Device, AlternIQStream, ScaleToV_p, TriggerInfo, MeasAuxInfo);

        if Status == 0
            IQ.Value = AlternIQStream.value;

            Points = IQS_StreamInfo.PacketSamples;
    
            for i = 1:Points
                I_data(i) = single(AlternIQStream.Value((i-1) * 2 + 1))* ScaleToV_p.Value;
                Q_data(i)  = single(AlternIQStream.Value((i-1) * 2 + 2))* ScaleToV_p.Value;
            end
        else
            % If an error occurs, print error message and close the device
            fprintf('IQS_GetIQStream Call error Status = %d\n', Status);

            % Return 0 to end function execution
            return;
        end
    end

    % Plot I and Q waveforms
    plot(1:I_plot, I_data, 1: Q_plot, Q_data);
    yMin = min(double(AlternIQStream.Value) * ScaleToV_p.Value);
    [max_values, max_indices] = max(double(AlternIQStream.Value) * ScaleToV_p.Value);
    disp(max_values)
    disp(max_indices)
    yMax = max(double(AlternIQStream.Value) * ScaleToV_p.Value);
    axis([0, I_plot - 1, yMin - 0.001, yMax + 0.001]);
    pause(0.01);

end

% Call IQS_BusTriggerStop function
Status = calllib('htra_api', 'IQS_BusTriggerStop', Device);

% Call Device_Close function
Status = calllib('htra_api', 'Device_Close', Device);

clear all;
% Unload library files
unloadlibrary('htra_api');   
disp('Uninstall complete')
