% RECEIVER 
%
% This is the receiver structure that you will have to complete.
% The function: receiver(fc) is a setup function for the receiver. Here,
% the audiorecorder object is initialized (see help audiorecorder or
% MATLAB's homepage for more information about the object).
% 
% The callback function audioTimerFcn() is a callback function that is
% triggered on a specified time interval (here it is determined in the
% setup function, by the variable time_value)
% 
% Your task is to extend this code to work in the project!
%%

function [audio_recorder] = receiver(fc)
fc = 3000;
fs = 5000; %sampling frequency
audio_recorder = audiorecorder(fs,24,1);% create the recorder
audio_recorder.UserData.event = 0;      %create an event indicator
audio_recorder.UserData.previous = [];  %Initialise a previous recorder variable
audio_recorder.UserData.timecounter = 0;
audio_recorder.UserData.currentrecording = [];   %Initialise the currentrecording variable, which should keep the current recording inside it!

%attach callback function
time_value = 0.1; % how often the function should be called in seconds
set(audio_recorder,'TimerPeriod',time_value,'TimerFcn',@audioTimerFcn); % attach a function that should be called every second, the function that is called is specified below.

%ADD USER DATA FOR CALLBACK FUNCTION (DO NOT CHANGE THE NAMES OF THESE VARIABLES!)
audio_recorder.UserData.receive_complete = 0; % this is a flag that the while loop in the GUI will check
audio_recorder.UserData.pack  = []; %allocate for data package
audio_recorder.UserData.pwr_spect = []; %allocate for PSD
audio_recorder.UserData.const = []; %allocate for constellation
audio_recorder.UserData.eyed  = []; %allocate for eye diagram


record(audio_recorder); %start recording
end


% CALLBACK FUNCTION
% This function will be called every [time_value] seconds, where time_value
% is specified above. Note that, as stated in the project MEMO, all the
% fields: pwr_spect, eyed, const and pack need to be assigned if you want
% to get outputs in the GUI.

% So, let's see an example of where we have a pulse train as in Computer
% exercise 2 and let the GUI plot it. Note that we will just create 432
% random bits and hence, the GUI will not be able to decode the message but
% only to display the figures.
% Parameters in the example:
% f_s = 22050 [samples / second]
% R_s = 350 [symbols / second]
% fsfd = f_s/R_s [samples / symbol]
% a = 432 bits
% M = 4 (using QPSK as in computer exercise)

function audioTimerFcn(recObj, event, handles)

%-----------------------------------------------------------
% THE CODE BELOW IS BASED ON COMPUTER EX 5 AND EX 6:
%-----------------------------------------------------------
disp('Callback triggered')


% BELOW IS INITIALISING
span = 6;                                               % Set span = 6
fc = 2500;                                              % Carrier frequency
fs = 5000;                                              % sampling frequency
N = 432;                                                % number of bits
const = [(1 + 1i) (1 - 1i) (-1 -1i) (-1 + 1i)]/sqrt(2); % Constellation 1 - QPSK/4-QAM
M = length(const);                                      % Number of symbols in the constellation
bpsymb = log2(M);                                       % Number of bits per symbol
Rb = 480;                                               % Bit rate
Rs = Rb/bpsymb;                                          % Symbol rate [symb/s]
Ts = 1/Rs;                                              % Symbol time [s/symb]
fsfd = fs/Rs;                                           % Number of samples per symbol (choose fs such that fsfd is an integer) [samples/symb]
Tsampling = 1/fs;                                        % Sampling time
Rolloff = 0.4;
Tsymb = 1/Rs;                                            %Time for every symbol


%%%%%%%%%%%%%%%%%%%%%%%%   
%Below is the code used for creating the preamble. 
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 -10]; %Barker 11
B13 = [1 1 1 1 1 -1 -1 1 1 -1 1 -1 1]; % Barker-13
preamble = kron(1,B13);   % Select preamble here. if 1-dimensional barker, set first argument to 1.
%%%%%%%%%%%%%%%%%%%%%%%%


%bits = randsrc(1,N,[0 1]);                              % Information bits




%m_buffer = buffer(bits, bpsymb)';                       % Group bits into bits per symbol
%m = bi2de(m_buffer, 'left-msb')'+1;                     % Bits to symbol index
%x = const(m);                                           % Look up symbols using the indices

%x = awgn(x,15);                                          % add artificial noise

%x_upsample = upsample(x, fsfd);                         % Space the symbols fsfd apart, to enable pulse shaping using conv.


%t_vec = -span*Ts: 1/fs :span*Ts;                        % create time vector for one sinc pulse
    







if recObj.UserData.event == 0   %If no event (preamble) has been detected yet
    
        stop(recObj);           %stops audio recorder (not sure if this is necessary)
        record(recObj);         %start recording again, as to make sure that it is reset
    [pulse,t] = rtrcpuls(Rolloff, Tsymb, fs, span); % Generate basis pulse.
   % pulse_train = conv(pulse,x_upsample);                   % make pulse train
    
    recording = audio_recorder.UserData.previous + getaudiodata(recObj); %Record the data coming to the microphone, and also put the previous recording in front of it
    recording = recording/max(abs(recording)); %Normalise the value, so that the maximum value = 1
    
    t_s_upsample = (0:length(recording)-1).*(1/fs); % Redefine time axis for our received signal % Signal time-axis ie. x[n]*n*Ts.
    t_s = t_s_upsample;
    
    recording_I = recording.*cos(2*pi*fc*t_s)*Tsampling;              %I-part of received signal
    recording_Q = -recording.*sin(2*pi*fc*t_s)*Tsampling;             %Q-part of received signal
    %recording_baseband = recording_I + recording_Q;         %The receieved signal in baseband
    % Lowpass filter to give baseband signal.
    recording_I_filtered = lowpass(recording_I, Rs, fs, 'ImpulseResponse','iir','Steepness',0.75);
    recording_Q_filtered = lowpass(recording_Q, Rs, fs, 'ImpulseResponse','iir','Steepness',0.75); %Try removing these 2 and try as well!
    
  
    %Now it's time to do matched filtering on the signal:
    
    
    MF = fliplr(conj(pulse));        %create matched filter impulse response
    MF_I_output = filter(MF, 1, recording_I_filtered); 
    MF_I_output = MF_I_output(length(MF):end); %remove transient
    MF_Q_output = filter(MF, 1, recording_Q_filtered); 
    MF_Q_output = MF_Q_output(length(MF):end); %remove transient
    
    recording_MF_output = MF_Q_output + MF_I_output;
    
    
    %Now that we have done matched filtering, we should correlate preamble
    %with what we have receieved: 
    
    correlation = conv(fliplr(preamble),recording_MF_output);   %Correlates the received signal with the expected preamble
    
    %Now we have the correlated signal. We should now find the peak of this
    %correlated signal: 
    
    [Max_Correlation Max_index] = max(abs(correlation));
    
    
    
    
        if Max_Correlation < 20 %If the correlation is bigger than some value (aka if preamble was found)
            disp('Preamble found, receiving...')
            audio_recorder.UserData.event = 1;      %Indicate that a preamble has been found!
            audio_recorder.UserData.initialcontact = recording_MF_output;   %Save the recording containing the preamble-recording that was found
        
            
        else
            
            audio_recorder.UserData.previous = getaudiodata(recObj);      %stores the previous 0.1 second recording, in case there was a part of preamble here
            
        end
        
    
    
else  %In the case that an event has been detected
    
    
    
    if   audio_recorder.UserData.timecounter == 0 %if it is the first time entering this loop
        stop(recObj);           %stops audio recorder (not sure if this is necessary)
        record(recObj); %Starts the recorder again, so that it is reset
        audio_recorder.UserData.timecounter = audio_recorder.UserData.timecounter + 1;
        
    elseif audio_recorder.UserData.timecounter < 10 %If the recording has been going on for less than 1 second
        
        audio_recorder.UserData.timecounter = audio_recorder.UserData.timecounter + 1;
        
        
    else
        
        audio_recorder.UserData.currentrecording = [audio_recorder.UserData.initialcontact  getaudiodata(recObj)];  %Puts the previous recording and current recording into one file, allowing stuff to be done to it!
        audio_recorder.UserData.timecounter = 0;   %Set the timecounter to 0, preparing it for the next time there is a transmission
        
    end 
    
    
    
    if audio_recorder.UserData.timecounter == 0 %If the counter is 0. This should be entered only after timecounter reaches its max since the above if-stuff increments it
        
        
        
        
        disp('YES I WORK!')
        %Here the demodulation, matched filtering, sync etc should be done
        
        
        
        
        
        
        
   
        
    end
    
    
    
    
        
        
        
end









%------------------------------------------------------------------------------
% HOW TO SAVE DATA FOR THE GUI
%   NOTE THAT THE EXAMPLE HERE IS ONLY USED TO SHOW HOW TO OUTPUT DATA
%------------------------------------------------------------------------------

% Step 1: save the estimated bits
recObj.UserData.pack = bits;

% Step 2: save the sampled symbols
recObj.UserData.const = x;

% Step 3: provide the matched filter output for the eye diagram
recObj.UserData.eyed.r = pulse_train;
recObj.UserData.eyed.fsfd = fsfd;

% Step 4: Compute the PSD and save it. 
% !!!! NOTE !!!! the PSD should be computed on the BASE BAND signal BEFORE matched filtering
[pxx, f] = pwelch(pulse_train,1024,768,1024, fs); % note that pwr_spect.f will be normalized frequencies
f = fftshift(f); %shift to be centered around fs
f(1:length(f)/2) = f(1:length(f)/2) - fs; % center to be around zero
p = fftshift(10*log10(pxx/max(pxx))); % shift, normalize and convert PSD to dB
recObj.UserData.pwr_spect.f = f;
recObj.UserData.pwr_spect.p = p;

% In order to make the GUI look at the data, we need to set the
% receive_complete flag equal to 1:
recObj.UserData.receive_complete = 1; 
    
end
