%receiver_callback_func
function [recObj] = receiver_callback_func

close all

rx_fs = 48e3;  % Sampling frequency [Sample/s]
recObj = audiorecorder(rx_fs,24,1);% create the recorder

tx_fs = 12e3;
Rb 			= 600%480*2;          % bit rate [bit/sec]    
Ts 			= 1/tx_fs;  % Sample time [s/Sample]
span    	= 6;   % Decides truncation for used pulse
rolloff 	= 0.4; % Roll-off factor (ie. alpha) for Raised Cosine pulse.
constellation = generateConstellation('4-QAM');
M 				= length(constellation); % Number of symbols in the constellation
bpsymb 		= log2(M);  % Number of bits per symbol                    
Rsymb  		= Rb/bpsymb;% Symbol Rate
Tsymb 		= 1/Rsymb;   % symbol time
sps    		= tx_fs/Rsymb; % Samples per symbol. [Sample/s / Symbol/s = Sample/Symbol]
OS          = 4

recObj.UserData.Rb 			   = Rb 			
recObj.UserData.Ts 			   = Ts 			
recObj.UserData.span    	   = span    	
recObj.UserData.rolloff 	   = rolloff 	
recObj.UserData.constellation  = constellation
recObj.UserData.M 			   = M 			
recObj.UserData.bpsymb 		   = bpsymb 		
recObj.UserData.Rsymb  		   = Rsymb  		
recObj.UserData.Tsymb 		   = Tsymb 		
recObj.UserData.sps    		   = sps   
recObj.UserData.OS    		   = OS 





%% Preambles
B2 =  [1 -1];       % Barker-2
B3 =  [1 1 -1];     % Barker-3
B4 =  [1 1 -1 1];   % Barker-4
B5 =  [1 1 1 -1 1]; % Barker-5%
B7 =  [1 1 1 -1 -1 1 -1];               % Barker-7
B11 = [1 1 1 -1 -1 -1 1 -1 -1 1 -1];   % Barker 11
B13 = [1 1 1 1 1 -1 -1 1 1 -1 1 -1 1];  % Barker-13

preamble = kron(B7, B7); % Choose preamble type.
preamble_symbs = preamble; % OPTIONAL. Map preamble to symbols.


%% Preamble-pulse modulation
[pulse_OS ~]  = rtrcpuls(rolloff, Tsymb, rx_fs, span); % Generate basis pulse.
preamble_upsample = upsample(preamble_symbs, OS*sps);
preamble_pulse_mod = conv(pulse_OS, preamble_upsample);
recObj.UserData.preamble_pulse_mod = preamble_pulse_mod
% Length of transient before and after pulse modulated preamble, due to convolution.
transient_length = (length(preamble_pulse_mod)-length(preamble_upsample))/2;
preamble_pulse_mod = preamble_pulse_mod(transient_length+1:end-(transient_length+sps*OS)); 

% Save upsampled, pulse modulated, truncated preamble to struct.
recObj.UserData.preamble_pulse_mod = preamble_pulse_mod;
% Save preamble bits to struct.
recObj.UserData.preamble = preamble;


figure(199)
plot(recObj.UserData.preamble_pulse_mod)


%ADD USER DATA FOR CALLBACK FUNCTION
recObj.UserData.counter = 1; %initialize a counter in the structure UserData
recObj.UserData.tx_fs = tx_fs;
recObj.UserData.rx_fs = rx_fs;
recObj.UserData.fc = 2000;




%attach callback function
time_value = 1; %the timer function will be called every 1 seconds
set(recObj,'TimerPeriod',time_value,'TimerFcn',@audioTimerFcn); %use cells to specify function and its arguments

record(recObj); %start recording

end

% CALLBACK FUNCTION
%
%   This function will be called at first once every second (since we set
%   time_value = 1), the counter will increase by one every time the
%   callback function is called. Then, as the counter reaches 10, the trigger
%   time will be set to 0.1 seconds. Now, the function will be called with
%   0.1 s time intervals until the counter reaches 20. When the counter
%   equals 20, the audiorecorder will be stopped.
%
% NOTE: we will only use recObj, i.e., the object that calls this function!
function audioTimerFcn(recObj, event, handles)
    disp('Callback Triggered')
    disp(recObj.UserData.counter)

    tx_fs = recObj.UserData.tx_fs; %12e3
    rx_fs = recObj.UserData.rx_fs; %48e3
        
    fc = recObj.UserData.fc;

	% Read from sctruct into local vars.
	Rb 			   = recObj.UserData.Rb 			 ;
	Ts 			   = recObj.UserData.Ts 			 ;
	span    	   = recObj.UserData.span    	     ;
	rolloff 	   = recObj.UserData.rolloff 	     ;
    constellation  = recObj.UserData.constellation   ;
	M 			   = recObj.UserData.M 			     ;
	bpsymb 		   = recObj.UserData.bpsymb 		 ;
	Rsymb  		   = recObj.UserData.Rsymb  		 ;
	Tsymb 		   = recObj.UserData.Tsymb 		     ;
    sps    		   = recObj.UserData.sps    		 ;
    OS = recObj.UserData.OS;
    preamble = recObj.UserData.preamble;

    rec_data = getaudiodata(recObj);
    figure(1)
    hold off
    plot(rec_data)

    %stop(recObj)
    %resume(recObj);
    
    if recObj.UserData.counter == 20
        stop(recObj);
    end
    recObj.UserData.counter = recObj.UserData.counter + 1;


    % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

    rx_sig_waveform = rec_data';

    t_s_OS = (0:length(rx_sig_waveform)-1).*(1/rx_fs); % Redefine time axis for our received signal % Signal time-axis ie. x[n]*n*Ts.
    rx_I =  rx_sig_waveform.*cos(2*pi*fc*t_s_OS);
    rx_Q = -rx_sig_waveform.*sin(2*pi*fc*t_s_OS);

    %% Recombine I/Q streams into Complex
    rx_sig = rx_I + 1i*rx_Q;

    %% Match-filter
    [pulse_OS ~]  = rtrcpuls(rolloff, Tsymb, rx_fs, span); % Generate basis pulse.

    MF = fliplr(conj(pulse_OS));        %create matched filter impulse response
    MF_out = filter(MF, 1, rx_sig);
    MF_out = MF_out(length(MF):end);   %remove transient

    
    MF_out_normalized = MF_out./( max(abs(MF_out))); % Normalize, so that peak correlation detection is more robust.
    
    figure(2)
    plot(abs(MF_out_normalized))
    title('Matched Filter out')


    
    
    %  Correlate
    preamble_pulse_mod = recObj.UserData.preamble_pulse_mod;
    corr = conv(MF_out_normalized, fliplr(preamble_pulse_mod));
    [peak_val ,peak_idx] = max(corr); % Find index of correlation peak.

    figure(3)
    plot(abs(corr))

    if abs(peak_val) > 200
        disp('Preamble detected. Trig.')
        pause(0.6)
        stop(recObj);
        
        figure
        plot(getaudiodata(recObj));
        
        rx_sig = getaudiodata(recObj)';
    t_s_upsample = (0:length(rx_sig)-1).*(1/rx_fs); % Redefine time axis for our received signal % Signal time-axis ie. x[n]*n*Ts.
    t_s = t_s_upsample;
    rx_I =  rx_sig.*cos(2*pi*fc*t_s);
    rx_Q = -rx_sig.*sin(2*pi*fc*t_s);

    rx_I_downsampled= downsample(rx_I, OS);
    rx_Q_downsampled = downsample(rx_Q, OS);

    
    % Matched filter
    [pulse ~] = rtrcpuls(rolloff, Tsymb, tx_fs, span); % Generate basis pulse.
    MF = fliplr(conj(pulse));        %create matched filter impulse response
    MF_I_output = filter(MF, 1, rx_I_downsampled); 
    MF_I_output = MF_I_output(length(MF):end); %remove transient
    MF_Q_output = filter(MF, 1, rx_Q_downsampled); 
    MF_Q_output = MF_Q_output(length(MF):end); %remove transient

    avg = zeros(sps, 1);

    MF_output_complex = MF_I_output + i.*MF_Q_output;

    for delta = 1:sps
        shifted_MF_output = circshift(MF_output_complex, delta-1);
        shifted_samples = shifted_MF_output(1:sps:end);
        avg(delta) = sum(abs(shifted_samples))./length(shifted_samples);
    end

    [~, max_idx] = max(avg);
    sync_offset = max_idx-1;

    MF_I_output_shifted = circshift(MF_I_output, sync_offset);
    MF_Q_output_shifted = circshift(MF_Q_output, sync_offset);
    rx_symbs = [MF_I_output_shifted(1:sps:end)', MF_Q_output_shifted(1:sps:end)'];

    % Unmapping to symbols
    samples = rx_symbs(:,1) + 1i.*rx_symbs(:,2); 

    % Preamble correlation
    sample_corr = conv(samples, fliplr(preamble));
    [~,peak_idx] = max(sample_corr);

    phzError = angle(sample_corr(peak_idx))%-pi/4 % Offset by nominal phase of preamble.
    samples_phzCorrected = samples.*exp(1i*-phzError); 

    %% Normalize received sample energy
    samples_phzCorrected = samples_phzCorrected.* (1./abs(samples(peak_idx)));
        
    % Plot phase-corrected scatterplot.
    figure
    hold on
    plot(samples_phzCorrected, 'ob'); title('Phase-corrected rx. Decision regions overlaid.')
    if length(constellation)>2
        voronoi(real(constellation), imag(constellation))
    end
    plot(real(constellation), imag(constellation), 'or')
    grid on; axis square;
    xlim([-2 2]);
    ylim([-2 2]);
    hold off

    % Minimum euclidean distance. Unmap symbols to messages
    d = abs(repmat(samples_phzCorrected, 1, length(constellation)) - repmat(constellation, length(samples), 1)).^2; % Compute distance of every sample symbol to every constellation point.
    [~, nearest_idx] = min(d, [], 2);  % Get index of value with minimum distance to a constellation point. Traverse along column.

    rx_mapped = constellation(nearest_idx); % Choose corresponding symbol.

    % Symbols to messages
    m_hat = rx_mapped(peak_idx+1:end); % Get symbols after preamble -> actual message.

    % Messages to bits
    b_hat_buffer = de2bi(nearest_idx(peak_idx+1:end)-1, bpsymb, 'left-msb')';
    b_hat = b_hat_buffer(:)'; %write as a vector
   % b_hat = b_hat(1:432);

    b_hat_trunc = b_hat(1:end-rem(length(b_hat), 8)); % Truncate received bitstream so that it's divisible by 7 (ASCII length).
    disp(['Received message (quote marks added): "' bitStream2string(b_hat_trunc) '"'])

        
    end


end
    
    
