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

filePath = fullfile(pwd, 'htra_api_mat');  

% Check whether the folder exists. If it does not exist, 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'))   
   % Pay attention to the file paths of 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;

% Open the device.
Device_1 = libpointer; % Create a Device pointer.
DevNum_1 = 1;
Status_1 = 0;

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

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

IS_USB = 1;  % USB device by default. Set this value to 0 if a network-port device is used.

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

BootProfile_1 = BootProfile;
BootProfile_2 = BootProfile;

BootInfo_1 = BootInfo;
BootInfo_2 = BootInfo;

% Call the Device_Open function.
Status = calllib('htra_api', 'Device_Open', Device, DevNum, BootProfile_1, BootInfo_1);

if Status == 0
    get(BootProfile); % Print the value of BootProfile.
    get(BootInfo);    % Print the value of BootInfo.
    fprintf('Device successfully\n');
else
    % Device opening failed. Handle different error status values.
    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 the error status value.
    return;
end

% Call the Device_Open function.
Status_1 = calllib('htra_api', 'Device_Open', Device_1, DevNum_1, BootProfile_2, BootInfo_2);

if Status_1 == 0
    fprintf('Device successfully\n');
    get(BootProfile_2); % Print the value of BootProfile_2.
    get(BootInfo_2);    % Print the value of BootInfo_2.
else
    % Device opening failed. Handle different error status values.
    switch Status_1
        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 the error status value.
    return;
end

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

% Define the IQS_ProfileIn pointer and IQS_ProfileOut_0 pointer.
IQS_ProfileIn = IQS_Profile;
IQS_ProfileOut_0 = IQS_Profile;

% IQS_ProfileOut_1 pointer.
IQS_ProfileOut_1 = IQS_Profile;

% Call the IQS_ProfileDeInit function.
Status = calllib('htra_api', 'IQS_ProfileDeInit', Device, IQS_ProfileIn);
get(IQS_ProfileIn);  % Print the value of IQS_ProfileIn.

% Call the IQS_ProfileDeInit function.
Status_1 = calllib('htra_api', 'IQS_ProfileDeInit', Device_1, IQS_ProfileIn);
get(IQS_ProfileIn); % Print the value of IQS_ProfileIn.

% Modify some parameters of the IQS_ProfileIn structure.
IQS_ProfileIn.CenterFreq_Hz = 1e9;              % Configure the center frequency.
IQS_ProfileIn.RefLevel_dBm = 0;                 % Configure the reference level.
IQS_ProfileIn.DecimateFactor = 2;               % Configure the decimation factor.
IQS_ProfileIn.TriggerSource = 1;                % Configure the trigger source.
IQS_ProfileIn.TriggerLength = 16242;            % Configure the number of points collected in a single trigger.
% IQS_ProfileIn.ReferenceClockSource = 1;       % Configure the reference clock source.
% IQS_ProfileIn.ReferenceClockFrequency = 10e9; % Configure the reference clock frequency.

% Directly load the IQS_StreamInfo_TypeDef structure.
load(fullfile(filePath, 'IQS_StreamInfo.mat')); 
IQS_StreamInfo_0 = IQS_StreamInfo;
IQS_StreamInfo_1 = IQS_StreamInfo;

% Call the IQS_Configuration function.
Status = calllib('htra_api', 'IQS_Configuration', Device, IQS_ProfileIn, IQS_ProfileOut_0, IQS_StreamInfo_0);

% If the configuration is successfully applied (Status == 0)
if Status == 0
    fprintf('Configuration successfully\n');
    get(IQS_ProfileOut_0); % Print the value of IQS_ProfileOut_0.
    get(IQS_StreamInfo_0); % Print the value of StreamInfo.
else
    % If the call fails, print the error message and close the device.
    fprintf('Configuration successfully\n');
    Device_Close(Device);
    
    % Return 0 and terminate the function execution.
    return;
end

% Call the IQS_Configuration function.
Status_1 = calllib('htra_api', 'IQS_Configuration', Device_1, IQS_ProfileIn, IQS_ProfileOut_1, IQS_StreamInfo_1);

% If the configuration is successfully applied (Status_1 == 0)
if Status_1 == 0
    fprintf('Configuration delivery succeeded\n');
    get(IQS_ProfileOut_1); % Print the value of IQS_ProfileOut_1.
    get(IQS_StreamInfo_1); % Print the value of StreamInfo.
else
    % If the call fails, print the error message and close the device.
    fprintf('IQS_Configuration Call error Status_1 = %d\n', Status_1);
    Device_Close(Device);  
    
    % Return 0 and terminate the function execution.
    return;
end

% Directly load the IQS_TriggerInfo_TypeDef structure.
load(fullfile(filePath, 'TriggerInfo.mat'));   
TriggerInfo_0 = TriggerInfo;
TriggerInfo_1 = TriggerInfo;

% Directly load the MeasAuxInfo_TypeDef structure.
load(fullfile(filePath, 'MeasAuxInfo.mat'));  
MeasAuxInfo_0 = MeasAuxInfo;
MeasAuxInfo_1 = MeasAuxInfo;

IQ_data = int16(1:IQS_StreamInfo_0.PacketSamples * 2);

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

% Coefficient for converting integer values to absolute voltage values in volts.
ScaleToV = single(0);                                           
ScaleToV_p = libpointer('singlePtr', ScaleToV);

IQ_data_1 = int16(1:IQS_StreamInfo_0.PacketSamples * 2);

% Interleaved IQ time-domain data. A single channel may be in i8, i16, or i32 format.
AlternIQStream_1 = libpointer('int16Ptr', IQ_data_1);

% Coefficient for converting integer values to absolute voltage values in volts.
ScaleToV_1 = single(0);                                           
ScaleToV_p_1 = libpointer('singlePtr', ScaleToV_1);

% Select one device in the system to wait for the trigger signal.
% When this function finishes, it means the system has captured the trigger signal.
Status = calllib('htra_api', 'IQS_MultiDevice_WaitExternalSync', Device, IQS_ProfileOut_0);

% After the system captures the trigger signal, enable multi-device synchronous operation.
Status = calllib('htra_api', 'IQS_MultiDevice_Run', Device);
Status_1 = calllib('htra_api', 'IQS_MultiDevice_Run', Device_1);

DET_data1 = 1:IQS_StreamInfo_0.StreamSamples;
DET_data2 = 1:IQS_StreamInfo_1.StreamSamples;

% Acquire IQ data n times.
n = 10000;

for j = 1:n
    for i = 1:IQS_StreamInfo_0.PacketCount
        Status = calllib('htra_api', 'IQS_GetIQStream', Device, AlternIQStream, ScaleToV_p, TriggerInfo_0, MeasAuxInfo_0);
        Status_1 = calllib('htra_api', 'IQS_GetIQStream', Device_1, AlternIQStream_1, ScaleToV_p_1, TriggerInfo_1, MeasAuxInfo_1);
        if Status ~= 0
            % If the call fails, print the error message and close the device.
            fprintf('IQS_GetIQStream Call error Status = %d\n', Status);
            return;
        elseif Status_1 ~= 0
            % If the call fails, print the error message and close the device.
            fprintf('IQS_GetIQStream Call error Status_1 = %d\n', Status);

            % Return 0 and terminate the function execution.
            return;
        end
    end
    
    for t = 1:IQS_StreamInfo_1.StreamSamples    
        DET_data1(t) = power(AlternIQStream.value(2 * t - 1), 2) + power(AlternIQStream.value(2 * t), 2);  
        DET_data2(t) = power(AlternIQStream_1.value(2 * t - 1), 2) + power(AlternIQStream_1.value(2 * t), 2);
        DET_data1(t) = 10 * log10(DET_data1(t));
        DET_data2(t) = 10 * log10(DET_data2(t));
    end

    plot(1:IQS_StreamInfo_1.StreamSamples, DET_data1, 1:IQS_StreamInfo_1.StreamSamples, DET_data2);              
    axis([0, IQS_StreamInfo_1.StreamSamples - 1, 0, 50]);
    pause(0.001);
end

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

% Call the Device_Close function.
Status_1 = calllib('htra_api', 'Device_Close', Device_1);

clear all;

% Unload the library file.
unloadlibrary('htra_api');   
disp('Uninstall complete')