% Copyright (c) 2023 SpinCore Technologies, Inc.
% 
% Permission is hereby granted, free of charge, to any person obtaining a copy
% of this software and associated documentation files (the 'Software'), to deal
% in the Software without restriction, including without limitation the rights
% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
% copies of the Software, and to permit persons to whom the Software is
% furnished to do so, subject to the following conditions:
% The above copyright notice and this permission notice shall be included in
% all copies or substantial portions of the Software.
%
% THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
% THE SOFTWARE.
%
clc; clear;

global SPINAPI_DLL_NAME

SPINAPI_DLL_NAME = 'spinapi64';

addpath('../MATLAB_SpinAPI'); % MATLAB_SpinAPI Folder path address

SpinAPI_Drive = 'C:'; % Modify to the hard drive letter that contains SpinAPI

% Loads spinapi64 library and headers
% All files are located in SpinAPI folder
if ~libisloaded('spinapi64')  % Needs the MinGW-w64 add-on
    loadlibrary([SpinAPI_Drive,'\SpinCore\SpinAPI\lib\spinapi64.dll'], ...
                 [SpinAPI_Drive,'\SpinCore\SpinAPI\include\spinapi.h'], 'addheader',...
                 [SpinAPI_Drive,'\SpinCore\SpinAPI\include\pulseblaster.h'], 'addheader',...
                 [SpinAPI_Drive,'\SpinCore\SpinAPI\include\radioprocessor.h'], 'addheader',...
                 [SpinAPI_Drive,'\SpinCore\SpinAPI\include\dds.h']);
end

% Load constants to the workspace as variables
load('CONST.mat');

CLOCK_FREQ = 75.0;
MINIMUM_PULSE_LENGTH = 66.6666;

% User friendly names for the control bits
TX_ENABLE = 1;
TX_DISABLE = 0;
PHASE_RESET = 1;
NO_PHASE_RESET = 0;
DO_TRIGGER = 1;
NO_TRIGGER = 0;
USE_SHAPE = 1;
NO_SHAPE = 0;

fprintf('Copyright (c) 2015 SpinCore Technologies, Inc.\n\n');
fprintf(['This program outputs two square pulses with amplitude, delay between pulses,\n'...
	  'and pulse lengths specified by the user.\n\n']);

fprintf('Pulse durations must be specified in the format: \''time units\'', e.g., \''10.0 us\''\n\n');


if pb_init() ~= 0
  error('Error initializing board: %s\n', pb_get_error());
end
	 
while true
    fprintf('\n')

    % first_pulse_length
    while true
        try
            input_s = input('Please enter the first pulse length (minimum 66.6 ns): ', 's');
            parts = strsplit(input_s);
            if length(parts) ~= 2
                error('Invalid pulse length. Please use format: ''time units'', e.g., 10.0 us');
            end 
            pulse0_length = str2double(parts{1});
            pulse0_length_print = pulse0_length;
            units0 = parts{2};
            if ~ismember(units0, {'ns', 'us', 'ms', 's'})
                error('Invalid pulse length. Please use format: ''time units'', e.g., 10.0 us');
            end
            switch units0
                case 'ns'
                    % no conversion necessary
                case 'us'
                    pulse0_length = pulse0_length * 1000.0;
                case 'ms'
                    pulse0_length = pulse0_length * 1000000.0;
                case 's'
                    pulse0_length = pulse0_length * 1000000000.0;
            end
            if pulse0_length < MINIMUM_PULSE_LENGTH
	            error('Pulse length too short: %f. Minimum pulse length is %f.', pulse0_length, MINIMUM_PULSE_LENGTH)
            end 
            break;
       catch e
         fprintf('%s\n', e.message);
       end
    end
      

    % first_pulse_amplitude
    while true
        pulse0_amplitude = input('Please enter the first pulse amplitude scale (-1.0 to 1.0): ');
        if pulse0_amplitude < -1.0 || pulse0_amplitude > 1.0
            fprintf('Pulse amplitude %.2f invalid. Amplitude scale must be between -1.0 and 1.0 inclusive.', pulse0_amplitude); 
        else
            break
        end 
    end 
            
    % first_delay_length
    while true
        try
            input_s = input('Please enter the delay between first and second pulses (minimum 66.6 ns): ', 's');
            parts = strsplit(input_s);
            if length(parts) ~= 2
                error('Invalid delay length. Please use format: ''time units'', e.g., 10.0 us');
            end 
            delay0 = str2double(parts{1});
            delay0_print = delay0;
            unitsd0 = parts{2};
            if ~ismember(unitsd0, {'ns', 'us', 'ms', 's'})
                error('Invalid delay length. Please use format: ''time units'', e.g., 10.0 us');
            end
            switch unitsd0
                case 'ns'
                    % no conversion necessary
                case 'us'
                    delay0 = delay0 * 1000.0;
                case 'ms'
                    delay0 = delay0 * 1000000.0;
                case 's'
                    delay0 = delay0 * 1000000000.0;
            end
            if delay0 < MINIMUM_PULSE_LENGTH
	            error('Delay length too short: %f. Minimum delay length is %lf.', delay0, MINIMUM_PULSE_LENGTH)
            end 
            break;
       catch e
         fprintf('%s\n', e.message);
       end
    end
                
    fprintf('\n') 
    
    % Second pulse
    % second_pulse_length
    while true
        try
            input_s = input('Please enter the second pulse length (minimum 66.6 ns): ', 's');
            parts = strsplit(input_s);
            if length(parts) ~= 2
                error('Invalid pulse length. Please use format: ''time units'', e.g., 10.0 us');
            end 
            pulse1_length = str2double(parts{1});
            pulse1_length_print = pulse1_length;
            units1 = parts{2};
            if ~ismember(units1, {'ns', 'us', 'ms', 's'})
                error('Invalid pulse length. Please use format: ''time units'', e.g., 10.0 us');
            end
            switch units1
                case 'ns'
                    % no conversion necessary
                case 'us'
                    pulse1_length = pulse1_length * 1000.0;
                case 'ms'
                    pulse1_length = pulse1_length * 1000000.0;
                case 's'
                    pulse1_length = pulse1_length * 1000000000.0;
            end
            if pulse1_length < MINIMUM_PULSE_LENGTH
	            error('Pulse length too short: %f. Minimum pulse length is %f.', pulse1_length, MINIMUM_PULSE_LENGTH)
            end 
            break;
       catch e
         fprintf('%s\n', e.message);
       end
    end
    
    % second_pulse_amplitude
    while true
        pulse1_amplitude = input('Please enter the second pulse amplitude scale (-1.0 to 1.0): ');
        if pulse1_amplitude < -1.0 || pulse1_amplitude > 1.0
            fprintf('Pulse amplitude %.2f invalid. Amplitude scale must be between -1.0 and 1.0 inclusive.', pulse1_amplitude); 
        else
            break
        end 
    end 
    
    % second_delay_length
    while true
        try
            input_s = input('Please enter the delay between second and first pulses (minimum 66.6 ns): ', 's');
            parts = strsplit(input_s);
            if length(parts) ~= 2
                error('Invalid delay length. Please use format: ''time units'', e.g., 10.0 us');
            end 
            delay1 = str2double(parts{1});
            delay1_print = delay1;
            unitsd1 = parts{2};
            if ~ismember(unitsd1, {'ns', 'us', 'ms', 's'})
                error('Invalid delay length. Please use format: ''time units'', e.g., 10.0 us');
            end
            switch unitsd1
                case 'ns'
                    % no conversion necessary
                case 'us'
                    delay1 = delay1 * 1000.0;
                case 'ms'
                    delay1 = delay1 * 1000000.0;
                case 's'
                    delay1 = delay1 * 1000000000.0;
            end
            if delay1 < MINIMUM_PULSE_LENGTH
	            error('Delay length too short: %f. Minimum delay length is %lf.', delay1, MINIMUM_PULSE_LENGTH)
            end 
            break;
       catch e
         fprintf('%s\n', e.message);
       end
    end
    
    fprintf('\n') 
    	
    fprintf('Pulse 0 - Amplitude: %0.2f, Length: %0.2f %s\n', pulse0_amplitude, pulse0_length_print, units0);
    fprintf('Delay 0 - Length: %0.2f %s\n',  delay0_print, unitsd0);
    fprintf('Pulse 1 - Amplitude: %0.2f, Length: %0.2f %s\n' , pulse1_amplitude, pulse1_length_print, units1) ;
    fprintf('Delay 1 - Length: %0.2f %s\n' ,  delay1_print, unitsd1);
    
    pb_select_board(0);
    pb_core_clock(CLOCK_FREQ); 
    pb_set_defaults(); 
    
    pulse0_phase = 270.0; 
    if pulse0_amplitude < 0.0 
        pulse0_amplitude = pulse0_amplitude*-1.0;
        pulse0_phase = 90.0;    	 
    end
    pulse1_phase = 270.0; 
    if pulse1_amplitude < 0.0  
        pulse1_amplitude = pulse1_amplitude*-1.0; 
        pulse1_phase = 90.0;
    end
    	 
    
    pb_set_amp(pulse0_amplitude, 0);
    pb_set_amp(pulse1_amplitude, 1); 
    
    % Zero frequency is DC
    pb_start_programming(FREQ_REGS); 
    pb_set_freq(0.0* MHz); 
    pb_stop_programming(); 
    
    pb_start_programming(TX_PHASE_REGS); 
    pb_set_phase(pulse0_phase); 
    pb_set_phase(pulse1_phase); 
    pb_stop_programming(); 
    
    pb_start_programming(PULSE_PROGRAM); 
    	
    % 90-deg pulse
    start = pb_inst_dds_shape(0, 0, TX_ENABLE, NO_PHASE_RESET, NO_SHAPE, 0, '1FF', CONTINUE, 0, pulse0_length); 
    pb_inst_dds_shape(0, 1, TX_DISABLE, PHASE_RESET, NO_SHAPE, 1, 0, CONTINUE, 0, delay0); 
    
    % 180-deg pulse
    pb_inst_dds_shape(0, 1, TX_ENABLE, NO_PHASE_RESET, NO_SHAPE, 1, '1FF', 'CONTINUE', 0, pulse1_length); 
    pb_inst_dds_shape(0, 0, TX_DISABLE, PHASE_RESET, NO_SHAPE, 0, 0, 'BRANCH', start, delay1); 
    pb_stop_programming(); 
     
    pb_reset(); 
    pb_start(); 
    
    fprintf('Program has been triggered.\n'); 
    
    break
end 

% Release control of the board
pb_close(); 

fprintf('Press enter to continue...\n');
pause; 

pb_stop(); 

% Unload library and free memory
unloadlibrary('spinapi64');