clc;

setenv('MW_MINGW64_LOC','C:\msys64\mingw64');
mex -setup C++
%% Locate DLL / header / MEX files.
thisDir = fileparts(mfilename('fullpath'));
if isempty(thisDir)
    thisDir = pwd;
end

apiDir = fullfile(thisDir, 'h2_api');
headerPath = fullfile(apiDir, 'h2_api_matlab.h');
dllPath    = fullfile(apiDir, 'h2_api.dll');
genDllPath = fullfile(apiDir, 'genSignalWave.dll');

if exist(headerPath, 'file') ~= 2
    error('Cannot find header file: %s', headerPath);
end
if exist(dllPath, 'file') ~= 2
    error('Cannot find h2_api.dll in %s or %s', fullfile(thisDir,'h2_api'), thisDir);
end
if exist(genDllPath, 'file') ~= 2
    error('Cannot find genSignalWave.dll in %s', apiDir);
end

addpath(thisDir);
addpath(apiDir);
setenv('PATH', [apiDir ';' getenv('PATH')]);

%% Load h2_api.dll for non-modulation TX functions.
LIB = 'h2_api_single';
if ~libisloaded(LIB)
    loadlibrary(dllPath, headerPath, ...
        'alias', LIB, ...
        'includepath', apiDir);
end
%% Compile h2_mod_mex if needed.
mexName = ['h2_mod_mex.' mexext];
mexPath = fullfile(apiDir, mexName);
if exist(mexPath, 'file') ~= 3
    altMexPath = fullfile(thisDir, mexName);
    if exist(altMexPath, 'file') == 3
        mexPath = altMexPath;
    else
        cppPath = fullfile(apiDir, 'h2_mod_mex.cpp');
        if exist(cppPath, 'file') ~= 2
            cppPath = fullfile(thisDir, 'h2_mod_mex.cpp');
        end
        if exist(cppPath, 'file') ~= 2
            error('Cannot find MEX source file h2_mod_mex.cpp in %s or %s', apiDir, thisDir);
        end

        fprintf('Compiling h2_mod_mex from: %s\n', cppPath);
        try
            mex('-v', '-largeArrayDims', cppPath, '-outdir', apiDir);
        catch ME
            error(['Failed to compile h2_mod_mex. Please run "mex -setup C++" first, ', ...
                   'then run this script again. Original error:\n%s'], ME.message);
        end
    end
end

%% Open device
Device = libpointer('voidPtrPtr');
DevNum = uint8(0);
chn = libpointer('uint16Ptr', uint16(0));

% Allocate channel buffer.
CHANNEL_COUNT = 8;
CHANNEL_STRUCT_BYTES = 16;  % sizeof(channel) on 64-bit build: void** + uint16 + uint8 + alignment
ch_buf = zeros(1, CHANNEL_COUNT * CHANNEL_STRUCT_BYTES, 'uint8');
ch = libpointer('uint8Ptr', ch_buf);

% Create device_info.
deviceinfo.model = uint16(0);
deviceinfo.uid_l64 = uint64(0);
deviceinfo.uid_h32 = uint32(0);
deviceinfo.hardware_version = uint16(0);
deviceinfo.mfw_version = uint32(0);
deviceinfo.ffw_version = uint32(0);
deviceinfo.pmu_version = uint32(0);
deviceinfo.agu_version = uint32(0);
deviceinfo.bus_version = uint32(0);
deviceinfo.eio_version = uint32(0);
deviceinfo.bus_bandwidth = uint32(0);
deviceinfo_p = libstruct('device_info', deviceinfo);

USE_ETH = false;

if USE_ETH
    ETH_STANDARD = uint8(1);
    ethsettings.eth_interface = ETH_STANDARD;
    ethsettings.eth_is_ipv6 = uint8(0);
    ethsettings.eth_ip_address = zeros(1, 16, 'uint8');
    ethsettings.eth_ip_address(1:4) = uint8([192 168 1 100]);
    ethsettings.eth_remote_port = uint16(5000);
    ethsettings.eth_read_timeout = int32(5000);

    ethsettings_p = libstruct('eth_setting', ethsettings);
    Status = calllib(LIB, 'device_open_eth', Device, ethsettings_p, ch, chn, deviceinfo_p);
    fprintf('device_open_eth: Status = %d\n', Status);
 else
    Status = calllib(LIB, 'device_open_usb', Device, DevNum, ch, chn, deviceinfo_p);
    fprintf('device_open_usb: Status = %d\n', Status);
end

if Status ~= 0
    error('Device open failed. Status = %d', Status);
end

fprintf('channel count = %d\n', chn.Value);
get(deviceinfo_p);

%% Open Modulation
modType = 'ofdm';
ofdmProfile_in = struct();
ofdmProfile_in.idleinterval = 0.001;
ofdmProfile_in.fftsize = 256;
ofdmProfile_in.symbolcount = 100;
ofdmProfile_in.guardbandcarriers_left = 10;
ofdmProfile_in.guardbandcarriers_right = 10;
ofdmProfile_in.guardinterval = 25;
ofdmProfile_in.window_length = 10;
ofdmProfile_in.windowed = 1;
ofdmProfile_in.digitalmod_type = 3;
ofdmProfile_in.nulldc = 1;
ofdmProfile_in.samplerate = 20e6;

symbolLength = int32(0);

[iqData, sampleRateHz, modInfo, ofdmProfile_out] = h2_mod_mex( ...
    'generate', modType, ofdmProfile_in, symbolLength, double(DevNum), apiDir);

fprintf('MEX modulation generation succeeded.\n');
disp('Configured ofdm output profile:');
disp(ofdmProfile_out);

if isempty(iqData) || mod(numel(iqData), 2) ~= 0
    error('Invalid IQ data returned by h2_mod_mex: numel(iqData) = %d', numel(iqData));
end

%% Generate am waveform
pause(0.2);
N  = numel(iqData);
Fs = double(sampleRateHz);

% Keep original minimum length behavior, but avoid using a variable named "length".
minIqInt16Length = 65536 * 2;
if N < minIqInt16Length
    repeatCount = ceil(minIqInt16Length / N);
    iqData = repmat(iqData(:), repeatCount, 1);
    N = numel(iqData);
    fprintf('IQ was repeated to %d int16 samples, %.0f IQ points.\n', N, N/2);
else
    iqData = iqData(:);
end
%% Configure RF.
fc = double(1.0e9);      % Hz
level = single(0.0);     % dBm
status = calllib(LIB, 'tx_config_ffm', ch, fc, level);
if status ~= 0
    fprintf('tx_config_ffm status = %d\n', status);
else
    fprintf('tx_config_ffm success\n');
end
    
%% Configure trigger.
TRIGGER_SOURCE_BUS   = uint8(0);
TRIGGER_EDGE_RISING  = uint8(0);
TRIGGER_ACTION_SWEEP = uint8(1);

txtrigger.source = TRIGGER_SOURCE_BUS;
txtrigger.edge   = TRIGGER_EDGE_RISING;
txtrigger.action = TRIGGER_ACTION_SWEEP;
txtrigger.count  = int32(-1);
trg = libstruct('tx_trigger', txtrigger);

status = calllib(LIB, 'channel_config_trigger', ch, trg);
fprintf('channel_config_trigger status = %d\n', status);
    
%% Download waveform.
waveid = libpointer('uint16Ptr', uint16(0));
status = calllib(LIB, 'tx_clear_waveform', Device);
fprintf('tx_clear_waveform status = %d\n', status);

final_waveform = int16(iqData(:));
wavePtr = libpointer('int16Ptr', final_waveform);
iqPoints = uint32(numel(final_waveform) / 2);

status = calllib(LIB, 'tx_download_waveform', Device, wavePtr, iqPoints, waveid);
fprintf('tx_download_waveform status = %d\n', status);
fprintf('Wave ID = %d\n', waveid.Value);
%% Configure playback.
waveforms = int16(1);
waveform = int16(waveid.Value);
repeat = int32(-1);
srate = double(Fs);

status = calllib(LIB, 'tx_config_playback', ch, waveform, repeat, srate, waveforms);
if status ~= 0
    fprintf('tx_config_playback status = %d\n', status);
else
    fprintf('tx_config_playback success\n');
end
%% Configure output.
STATE_ON = uint8(1);
status = calllib(LIB, 'tx_config_output', ch, STATE_ON, STATE_ON);
fprintf('tx_config_output: Status = %d\n', status);

%% Start channel.
Status = calllib(LIB, 'channel_start', ch);
fprintf('channel_start: Status = %d\n', Status);

%% Send bus trigger.
Status = calllib(LIB, 'channel_trigger_bus', ch);
fprintf('channel_trigger_bus: Status = %d\n', Status);
% Run for 10 seconds.
pause(10);
    
%% Close device.
Status = calllib(LIB, 'device_close', Device);
deviceOpened = false;
fprintf('device_close: Status = %d\n', Status);
disp('Demo complete.');
