%% 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');