4G/LTE - PHY Channel

 

 

 

LTE UL (SC-FDMA) Demodulation

 

In this page, I will show you how OFDM demodulation works by using 'semi-real data'. By semi-real, I mean that the data that I used here is not from live eNB but generated by a real hardware (Vector signal generator) and captured by Vector Signal Analyzer with special Sync signal. So you don't have to implement an algorithm for synchronization and a lot of compensation which is a too much for an example. If you are interested in working as beginner level of LTE DSP, this can be helpful or motivation as well.

The uplink needs two steps more than the LTE DL (OFDM) Demodulation page. The subcarriers sit half a subcarrier away from the FFT bins, so the receiver shifts the signal by 7.5 kHz first. The data was also spread by a DFT at the transmitter, so the receiver takes an IFFT of the occupied subcarriers after the FFT.

Followings are the topics to be covered in this page.

Result

Does the SC-FDMA chain give back clean QPSK symbols, and which symbol of the slot behaves differently? The figure below answers both for the seven SC-FDMA symbols of slot 0 in subframe 0. Each row is one symbol, and each column is one stage of the processing.

Following is the result of the Matlab source code listed here. (A) shows the magnitude of the IFFT result for each symbols within the first slot of subframe 0. (B) is the I/Q plot (real and imaginary) representation of column (A). You would notice some degree of rotation (phase shift). (C) is the result after phase compensation of column (B).

IFFT magnitude, raw constellation and phase compensated constellation for the seven SC-FDMA symbols of slot 0 in subframe 0

Rows are SC-FDMA symbols 0 to 6 of slot 0. Column (A) is the magnitude of the 300 recovered values, column (B) the raw constellation, and column (C) the constellation after the fixed phase rotation.

  • Symbols 0 to 2 and 4 to 6 : four tight QPSK clusters, rotated in column (B) and square in column (C).
  • Symbol 3 : a flat line in column (A) and a ring in columns (B) and (C).
  • Column (A) is not a spectrum here : for data symbols it is the magnitude after the IFFT, one point per QPSK symbol.

Symbol 3 is the PUSCH DMRS. With the normal cyclic prefix, 36.211 v19.3.0 clause 5.5.2.1.2 places it in the fourth symbol of each slot. The DMRS is mapped straight onto the subcarriers without transform precoding, so the code skips the IFFT for this symbol. Its sequence for 25 RB is built from a Zadoff-Chu sequence, which has a constant amplitude and a changing phase. So its magnitude is a flat line, and its points spread around a circle.

The data symbols show the opposite picture. Their values after the IFFT are the QPSK symbols themselves, so the magnitude in column (A) stays roughly constant and the constellation shows four clusters. In the LTE DL (OFDM) Demodulation example the same column shows the spectrum, because OFDM data needs no IFFT at the receiver.

One fixed PhaseOffset of exp(-jπ/8), a rotation of -22.5 degrees, squares up every data symbol. So the phase does not drift over the slot. A real receiver would estimate this rotation from the DMRS in symbol 3, rather than by eye.

  • DMRS in symbol 3 : no transform precoding, constant amplitude, a ring in the constellation.
  • Data after the IFFT : the QPSK symbols themselves.
  • One phase value for the whole slot : the DMRS is the reference a real receiver would use for it.

LTE Configuration of the Data

The configuration sets the three numbers the code depends on: the FFT size, the number of subcarriers, and the IFFT size for transform decoding. The signal generator used the following settings.

The data that I used in this example is from following LTE Configuration.

  • System BandWidth = 5 Mhz
  • Number of RB = 25
  • Physical Cell ID = 0
  • Sampling Rate = 7.68 Mhz

A 5 MHz LTE carrier has 25 RB, and the code sets N_RB = 25. The signal therefore occupies all 25 RB, which is 300 subcarriers, and the IFFT in the code has 300 points. 36.211 clause 5.3.3 allows only DFT sizes whose prime factors are 2, 3 and 5, and 300 = 22 · 3 · 52 meets that rule. A sampling rate of 7.68 Msps gives an FFT of 512 points, as in the downlink.

Physical Cell ID = 0 selects the DMRS sequence group, together with the hopping settings. The data symbols do not depend on it for demodulation. The cell identity returns only later, in the descrambling of the bits after the QPSK decision.

  • 25 RB : 300 subcarriers, a 300 point IFFT for transform decoding.
  • 300 = 22 · 3 · 52 : a DFT size that 36.211 allows.
  • 7.68 Msps : a 512 point FFT, the same as the 5 MHz downlink.

Matlab Code

The code follows the SC-FDMA transmitter backwards. It removes the cyclic prefix, undoes the half-subcarrier shift, takes the FFT, picks the 300 occupied bins, and takes the IFFT that undoes the transform precoding.

Following is the Matlab source code that produced the figure shown above. The code is designed intentionally long. I might have make it very short if I used for-loop, but it would look harder to understand. So I try to write every procedure step by step. Hopefully, this would be easier for you to follow even though it looks a little too lengthy.

     

    clear all;

     

    % you can download the data file in this example from here.

    % this is for reading the baseband IQ data of OFDM symbol in time domain.

    % detailed explantion for this three line and file format in this example is described in

    % Matlab : Read Number in Binary file page.

     

    fid = fopen('UL_5_25_NoFilter_S7_68_Trig.bin','r');

    [data,count] = fread(fid, 'single');

    fclose(fid);

     

     

    % This variable represents the first sample in the first SCFDM symbol.

    % How do I know this ? This is just a magic number I could figure out from the vector signal

    % analyzer. Just think this is just a given number. If you seriously want to work in this

    % area, try to implement the algorithm to find this number on your own.

    Offset = 280228;

     

    % Nfft represents the FFT size for the specified system bandwidth. 512 is the FFT size for

    % 5 Mhz System Bandwidth.

    Nfft = 512;

     

    % N_RB is the number of RB assinged to this signal. N_RE is the number of Resource Elements

    % in this signal and can be calculated from N_RB. This N_RE determines the IFFT size in

    % demodulation process (N_RE is the size of IFFT)

    N_RB = 25;

    N_RE = N_RB * 12;

     

    % SamplingScale is to figure out the number of samples in CP and OFDM Symbol for each specific system

    % Bandwidth from the 20 Mhz Bandwidth. Physical Layer Parameters - FDD, Downlink will help you understand

    % the meaning of each numbers here.

    SamplingScale = (double(Nfft)/2048);

    CP_LengthList = SamplingScale * [160;144;144;144;144;144;144];

    Symbol_LengthList = SamplingScale * [2048;2048;2048;2048;2048;2048;2048];

     

    % This is to compensate the phase rotation shown in track (B) of the result.

    % How do I figure out this number. I just got it by eye balling and a couple of try and error.

    % If you seriously want to work in this area, try to implement the algorithm to find this

    % number on your own.

    PhaseOffset = exp(-j*pi/8);

     

    % This is to generate a complex numbered sequence to shift the frequency of the I/Q signal by 7.5 Khz

    % the number of samples of this data is same as the number of samples of each SC-FDM symbol.

    Ts = 1.0/(7.68 * 10^6);

    t = linspace(0,Nfft-1,Nfft);

    t = t .* Ts;

    Fshift = 7.5 * 1000 ;

    FrequencyShift = exp(j*2*pi*Fshift*t);

     

     

    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Demodulation of symbol 0 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

    % This is to demoulate the symbol 0 within the slot 0 of Subframe 0.

    % ChuckStart here indicates the index(position) of the first sample within the binary file.

    % You would notice that I skip the first CP length. This is the same effect of removing the

    % first CP as described in CP removal described in RE Map to/from Antenna page.

    ChunkStart = Offset ...

                + CP_LengthList(1);

     

    % This variable indicates the number of samples within the first OFDM symbol excluding the CP

    ChunkLength = Symbol_LengthList(1);

     

    % This is to read I/Q data pair.

    DataChunk = data((2.* ChunkStart)-1 : (2.*(ChunkStart+ChunkLength)-2));

     

    % This is to separate I and Q into a separate array (vector)

    DataChunkI = DataChunk(1:2:length(DataChunk));

    DataChunkQ = DataChunk(2:2:length(DataChunk));

     

    % This is to combine I array and Q array into a complex number array.

    DataChunkComplex = DataChunkI + j*DataChunkQ;

     

    % This is to convert the time domain OFDM symbol into the frequency domain. Note that the data

    % is shifted in frequency domain

    DataChunkFFT = fft(DataChunkComplex .* FrequencyShift');

     

    % This is to take out the data samples which will be mapped to each resource element

    % within the symbol

    DataChunkFFT_RE = [DataChunkFFT(end-(N_RE/2-1):end)' DataChunkFFT(1:N_RE/2)'];

     

     

    % This is to perform the subcarrier demapping (converting each symboles of resource elements

    % to original binary data

    DataChunkFFT_IFFT = ifft(DataChunkFFT_RE);

    DataChunkFFT_IFFT = DataChunkFFT_IFFT/max(abs(DataChunkFFT_IFFT));

    DataChunkFFT_IFFT = DataChunkFFT_IFFT;

    DataChunkFftMag = abs(DataChunkFFT_IFFT);

     

    % This is to plot the IFFT result in magnitude.

    % The result of this block is shown in track (A) of the figure at the beginning of the page

    subplot(7,5,[1 3]);

    plot(DataChunkFftMag);xlim([1, length(DataChunkFftMag)]); ylim([0 1.2]);

    set(gca,'xticklabel',[]);set(gca,'yticklabel',[]);

    set(gca,'xtick',[]);set(gca,'ytick',[]);

     

    % This is to plot the IFFT result in complex domain (I and Q axis)

    % The result of this block is shown in track (B) of the figure at the beginning of the page

    subplot(7,5,4);

    plot(real(DataChunkFFT_IFFT),imag(DataChunkFFT_IFFT),'ro','MarkerFaceColor',[1 0 0],'MarkerSize',2);

    xlim([-1 1]);ylim([-1 1]);

    set(gca,'xticklabel',[]);set(gca,'yticklabel',[]);

    set(gca,'xtick',[]);set(gca,'ytick',[]);

     

    % This is to plot the FFT result rotated by the angle specified by the variable PhaseOffset

    % The result of this block is shown in track (C) of the figure at the beginning of the page

    subplot(7,5,5);

    DataChunkFFT_Comp = DataChunkFFT_IFFT .* PhaseOffset;

    plot(real(DataChunkFFT_Comp),imag(DataChunkFFT_Comp),'ro','MarkerFaceColor',[1 0 0],'MarkerSize',2);

    xlim([-1 1]);ylim([-1 1]);

    set(gca,'xticklabel',[]);set(gca,'yticklabel',[]);

    set(gca,'xtick',[]);set(gca,'ytick',[]);

     

    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Demodulation of symbol 1 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

    % This is to demodulate the symbol 1 within the slot 0 of Subframe 0. Everything is same

    % as in the symbol 0 analysis except the 'ChunkStart. So I would not explain any further.

     

    ChunkStart = Offset ...

               + CP_LengthList(1) + Symbol_LengthList(1)  ...

               + CP_LengthList(2) ;

    ChunkLength = Symbol_LengthList(2);

     

    DataChunk = data((2.* ChunkStart)-1 : (2.*(ChunkStart+ChunkLength)-2));

    DataChunkI = DataChunk(1:2:length(DataChunk));

    DataChunkQ = DataChunk(2:2:length(DataChunk));

    DataChunkComplex = DataChunkI + j*DataChunkQ;

     

    DataChunkFFT = fft(DataChunkComplex .* FrequencyShift');

     

    DataChunkFFT_RE = [DataChunkFFT(end-(N_RE/2-1):end)' DataChunkFFT(1:N_RE/2)'];

    DataChunkFFT_IFFT = ifft(DataChunkFFT_RE);

    DataChunkFFT_IFFT = DataChunkFFT_IFFT/max(abs(DataChunkFFT_IFFT));

    DataChunkFFT_IFFT = DataChunkFFT_IFFT;

    DataChunkFftMag = abs(DataChunkFFT_IFFT);

     

    subplot(7,5,[6 8]);

    plot(DataChunkFftMag);xlim([1, length(DataChunkFftMag)]); ylim([0 1.2]);

    set(gca,'xticklabel',[]);set(gca,'yticklabel',[]);

    set(gca,'xtick',[]);set(gca,'ytick',[]);

     

    subplot(7,5,9);

    plot(real(DataChunkFFT_IFFT),imag(DataChunkFFT_IFFT),'ro','MarkerFaceColor',[1 0 0],'MarkerSize',2);

    xlim([-1 1]);ylim([-1 1]);

    set(gca,'xticklabel',[]);set(gca,'yticklabel',[]);

    set(gca,'xtick',[]);set(gca,'ytick',[]);

     

    subplot(7,5,10);

    DataChunkFFT_Comp = DataChunkFFT_IFFT .* PhaseOffset;

    plot(real(DataChunkFFT_Comp),imag(DataChunkFFT_Comp),'ro','MarkerFaceColor',[1 0 0],'MarkerSize',2);

    xlim([-1 1]);ylim([-1 1]);

    set(gca,'xticklabel',[]);set(gca,'yticklabel',[]);

    set(gca,'xtick',[]);set(gca,'ytick',[]);

     

     

    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Demodulation of symbol 2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

    % This is to demodulate the symbol 2 within the slot 0 of Subframe 0. Everything is same

    % as in the symbol 0 analysis except the 'ChunkStart. So I would not explain any further.

     

    ChunkStart = Offset  ...

                + CP_LengthList(1) + Symbol_LengthList(1)  ...

                + CP_LengthList(2) + Symbol_LengthList(2)  ...

                + CP_LengthList(3);

    ChunkLength = Symbol_LengthList(3);

     

    DataChunk = data((2.* ChunkStart)-1 : (2.*(ChunkStart+ChunkLength)-2));

    DataChunkI = DataChunk(1:2:length(DataChunk));

    DataChunkQ = DataChunk(2:2:length(DataChunk));

    DataChunkComplex = DataChunkI + j*DataChunkQ;

     

    DataChunkFFT = fft(DataChunkComplex .* FrequencyShift');

     

    DataChunkFFT_RE = [DataChunkFFT(end-(N_RE/2-1):end)' DataChunkFFT(1:N_RE/2)'];

    DataChunkFFT_IFFT = ifft(DataChunkFFT_RE);

    DataChunkFFT_IFFT = DataChunkFFT_IFFT/max(abs(DataChunkFFT_IFFT));

    DataChunkFFT_IFFT = DataChunkFFT_IFFT;

    DataChunkFftMag = abs(DataChunkFFT_IFFT);

     

    subplot(7,5,[11 13]);

    plot(DataChunkFftMag);xlim([1, length(DataChunkFftMag)]); ylim([0 1.2]);

    set(gca,'xticklabel',[]);set(gca,'yticklabel',[]);

    set(gca,'xtick',[]);set(gca,'ytick',[]);

     

    subplot(7,5,14);

    plot(real(DataChunkFFT_IFFT),imag(DataChunkFFT_IFFT),'ro','MarkerFaceColor',[1 0 0],'MarkerSize',2);

    xlim([-1 1]);ylim([-1 1]);

    set(gca,'xticklabel',[]);set(gca,'yticklabel',[]);

    set(gca,'xtick',[]);set(gca,'ytick',[]);

     

    subplot(7,5,15);

    DataChunkFFT_Comp = DataChunkFFT_IFFT .* PhaseOffset;

    plot(real(DataChunkFFT_Comp),imag(DataChunkFFT_Comp),'ro','MarkerFaceColor',[1 0 0],'MarkerSize',2);

    xlim([-1 1]);ylim([-1 1]);

    set(gca,'xticklabel',[]);set(gca,'yticklabel',[]);

    set(gca,'xtick',[]);set(gca,'ytick',[]);

     

     

    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Demodulation of symbol 3 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

    % This is to demodulate the symbol 3 within the slot 0 of Subframe 0. The symbol 3(the fourth

    % symbol in LTE UL slot is special. It is special symbol carrying PUSCH DMRS data, it does

    % not carry any user data. It just carries special reference signal based on Zad-off Chu

    % sequence. This signal does not go through transformation procoding when it is transmitted

    % so this does not go through IFFT process when it is demodulated. But I am still using the

    % variable xxxx_IFFT in this step as I used for other symbols.

     

    ChunkStart = Offset  ...

                + CP_LengthList(1) + Symbol_LengthList(1)  ...

                + CP_LengthList(2) + Symbol_LengthList(2)  ...

                + CP_LengthList(3) + Symbol_LengthList(3)  ...

                + CP_LengthList(4);

    ChunkLength = Symbol_LengthList(4);

     

    DataChunk = data((2.* ChunkStart)-1 : (2.*(ChunkStart+ChunkLength)-2));

    DataChunkI = DataChunk(1:2:length(DataChunk));

    DataChunkQ = DataChunk(2:2:length(DataChunk));

    DataChunkComplex = DataChunkI + j*DataChunkQ;

     

    DataChunkFFT = fft(DataChunkComplex .* FrequencyShift');

     

    DataChunkFFT_RE = [DataChunkFFT(end-(N_RE/2-1):end)' DataChunkFFT(1:N_RE/2)'];

    DataChunkFFT_IFFT = DataChunkFFT_RE;

    DataChunkFFT_IFFT = DataChunkFFT_IFFT/max(abs(DataChunkFFT_IFFT));

    DataChunkFFT_IFFT = DataChunkFFT_IFFT;

    DataChunkFftMag = abs(DataChunkFFT_IFFT);

     

    subplot(7,5,[16 18]);

    plot(DataChunkFftMag);xlim([1, length(DataChunkFftMag)]); ylim([0 1.2]);

    set(gca,'xticklabel',[]);set(gca,'yticklabel',[]);

    set(gca,'xtick',[]);set(gca,'ytick',[]);

     

    subplot(7,5,19);

    plot(real(DataChunkFFT_IFFT),imag(DataChunkFFT_IFFT),'ro','MarkerFaceColor',[1 0 0],'MarkerSize',2);

    xlim([-1 1]);ylim([-1 1]);

    set(gca,'xticklabel',[]);set(gca,'yticklabel',[]);

    set(gca,'xtick',[]);set(gca,'ytick',[]);

     

    subplot(7,5,20);

    DataChunkFFT_Comp = DataChunkFFT_IFFT .* PhaseOffset;

    plot(real(DataChunkFFT_Comp),imag(DataChunkFFT_Comp),'ro','MarkerFaceColor',[1 0 0],'MarkerSize',2);

    xlim([-1 1]);ylim([-1 1]);

    set(gca,'xticklabel',[]);set(gca,'yticklabel',[]);

    set(gca,'xtick',[]);set(gca,'ytick',[]);

     

     

    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Demodulation of symbol 4 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

    % This is to demodulate the symbol 4 within the slot 0 of Subframe 0. Everything is same

    % as in the symbol 0 analysis except the 'ChunkStart. So I would not explain any further.

     

    ChunkStart = Offset  ...

                + CP_LengthList(1) + Symbol_LengthList(1)  ...

                + CP_LengthList(2) + Symbol_LengthList(2)  ...

                + CP_LengthList(3) + Symbol_LengthList(3)  ...

                + CP_LengthList(4) + Symbol_LengthList(4)  ...

                + CP_LengthList(5);

    ChunkLength = Symbol_LengthList(5);

     

    DataChunk = data((2.* ChunkStart)-1 : (2.*(ChunkStart+ChunkLength)-2));

    DataChunkI = DataChunk(1:2:length(DataChunk));

    DataChunkQ = DataChunk(2:2:length(DataChunk));

    DataChunkComplex = DataChunkI + j*DataChunkQ;

     

    DataChunkFFT = fft(DataChunkComplex .* FrequencyShift');

     

    DataChunkFFT_RE = [DataChunkFFT(end-(N_RE/2-1):end)' DataChunkFFT(1:N_RE/2)'];

    DataChunkFFT_IFFT = ifft(DataChunkFFT_RE);

    DataChunkFFT_IFFT = DataChunkFFT_IFFT/max(abs(DataChunkFFT_IFFT));

    DataChunkFFT_IFFT = DataChunkFFT_IFFT;

    DataChunkFftMag = abs(DataChunkFFT_IFFT);

     

    subplot(7,5,[21 23]);

    plot(DataChunkFftMag);xlim([1, length(DataChunkFftMag)]); ylim([0 1.2]);

    set(gca,'xticklabel',[]);set(gca,'yticklabel',[]);

    set(gca,'xtick',[]);set(gca,'ytick',[]);

     

    subplot(7,5,24);

    plot(real(DataChunkFFT_IFFT),imag(DataChunkFFT_IFFT),'ro','MarkerFaceColor',[1 0 0],'MarkerSize',2);

    xlim([-1 1]);ylim([-1 1]);

    set(gca,'xticklabel',[]);set(gca,'yticklabel',[]);

    set(gca,'xtick',[]);set(gca,'ytick',[]);

     

    subplot(7,5,25);

    DataChunkFFT_Comp = DataChunkFFT_IFFT .* PhaseOffset;

    plot(real(DataChunkFFT_Comp),imag(DataChunkFFT_Comp),'ro','MarkerFaceColor',[1 0 0],'MarkerSize',2);

    xlim([-1 1]);ylim([-1 1]);

    set(gca,'xticklabel',[]);set(gca,'yticklabel',[]);

    set(gca,'xtick',[]);set(gca,'ytick',[]);

     

     

    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Demodulation of symbol 5 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

    % This is to demodulate the symbol 5 within the slot 0 of Subframe 0. Everything is same

    % as in the symbol 0 analysis except the 'ChunkStart. So I would not explain any further.

     

    ChunkStart = Offset  ...

                + CP_LengthList(1) + Symbol_LengthList(1)  ...

                + CP_LengthList(2) + Symbol_LengthList(2)  ...

                + CP_LengthList(3) + Symbol_LengthList(3)  ...

                + CP_LengthList(4) + Symbol_LengthList(4)  ...

                + CP_LengthList(5) + Symbol_LengthList(5)  ...

                + CP_LengthList(6);

    ChunkLength = Symbol_LengthList(6);

     

    DataChunk = data((2.* ChunkStart)-1 : (2.*(ChunkStart+ChunkLength)-2));

    DataChunkI = DataChunk(1:2:length(DataChunk));

    DataChunkQ = DataChunk(2:2:length(DataChunk));

    DataChunkComplex = DataChunkI + j*DataChunkQ;

     

    DataChunkFFT = fft(DataChunkComplex .* FrequencyShift');

     

    DataChunkFFT_RE = [DataChunkFFT(end-(N_RE/2-1):end)' DataChunkFFT(1:N_RE/2)'];

    DataChunkFFT_IFFT = ifft(DataChunkFFT_RE);

    DataChunkFFT_IFFT = DataChunkFFT_IFFT/max(abs(DataChunkFFT_IFFT));

    DataChunkFFT_IFFT = DataChunkFFT_IFFT;

    DataChunkFftMag = abs(DataChunkFFT_IFFT);

     

    subplot(7,5,[26 28]);

    plot(DataChunkFftMag);xlim([1, length(DataChunkFftMag)]); ylim([0 1.2]);

    set(gca,'xticklabel',[]);set(gca,'yticklabel',[]);

    set(gca,'xtick',[]);set(gca,'ytick',[]);

     

    subplot(7,5,29);

    plot(real(DataChunkFFT_IFFT),imag(DataChunkFFT_IFFT),'ro','MarkerFaceColor',[1 0 0],'MarkerSize',2);

    xlim([-1 1]);ylim([-1 1]);

    set(gca,'xticklabel',[]);set(gca,'yticklabel',[]);

    set(gca,'xtick',[]);set(gca,'ytick',[]);

     

    subplot(7,5,30);

    DataChunkFFT_Comp = DataChunkFFT_IFFT .* PhaseOffset;

    plot(real(DataChunkFFT_Comp),imag(DataChunkFFT_Comp),'ro','MarkerFaceColor',[1 0 0],'MarkerSize',2);

    xlim([-1 1]);ylim([-1 1]);

    set(gca,'xticklabel',[]);set(gca,'yticklabel',[]);

    set(gca,'xtick',[]);set(gca,'ytick',[]);

     

     

    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Demodulation of symbol 6 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

    % This is to demodulate the symbol 6 within the slot 0 of Subframe 0. Everything is same

    % as in the symbol 0 analysis except the 'ChunkStart. So I would not explain any further.

     

    ChunkStart = Offset  ...

                + CP_LengthList(1) + Symbol_LengthList(1)  ...

                + CP_LengthList(2) + Symbol_LengthList(2)  ...

                + CP_LengthList(3) + Symbol_LengthList(3)  ...

                + CP_LengthList(4) + Symbol_LengthList(4)  ...

                + CP_LengthList(5) + Symbol_LengthList(5)  ...

                + CP_LengthList(6) + Symbol_LengthList(6)  ...

                + CP_LengthList(7);

    ChunkLength = Symbol_LengthList(7);

     

    DataChunk = data((2.* ChunkStart)-1 : (2.*(ChunkStart+ChunkLength)-2));

    DataChunkI = DataChunk(1:2:length(DataChunk));

    DataChunkQ = DataChunk(2:2:length(DataChunk));

    DataChunkComplex = DataChunkI + j*DataChunkQ;

     

    DataChunkFFT = fft(DataChunkComplex .* FrequencyShift');

     

    DataChunkFFT_RE = [DataChunkFFT(end-(N_RE/2-1):end)' DataChunkFFT(1:N_RE/2)'];

    DataChunkFFT_IFFT = ifft(DataChunkFFT_RE);

    DataChunkFFT_IFFT = DataChunkFFT_IFFT/max(abs(DataChunkFFT_IFFT));

    DataChunkFFT_IFFT = DataChunkFFT_IFFT;

    DataChunkFftMag = abs(DataChunkFFT_IFFT);

     

    subplot(7,5,[31 33]);

    plot(DataChunkFftMag);xlim([1, length(DataChunkFftMag)]); ylim([0 1.2]);

    set(gca,'xticklabel',[]);set(gca,'yticklabel',[]);

    set(gca,'xtick',[]);set(gca,'ytick',[]);

     

    subplot(7,5,34);

    plot(real(DataChunkFFT_IFFT),imag(DataChunkFFT_IFFT),'ro','MarkerFaceColor',[1 0 0],'MarkerSize',2);

    xlim([-1 1]);ylim([-1 1]);

    set(gca,'xticklabel',[]);set(gca,'yticklabel',[]);

    set(gca,'xtick',[]);set(gca,'ytick',[]);

     

    subplot(7,5,35);

    DataChunkFFT_Comp = DataChunkFFT_IFFT .* PhaseOffset;

    plot(real(DataChunkFFT_Comp),imag(DataChunkFFT_Comp),'ro','MarkerFaceColor',[1 0 0],'MarkerSize',2);

    xlim([-1 1]);ylim([-1 1]);

    set(gca,'xticklabel',[]);set(gca,'yticklabel',[]);

    set(gca,'xtick',[]);set(gca,'ytick',[]);

     

     

     

The frequency shift comes from the uplink baseband formula. 36.211 clause 5.6 places subcarrier k at (k + 1/2)Δf, so no subcarrier sits on DC and every subcarrier is 7.5 kHz away from an FFT bin. Without a shift, every subcarrier would leak into its neighbours, and the constellation would not form clean clusters.

FrequencyShift is written with a plus sign, but the code applies it as FrequencyShift'. In Matlab, the ' operator is the complex conjugate transpose. So the samples are multiplied by exp(-j2π · 7.5 kHz · t), which moves subcarrier k from (k + 1/2)Δf down to kΔf. The subcarriers then sit on FFT bins -150 to 149.

The FFT output puts the negative frequencies at the end of the vector. So DataChunkFFT_RE takes the last 150 bins and the first 150 bins, which gives the 300 subcarriers in frequency order. The same ' operator conjugates these 300 values, so the IFFT returns the QPSK symbols conjugated and in reversed order. The constellation still shows four QPSK clusters. A receiver that decodes bits from it should use the plain transpose .' in both places.

The same half-subcarrier shift explains why the Cyclic Prefix Correlation page finds a negative Re(r) at each uplink cyclic prefix. Here the shift is removed before the FFT, and the cyclic prefix is skipped, so that sign has no effect on the result.

The data file named in the code, UL_5_25_NoFilter_S7_68_Trig.bin, is not on the site, so the download link at the top of the code does not work. The uplink file that does exist, LTE_UL_5_1_CID_0_1RB_NoFilter.bin, is named as a 1 RB capture and does not match the 25 RB setting of this code.

  • A 7.5 kHz shift first : undoes the (k + 1/2)Δf offset of the uplink.
  • ' conjugates in Matlab : the shift runs downwards, and the recovered symbols come out conjugated.
  • 300 bins from both ends of the FFT : the negative frequencies sit at the end of the vector.
  • IFFT for data, not for the DMRS : only the data went through transform precoding.
  • The download link is broken : the 25 RB file is missing from the site.

Reference

[1] 3GPP TS 36.211 v19.3.0 - clause 5.3.3 for transform precoding, clause 5.5.2.1.2 for the DMRS mapping, and clause 5.6 for SC-FDMA baseband signal generation

[2] LTE DL (OFDM) Demodulation

[3] LTE Timing Sync : Cyclic Prefix Correlation