%% GFSK Fixed-point implementation

%% Transmission

%%% Transmission/modulation parameters

% fixed-point precision
prec_op_TX = 32;           % precision to be used for the operations performed on the considered signal
prec_cst = 8;           % precision for the constants

f_c = 2.45e09;          % Carrier frequency
r_symb = 1e06;          % Symbol rate
Ts = 1/r_symb;          % Symbol period
m = 0.5;                % Modulation index
BTb = 0.5;              % Bandwidth - Symbol perdiod product


N_symb = 1e04;          % Number of symbols used for the simulation
reso = 8;               % Resolution factor (number of samples by symbol period)
dfppm = 20;


%%% For BER vs Eb/N0 plot and noise variance (N0) generation
Nsnr = 16;              % Number of different values for the plot
Eb_N0_dB = linspace(0,Nsnr-1,Nsnr);
Eb_N0 = 10.^(Eb_N0_dB/10);
Eb = 1;
N0 = Eb./Eb_N0;


%%% Generation of the preamble
preamble = fi([1;-1;1;-1;1;-1;1;-1], 1, prec_cst, prec_cst-1);
Lpreamble = length(preamble);
N_symb = N_symb-Lpreamble;

Lnoisestart = 92;
N_symb = N_symb-Lnoisestart;

%%% Generation of the sequence of symbols (I)
bits = fi(round(rand(N_symb,1)), 1, prec_cst, prec_cst-1,'ProductMode','SpecifyPrecision','ProductFractionLength',16-1,'ProductWordLength',16,'SumMode', 'SpecifyPrecision','SumFractionLength',16-1,'SumWordLength',16);
I0 = bits-0.5;
I1 = I0*2;
I1(1) = fi(1, 1, prec_cst, prec_cst-1,'ProductMode','SpecifyPrecision','ProductFractionLength',16-1,'ProductWordLength',16,'SumMode', 'SpecifyPrecision','SumFractionLength',16-1,'SumWordLength',16);
I1(2) = fi(1, 1, prec_cst, prec_cst-1,'ProductMode','SpecifyPrecision','ProductFractionLength',16-1,'ProductWordLength',16,'SumMode', 'SpecifyPrecision','SumFractionLength',16-1,'SumWordLength',16);
I = fi([preamble;I1], 1, prec_cst, prec_cst-1,'ProductMode','SpecifyPrecision','ProductFractionLength',16-1,'ProductWordLength',16,'SumMode', 'SpecifyPrecision','SumFractionLength',16-1,'SumWordLength',16);
LI = length(I);


%%% Gaussian pulse-shape filter (g) generation
N_Ts = 3;                   % Number of symbol periods for the filter support
time = -N_Ts/2*Ts : Ts/reso : N_Ts/2*Ts;    % Filter support
Tb = Ts;                    % Here, we will always have Tb=Ts
K = sqrt(log(2));           % Parameter for the filter amplitude
g = g_GMSK(time,K,Tb,BTb);  % Filter generation, using my function "g_GMSK"
Lg = length(g);

% g_GMSK can be precomputed and tabled since it is constant, so it does not
% have to be calculated in fixed-point, only the final values are
% converted.
gfi = fi(g,1,16,16-1,'ProductMode','SpecifyPrecision','ProductFractionLength',16-1,'ProductWordLength',16,'SumMode', 'SpecifyPrecision','SumFractionLength',16-1,'SumWordLength',16);


%%% Convolution (convo) between the symbol sequence and the Gaussian filter
tmin = (reso*(LI-1)+Lg+1)/2 - (reso*LI)/2;
tmax = (reso*(LI-1)+Lg+1)/2 + (reso*LI)/2;
Iup = upsample(I, reso);
convfiIupgfi = convfi(Iup,gfi',16,prec_op_TX);
convofi = convfiIupgfi(tmin:tmax);
Lconvo = length(convofi);


%%% Integration (integfi) of the convolution (trapezium method)
% First, we divide everything by 256 as it is the max value the integral
% can output considering a packet of +-300 bits. This way, saturation is
% avoided.
convofi = bitsra(convofi, 8);
integfi = fi(zeros(Lconvo-1,1),1,32,32-1,'ProductMode','SpecifyPrecision','ProductFractionLength',prec_op_TX-1,'ProductWordLength',prec_op_TX,'SumMode', 'SpecifyPrecision','SumFractionLength',prec_op_TX-1,'SumWordLength',prec_op_TX);
integfi(1) = bitsra(convofi(1)+convofi(2), log2(2*reso));  % Division is performed via arithmetic shift right
for i = 2:Lconvo-1
    integfi(i) = integfi(i-1) + bitsra(convofi(i)+convofi(i+1), log2(2*reso));
end
% To avoid saturation resulting from multiplication by pi/2, the signal
% should be divided by 2.
integfi = bitsra(integfi, 1);


%%% Phase (phi) generation
fipi_2 = fi(pi/2, 1, prec_cst, prec_cst-2,'ProductMode','SpecifyPrecision','ProductFractionLength',prec_op_TX-1,'ProductWordLength',prec_op_TX,'SumMode', 'SpecifyPrecision','SumFractionLength',prec_op_TX-1,'SumWordLength',prec_op_TX);
phifi = fipi_2 * integfi;
Lphifi = length(phifi);


%%% Transmitted signal (s) generation
s = exp(1j*double(phifi)*2^9);
Ls = length(s);





%% Reception


% Fixed-point precision
prec_op_RX = 16;
precRX = 16;


% Table of values for the CFO correction:
ak = [-0.894221291841193;...
      -0.823171345055248;...
      -0.624515667660133;...
      -0.335010157734548;...
                       0;...
       0.335010157734548;...
       0.624515667660133;...
       0.823171345055248;...
       0.894221291841193;...
       0.823171345055248;...
       0.624515667660133;...
       0.335010157734548;...
                       0;...
      -0.335010157734548;...
      -0.624515667660133;...
      -0.823171345055248];
sinak = sin(ak);
cosak = cos(ak);
sinakfi = fi(sinak,1,prec_cst,prec_cst-1,'ProductMode','SpecifyPrecision','ProductFractionLength',prec_op_RX-1,'ProductWordLength',prec_op_RX,'SumMode', 'SpecifyPrecision','SumFractionLength',prec_op_RX-1,'SumWordLength',prec_op_RX);
cosakfi = fi(cosak,1,prec_cst,prec_cst-1,'ProductMode','SpecifyPrecision','ProductFractionLength',prec_op_RX-1,'ProductWordLength',prec_op_RX,'SumMode', 'SpecifyPrecision','SumFractionLength',prec_op_RX-1,'SumWordLength',prec_op_RX);
pow_cosak2pifi_m1 = fi(1./(cosak*2*pi),1,prec_cst,prec_cst-1);


BERcorrfi = zeros(Nsnr,1);
BERfi = zeros(Nsnr,1);
df_hatfi = zeros(Nsnr,1);
ind_trig = zeros(Nsnr,1);
SFindi = zeros(Nsnr,1);
% Loop for each different value of the noise variance
% for i = 1:Nsnr
SNR = 13;
for i = SNR+1

    
    %%% AWGN (n) generation
    % noise can remain in floating-point, it doesn't matter as it will be
    % converted as soon as it will be added to the signal.
    N = N0(i)*reso;
    n = awgn(Lnoisestart*reso+Ls, 1, N);
    
    
    %%% Received signal (r) generation
    r = [zeros(Lnoisestart*reso, 1) ; s] + n;
    Lr = length(r);

    
        %%% CFO        
        df = dfppm/1e06 * f_c;
        time = (1/(r_symb*reso) : 1/(r_symb*reso) : (Lr)/(r_symb*reso))';
        rcfo = exp(1j*2*pi*df*time).*r;
        rcfofi0 = fi(rcfo,1,precRX,precRX-4,'ProductMode','SpecifyPrecision','ProductFractionLength',prec_op_RX-1,'ProductWordLength',prec_op_RX,'SumMode', 'SpecifyPrecision','SumFractionLength',prec_op_RX-1,'SumWordLength',prec_op_RX);
        % /!\ abs(rcfof) can go up to 13 (result from 100000 iterations 
        % with 300 bits and SNR=13dB) => a normalization by 16 should be
        % performed.
        rcfofi = fi(bitsra(rcfofi0,4),1,precRX,precRX-1,'ProductMode','SpecifyPrecision','ProductFractionLength',prec_op_RX-1,'ProductWordLength',prec_op_RX,'SumMode', 'SpecifyPrecision','SumFractionLength',prec_op_RX-1,'SumWordLength',prec_op_RX);
        Lrcfo = length(rcfo);
        
       
        %%% Filtering of the received CFO'ed signal
        % This is done in digital, so from now on signals must be in
        % fixed-point. But the filter can be precomputed, so only its final
        % coefficient values have to be converted.
        % Moreover, as the filter coefficients are <= 1, there is no need
        % for normalization. But one is done nonetheless as rcfo needs one.
        fcut = 0.6e06;
        Nper = 12;
        h0 = LPF('Hamming', fcut, reso, Nper);
        h0fi = fi(h0,1,prec_cst,prec_cst-1,'ProductMode','SpecifyPrecision','ProductFractionLength',prec_op_RX-1,'ProductWordLength',prec_op_RX,'SumMode', 'SpecifyPrecision','SumFractionLength',prec_op_RX-1,'SumWordLength',prec_op_RX);
        rconvfih0fi0 = convfi(rcfofi,h0fi',prec_op_RX,prec_op_RX);
        rconvfih0fi = rconvfih0fi0(1+Nper/2*reso:end-Nper/2*reso);
        rficfof = rconvfih0fi;
        
        


        %%% Preamble detection
        % Hermitian product
        OSR = reso;
        Hprodfi0 = rficfof(1+OSR:end).*conj(rficfof(1:end-OSR));
        % IIR filter
        IIRb = 1;
        IIRa = zeros(OSR+1,1);
        IIRa(1) = 1;
        IIRa(OSR+1) = 0.875;
        impu = eye(500,1);
        impuresp = filter(IIRb, IIRa, impu);
        % impuresp can be precomputed and tabled, so only the final values
        % need to be converted into fixed-point.
        impurespfi = fi(impuresp,1,prec_cst,prec_cst-1,'ProductMode','SpecifyPrecision','ProductFractionLength',prec_op_RX-1,'ProductWordLength',prec_op_RX,'SumMode', 'SpecifyPrecision','SumFractionLength',prec_op_RX-1,'SumWordLength',prec_op_RX);
        % /!\ abs(IIRHprod) can go up to 350 (result from 100000 iterations 
        % with 300 bits and SNR=13dB) => a normalization by 512 should be
        % performed, normalization of Hprodfi. But it is already 16*16 = 
        % 256 times smaller due to the previous normalization, so it needs 
        % an additional normalization factor of only 2 to reach 512!
        Hprodfi = fi(bitsra(Hprodfi0,1),1,precRX,precRX-1,'ProductMode','SpecifyPrecision','ProductFractionLength',prec_op_RX-1,'ProductWordLength',prec_op_RX,'SumMode', 'SpecifyPrecision','SumFractionLength',prec_op_RX-1,'SumWordLength',prec_op_RX);
        IIRHprodfi0 = convfi(Hprodfi,impurespfi,precRX,prec_op_RX);
        IIRHprodfi = IIRHprodfi0(1:end-(length(impu)-1));
        absIIRHprodfi = abs(IIRHprodfi);
        LabsIIRHprodfi = length(absIIRHprodfi);

        
        % Search for preamble
        if (SNR==13)
            thresh = 37.5;
        elseif (SNR==14)
            thresh = 32.5;
        end
        % Due to the normalization, the comparison must be done wrt a
        % threshold that has undergone the same normalization.
        threshfi = fi(thresh/2^(4+4+1),1,prec_cst,prec_cst-1,'ProductMode','SpecifyPrecision','ProductFractionLength',prec_cst-1,'ProductWordLength',prec_cst,'SumMode', 'SpecifyPrecision','SumFractionLength',prec_cst-1,'SumWordLength',prec_cst);
        p = 1;
        booly = false;
        while(booly == false)
            if (absIIRHprodfi(p) > threshfi)
                booly = true;
            elseif (p >= LabsIIRHprodfi)
                booly = true;
                fprintf('Error, after 1000 iterations, there is still no value that has reached the threshold!');
            else
                p = p+1;
            end
        end
        ind_trig(i) = p;
        
        
        %%% Symbol timing recovery
        Noffset = 5;
        offset = Noffset*reso;
        maxxtemp = absIIRHprodfi(ind_trig(i)+offset);
        indtemp = ind_trig(i)+offset;
        kk = 1;
        while (kk < 1*reso)
            if (absIIRHprodfi(ind_trig(i)+offset+kk) > maxxtemp)
                maxxtemp = absIIRHprodfi(ind_trig(i)+offset+kk);
                indtemp = ind_trig(i)+offset+kk;
            end
            kk = kk+1;
        end
        SFindi(i) = indtemp;
        
        
        %%% CFO correction
        df_hat_temp1 = 0;
        Nperiod = 6;
        b = 1;
        counter = 1;
        afikind = zeros(Nperiod*reso,1);
        symbrfi = fi(zeros(Nperiod*reso,1),1,precRX,precRX-1,'ProductMode','SpecifyPrecision','ProductFractionLength',prec_op_RX-1,'ProductWordLength',prec_op_RX,'SumMode', 'SpecifyPrecision','SumFractionLength',prec_op_RX-1,'SumWordLength',prec_op_RX);
        symbrfiold = fi(zeros(Nperiod*reso,1),1,precRX,precRX-1,'ProductMode','SpecifyPrecision','ProductFractionLength',prec_op_RX-1,'ProductWordLength',prec_op_RX,'SumMode', 'SpecifyPrecision','SumFractionLength',prec_op_RX-1,'SumWordLength',prec_op_RX);        
        hermifi_unnorm = fi(zeros(Nperiod*reso,1),1,precRX,precRX-1,'ProductMode','SpecifyPrecision','ProductFractionLength',prec_op_RX-1,'ProductWordLength',prec_op_RX,'SumMode', 'SpecifyPrecision','SumFractionLength',prec_op_RX-1,'SumWordLength',prec_op_RX);
        hermifi = fi(zeros(Nperiod*reso,1),1,precRX,precRX-1,'ProductMode','SpecifyPrecision','ProductFractionLength',prec_op_RX-1,'ProductWordLength',prec_op_RX,'SumMode', 'SpecifyPrecision','SumFractionLength',prec_op_RX-1,'SumWordLength',prec_op_RX);
        demodsymbrfirfiold = fi(zeros(Nperiod*reso,1),1,precRX,precRX-1,'ProductMode','SpecifyPrecision','ProductFractionLength',prec_op_RX-1,'ProductWordLength',prec_op_RX,'SumMode', 'SpecifyPrecision','SumFractionLength',prec_op_RX-1,'SumWordLength',prec_op_RX);
        dkfi = fi(zeros(Nperiod*reso,1),1,precRX,precRX-1,'ProductMode','SpecifyPrecision','ProductFractionLength',prec_op_RX-1,'ProductWordLength',prec_op_RX,'SumMode', 'SpecifyPrecision','SumFractionLength',prec_op_RX-1,'SumWordLength',prec_op_RX);
        dfhfi = fi(zeros(Nperiod*reso,1),1,precRX,precRX-1,'ProductMode','SpecifyPrecision','ProductFractionLength',prec_op_RX-1,'ProductWordLength',prec_op_RX,'SumMode', 'SpecifyPrecision','SumFractionLength',prec_op_RX-1,'SumWordLength',prec_op_RX);
        dfh1fi0 = fi(zeros(Nperiod*reso,1),1,precRX,precRX-1,'ProductMode','SpecifyPrecision','ProductFractionLength',prec_op_RX-1,'ProductWordLength',prec_op_RX,'SumMode', 'SpecifyPrecision','SumFractionLength',prec_op_RX-1,'SumWordLength',prec_op_RX);
        dfh1fi = fi(zeros(Nperiod*reso,1),1,precRX,precRX-1,'ProductMode','SpecifyPrecision','ProductFractionLength',prec_op_RX-1,'ProductWordLength',prec_op_RX,'SumMode', 'SpecifyPrecision','SumFractionLength',prec_op_RX-1,'SumWordLength',prec_op_RX);
        while(b <= Nperiod*reso)
            
            symbrfi(b) = rficfof(b+ind_trig(i)+reso-1);
            symbrfiold(b) = rficfof(b+ind_trig(i)+reso-1-reso);
            
            hermifi_unnorm(b) = symbrfi(b)*conj(symbrfiold(b));
            % The division for the normalization is somehow problematic,
            % as I don't know how to handle this in Matlab, I "cheat" by
            % using a double value for the divider.
            hermifi(b) = hermifi_unnorm(b)/double(abs(hermifi_unnorm(b)));
            demodsymbrfirfiold(b) = imag(hermifi(b));

            dkfi0 = demodsymbrfirfiold(b);
            % Must be divided by 2 to avoid saturation when subtracting it
            % with sinakfik
            dkfi(b) = bitsra(dkfi0,1);
            
            % Simulated the index to fetch the values from the LUTs.
            afikind(b) = mod(b,2*reso);
            if (afikind(b) == 0)
                afikind(b) = 2*reso;
            end
            
            sinakfik0 = sinakfi(afikind(b));
            % Must be divided by 2 to avoid saturation when subtracting it
            % from dkfi            
            sinakfik = bitsra(sinakfik0,1);

            dfh1fi0(b) = (dkfi(b)-sinakfik)*pow_cosak2pifi_m1(afikind(b));
            
            % Here, I accumulate Nperiod*reso (48) values, so I have to
            % normalize by this value beforehand (48->64).
            dfh1fi(b) = bitsra(dfh1fi0(b),6);
            df_hat_temp1 = df_hat_temp1+dfh1fi(b);
            dfhfi(b) = df_hat_temp1/counter;
            counter = counter + 1;
                                            
            b = b+1;
            
        end
        

        df_hatfi(i) = dfhfi(end);
        normfact = 2^7/Tb;

        % Correction is done in RF, and the denormalization is performed at
        % the same time using the factor normfact.
        rcorr = exp(-1j*2*pi*(double(df_hatfi(i))*normfact)*time((Lnoisestart+Lpreamble)*reso+1 : end)).*rcfo((Lnoisestart+Lpreamble)*reso+1 : end);
        rcorrfi0 = fi(rcorr, 1, precRX);
        % Looks like convfi(rcorrfi,h0fi) can go up to 10, so it seems
        % appropriate to normalize by 16.
        rcorrfi = fi(bitsra(rcorrfi0, 4),1,precRX,precRX-1,'ProductMode','SpecifyPrecision','ProductFractionLength',prec_op_RX-1,'ProductWordLength',prec_op_RX,'SumMode', 'SpecifyPrecision','SumFractionLength',prec_op_RX-1,'SumWordLength',prec_op_RX);
        rcorrficonvfih0fi0 = convfi(rcorrfi,h0fi',precRX,prec_op_RX);
        rcorrficonvfih0fi = rcorrficonvfih0fi0(1+Nper/2*reso:end-Nper/2*reso);
        rcorrfif = rcorrficonvfih0fi;
               
        
    %%% Filtering of the received signal (for the CFO-free case)
    rfi0 = fi(r, 1, precRX);
    % Looks like convfi(rfi,h0fi) can go up to 12, so it seems appropriate
    % to normalize by 16.
    rfi = fi(bitsra(rfi0, 4),1,precRX,precRX-1,'ProductMode','SpecifyPrecision','ProductFractionLength',prec_op_RX-1,'ProductWordLength',prec_op_RX,'SumMode', 'SpecifyPrecision','SumFractionLength',prec_op_RX-1,'SumWordLength',prec_op_RX);
    rficonvfih0fi0 = convfi(rfi,h0fi',precRX,prec_op_RX);
    rficonvfih0fi = rficonvfih0fi0(1+Nper/2*reso:end-Nper/2*reso);
    rfif = rficonvfih0fi;        
    

    %%% Demodulation
    
    indidown = mod(SFindi(i)+2,reso);
    if (indidown == 0)
        indidown = reso;
    end       
    
    rcorrfifdown = downsample(rcorrfif(indidown:end),reso/2);
    Lrcorrfifdown = length(rcorrfifdown);
    if (mod(Lrcorrfifdown,2)==0)
        demodficorr = imag(rcorrfifdown(2:2:end).*conj(rcorrfifdown(1:2:end-1)));
    else
        demodficorr = imag(rcorrfifdown(2:2:end-1).*conj(rcorrfifdown(1:2:end-2)));
    end    
    Ldemodficorr = length(demodficorr);


    rfifdown = downsample(rfif(indidown:end),reso/2);
    Lrfifdown = length(rfifdown);
    if (mod(Lrfifdown,2)==0)
        demodfi = imag(rfifdown(2:2:end).*conj(rfifdown(1:2:end-1)));
    else
        demodfi = imag(rfifdown(2:2:end-1).*conj(rfifdown(1:2:end-2)));
    end
    demodfi = demodfi(1+Lpreamble+Lnoisestart:end);
    Ldemodfi = length(demodfi);
    

    %%% Decision
    Icorrfi_hat = sign(demodficorr);
    BERcorrfi(i) = sum(sign(I(1+Lpreamble:Lpreamble+Ldemodficorr))~=Icorrfi_hat)/N_symb;
    Ifi_hat = sign(demodfi);
    BERfi(i) = sum(sign(I(1+Lpreamble:Lpreamble+Ldemodfi))~=Ifi_hat)/N_symb;
    

end


% BER vs Eb/N0 plot
figure;
semilogy(Eb_N0_dB,BERcorrfi,'xg','LineWidth',1.5,'MarkerSize',8);
hold on;
semilogy(Eb_N0_dB,BERfi,'xk','LineWidth',1.5,'MarkerSize',8);
axis([0 Nsnr 10/N_symb 1]);
grid;
title( strcat('BER -- df = ', num2str(df)) );
xlabel('E_{b}/N_{0} [dB]');
ylabel('BER');