Matlab Toolbox - 4G/LTE

 

 

 

Detecting/Decoding PDSCH

 

Main purpose of this page is to show how to decode PDSCH data that is contained in resource Grid of one subframe. The important steps are as follows :

    i) Create a physical layer symbols for all the channels (RS, PSS, SSS, PDCCH, PDSCH)

    ii) Put all the channel data into a resource grid

    iii) Take out all the resource element for PDSCH from the resource grid

    iv) Decode the data (Convert the PDSCH to codeword bit stream)

Main purpose of this page is step iv), but most of the portions in the following source code is used for step i)~iii)

The receiver side is the transmit chain run backwards. The transmitter goes from transport block to codeword to 16QAM symbols to the resource grid. The receiver takes the PDSCH REs out of the grid, demodulates them back into a codeword, and decodes the codeword back into the transport block.

Followings are the topics to be covered in this page.

SISO - Decoding PDSCH of one Subframe Resource Grid

Can the transport block be recovered from one subframe of the resource grid? This example builds subframe 0 with one antenna and 6 RB, as on the OFDM Symbol Extraction page, and then decodes the PDSCH from that grid. No channel and no noise are applied, so both checks at the end should return true.

    % First you have to define properites of a eNodeB.  

    % NDLRB indicate System Bandwith in the unit of RBs.

    % NDLRB 6 = 1.4 Mhz, NDLRB 15 = 3.0 Mhz, NDLRB 25 = 5.0 Mhz,

    % NDLRB 50 = 10 Mhz, NDLRB 75 = 15 Mhz, NDLRB 100 = 20 Mhz

    % CellRefP indicate number of downlink Antenna. CellRefP = 1 means 1 transmission antenna (SISO)

    % NCellID indicate PCI (Physical Cell Identity) of the Cell

    % NSubframe indicate the subframe number.

    enb.CyclicPrefix = 'Normal';

    enb.PHICHDuration = 'Normal';

    enb.Ng = 'Sixth';

    enb.NDLRB = 6;

    enb.CellRefP = 1;

    enb.DuplexMode = 'FDD';

     

    enb.NCellID = 0;

    enb.NSubframe = 0;

    enb.CFI = 1;

     

    % Now populate all the information in DCI field as you like. Understanding details of DCI is also pretty huge topics.

    % You would need separate page for DCI for the details.

     

    dci.NDLRB = enb.NDLRB;

    dci.DCIFormat = 'Format1A';

    dci.AllocationType = 0;

    dci.Allocation.RIV = 18;

    dci.ModCoding = 10;

    dci.HARQNo = 0;

    dci.NewData = 0;

    dci.TPCPUCCH = 0;

    dci.DuplexMode = 'FDD';

    dci.NTxAnts = 1;

     

    % once you defined all the detailed fields of DCI, just pass it to lteDCI() function with eNB info as follows,

    % then you will get the bit stream for the DCI.

     

    [dciMessage,dciMessageBits] = lteDCI(enb,dci);

     

    % for this step, you need to set a couple of additional parameters as shown below. C_RNTI will be XORed to CRC bits

    % PDCCHFormat will determined Aggregation Level.

    %          PDCCHFormat 0 indicate Aggregation Level 1

    %          PDCCHFormat 1 indicate Aggregation Level 2

    %          PDCCHFormat 2 indicate Aggregation Level 4

    %          PDCCHFormat 3 indicate Aggregation Level 8

     

    C_RNTI = 100;                         

    pdcchConfig.RNTI = C_RNTI;            

    pdcchConfig.PDCCHFormat = 0;          

     

    % then pass dciMessageBits and pdcchConfig to lteDCIEncode, the you would get the encoded bitstream.

     

    codedDciBits = lteDCIEncode(pdcchConfig, dciMessageBits);

     

    % If you pass the enb into ltePDCCHInfo() function, it will give you the amount of resources that can be allocated

    % for PDCCH allocation. This is not the amount of resource for only one DCI. It will give you the total/maximum

    % amount of the resources that can be allocated for PDCCH.

     

    pdcchDims = ltePDCCHInfo(enb);

     

    % With ltePDCCHSpace, you can get the list of all the possible spaces that can carry PDCCH. In this example,

    % the space were shown in the unit of bits.

     

    pdcchBits = -1*ones(pdcchDims.MTot, 1);

     

    % generate an array with the length that can accommodate all the possible PDCCH bits.

     

    candidates = ltePDCCHSpace(enb, pdcchConfig, {'bits', '1based'});

     

    % select one of the candidate bitSection and assign the codedDcitBits. You can select any candidate bit section,

    % but in this example, I selected the first candidate section.

     

    pdcchBits ( candidates(1, 1) : candidates(1, 2) ) = codedDciBits;

     

    % if pass the encodedBits into ltePDCCH(), it will generate the modulated physical layer symbols.

     

    pdcch_sym = ltePDCCH(enb, pdcchBits);

    pdcch_sym_ind = ltePDCCHIndices(enb,{'1based','re'});

    pdcch_sym_arrayIndex = 0:length(pdcch_sym)-1;

     

    % Now we set various parameters defining PDSCH channel. (In real transmission, you would need to create

    % a dci that is corresponding the configuration here. But in this example, I will go without defining DCI)

     

    pdsch.NTxAnts = 1;

    pdsch.NLayers = 1;

    pdsch.TxScheme = 'Port0';

    pdsch.Modulation = {'16QAM'};

    pdsch.RV = 0;

    pdsch.RNTI = C_RNTI;

     

    % Now I have to create a vector carrying the number of PRB indexes that will be used to carry this PDSCH.

    % for example, pdsch_prbs in following section would create a vector [0 1 2 3]

     

    START_RB = 0;

    N_RB = 4;

     

    pdsch_prbs = (START_RB:(START_RB+N_RB-1)).';

     

    % Now we have to generate a bit sequence which would exactly fit to the number of resource elements that are

    % allocated for PDSCH for this specific subframe. To figure out exact Resource Element information, unlike in other

    % channel processing, I would run ltePDSCHIndices() first. As you see in the following code, ltePDSCHIndices()

    % returns the information that would give you the size of transport block size in the unit of bits.

     

    [pdsch_sym_ind,pdschIndInfo] = ltePDSCHIndices(enb,pdsch,pdsch_prbs,{'1based','re'});

    codedTrBlkSize = pdschIndInfo.G;

     

    % now I would create a bit array that carries the user data. In this example, I generated randomly but in real

    % situation, this would carry your user data (e.g, image, movie, files etc)

     

    UncodedTrBlkSize = 300; % Must be less than coded transport size

    dlschTransportBlk = round(rand(1,UncodedTrBlkSize));

    % dlschTransportBlk = round(rand(1,codedTrBlkSize));

     

    % now if you pass all the information to lteDLSCH), it will generate the encoded codeword data for the transport

    % block you defined.

     

    codeword = lteDLSCH(enb,pdsch,codedTrBlkSize,dlschTransportBlk);

     

    % now if you pass the encoded data (codeword) with eNB and pdsch config to ltePDSCH(), you can generate

    % physical layer symbols for the encoded data.

     

    pdsch_sym = ltePDSCH(enb,pdsch,codeword);

    pdsch_sym_arrayIndex = 0:length(pdsch_sym)-1;

     

    % Following is to create an empty resource grid for one subframe.

     

    resourceGrid = lteDLResourceGrid(enb);

     

    % Following is to create symbols for Cell Specific Reference Signal and make a list of resource index for the

    % reference signal.

     

    rsAnt0 = lteCellRS(enb,0);

    indAnt0 = lteCellRSIndices(enb,0);

    resourceGrid(indAnt0) = rsAnt0;

     

    % Following is to create symbols for PBCH and make a list of resource index for the signal (channel)

     

    mib_bits = lteMIB(enb);

    bch_cw = lteBCH(enb,mib_bits);

     

    pbch_sym = ltePBCH(enb,bch_cw);

    pbch_sym_arrayIndex = 0:length(pbch_sym)-1;

    pbch_sym_ind = ltePBCHIndices(enb,{'1based','re'});

     

    % Following is to create symbols for PSS and make a list of resource index for the signal

     

    pss = ltePSS(enb);

    pss_arrayIndex = 0:length(pss)-1;

    pss_sym_ind = ltePSSIndices(enb,0,{'1based','re'});

     

    % Following is to create symbols for SSS and make a list of resource index for the signal

     

    sss = lteSSS(enb);

    sss_arrayIndex = 0:length(sss)-1;

    sss_sym_ind = lteSSSIndices(enb,0,{'1based','re'});

     

    % Following is to create symbols for PCFICH and make a list of resource index for the signal

     

    cfi_cw = lteCFI(enb);

    pcfich_sym = ltePCFICH(enb,cfi_cw);

    pcfich_sym_arrayIndex = 0:length(pcfich_sym)-1;

    pcfich_sym_ind = ltePCFICHIndices(enb,{'1based','re'});

     

    % Following is to create symbols for PHICH and make a list of resource index for the signal

     

    PHICH_Group_Index = 0;

    PHICH_Sequence_Index = 1;

    HARQ_Indicator_Value = 0; % 0 = NACK, 1 = ACK

     

    phich_sym = ltePHICH(enb,[PHICH_Group_Index,PHICH_Sequence_Index,HARQ_Indicator_Value]);

    phich_sym_arrayIndex = 0:length(phich_sym)-1;

    phich_sym_ind = ltePHICHIndices(enb,{'1based','re'});

     

    % Following part is filling the resource grid with each of the signal..

     

    pss_scale = 1.0;

    sss_scale = 1.0;

    phich_scale = 1.0;

    pcfich_scale = 1.0;

    pbch_scale = 1.0;

    pdcch_scale = 1.0;

    pdsch_scale = 1.0;

     

    resourceGrid(pss_sym_ind) = pss_scale .* pss;

    resourceGrid(sss_sym_ind) = sss_scale .* sss;

    resourceGrid(pcfich_sym_ind) = pcfich_scale .* pcfich_sym;

    resourceGrid(phich_sym_ind) = phich_scale .* phich_sym;

    resourceGrid(pbch_sym_ind) = pbch_scale .* pbch_sym(1:length(pbch_sym_ind));

    resourceGrid(pdcch_sym_ind) = pdcch_scale .* pdcch_sym;

    resourceGrid(pdsch_sym_ind) = pdsch_scale .* pdsch_sym;

     

    % This is not a required step, but I tried to change the name resourceGrid to a more meaningful name.

     

    txSubframe = resourceGrid;

     

    % This is the step to retrieve PDSCH physical layer symbol from the resourceGrid. In this example, I used the

    % pdsch_sym_ind that I used to generate PDSCH. But in real situration, the reciever should figure out the indice

    % based on enb parameter and DCI parameters.

     

    RxPdschSym = txSubframe(pdsch_sym_ind);

     

    % Once you get PDSCH physical layer symbols (I/Q) , just pass it into ltePDSCHDecode() and it will return the

    % codeword data. As you see, I used the same variable enb, pdsch that I used when I was generating PDSCH.

    % But in real situration, the reciever should figure out the indice based on enb parameter and DCI parameters.

     

    [rxCW,RxSymbols] = ltePDSCHDecode(enb,pdsch,RxPdschSym);

     

    % rxCW that was obtained in previous step is a 'cell' object. It is not in the form of ordinary array or vector format.

    % To convert the cell object into a vector, you can use cell2mat() function as follows.

     

    rxCWarray=cell2mat(rxCW);

     

    % if you print out rxCWarray (e.g, rxCWarray(1:10)), you will notice it is not a bit sequence (binary data).

    % they are real numbers. To convert the real number to binary number corresponding to 0 or 1, you can use

    % 'rxCWarray>0'. And use isequal() to compare the decoded PDSCH and original PDSCH codeword to verify

    % if the decoding processing was performed properly.

     

    isequal(int8(rxCWarray>0),double(codeword))

     

    decState = [];

     

    chanConfig.NLayers = pdsch.NLayers;

    chanConfig.TxScheme = pdsch.TxScheme;

    chanConfig.Modulation = pdsch.Modulation;

    chanConfig.RV = pdsch.RV;

    chanConfig.NTurboDecIts = 5;

     

    [rxTrBlk,blockCrc,decState] = lteDLSCHDecode(enb,chanConfig,UncodedTrBlkSize,rxCWarray,decState);

    isequal(int8(rxTrBlk'),int8(dlschTransportBlk))

     

     

The code runs the whole chain in one script. The transmit part is the same as on the PDSCH page. The function lteDLSCH encodes the transport block into a codeword of G bits, and ltePDSCH scrambles and modulates the codeword into 16QAM symbols. The receive part starts at RxPdschSym = txSubframe(pdsch_sym_ind), which reads the PDSCH REs back out of the grid.

The table below lists each step with its size in this example. The sizes follow from the configuration: 4 RB, CFI 1 at 6 RB, and subframe 0.

 

Step

Transmit function

Receive function

Size in this example

Transport block

rand

lteDLSCHDecode output

300 bits

Channel coding

lteDLSCH

lteDLSCHDecode

300 + 24 CRC bits, coded to G = 1088 bits

Scrambling and modulation

ltePDSCH

ltePDSCHDecode

1088 bits, 272 16QAM symbols

RE mapping

resourceGrid(pdsch_sym_ind)

txSubframe(pdsch_sym_ind)

272 REs

 

G comes from the RE count. With 6 RB, CFI 1 gives two control symbols, because 36.211 v19.3.0 Table 6.7-1 uses CFI + 1 symbols when the carrier has 10 RB or fewer. In subframe 0 at 6 RB, the SSS, PSS and PBCH fill symbols 5 to 10 across the whole carrier. The PDSCH therefore keeps symbols 2, 3, 4, 11, 12 and 13 on 48 subcarriers, which is 288 REs. The CRS take 8 REs in each of symbols 4 and 11, so 272 REs remain, and 16QAM gives 1088 bits.

On the coding side, lteDLSCH attaches a 24-bit CRC to the 300-bit block, which makes 324 bits. That is less than 6144, so 36.212 clause 5.1.2 keeps one code block. The turbo code of clause 5.1.3.2 needs a block size from Table 5.1.3-3, and the smallest one of at least 324 is 328, so 4 filler bits are added. Rate matching then fits the coded block into the 1088 bits of G, a code rate of about 0.3.

The code had one line too many here. After dlschTransportBlk = round(rand(1,UncodedTrBlkSize)), a second line set dlschTransportBlk to codedTrBlkSize random bits, 1088 instead of 300. The receiver decodes UncodedTrBlkSize = 300 bits, so the final isequal compared 300 bits with 1088 and returned false. That second line is now commented out, and the transmitted block is the 300 bits the receiver expects.

The script checks the result twice. The first isequal compares the hard decisions of the soft bits from ltePDSCHDecode with the transmitted codeword. The second compares the output of lteDLSCHDecode with the transport block. The blockCrc output of lteDLSCHDecode gives the same answer from the CRC alone, which is all a real receiver has. In a real receiver, the PDSCH indices, the modulation and the transport block size all come from the DCI rather than from the transmitter's variables.

  • ltePDSCHDecode undoes ltePDSCH : 272 16QAM symbols back into 1088 soft bits.
  • lteDLSCHDecode undoes lteDLSCH : 1088 soft bits back into the 300-bit block and a CRC flag.
  • G = 1088 bits : 272 REs on 4 RB in subframe 0 with 16QAM.
  • The transport block must be 300 bits : the size the decoder is given.

Disclaimer !

This page is only to show you the overall logics and visualization for various LTE physical layer channels. I haven't investigated much about verifying about the accuracy.

If you think the code is not so efficient, it is 100% my fault. I haven't made any effort for effiecient code. I just tried to create code as simple as possible for the readers. As you know, easy-to-read code is not always efficient for a specific chipset.

If you find any mistake in terms of accuracy, it is also very highly likely be my fault. Not the problem of Matlab tool box itself.

Any comment and corrections if you find any mistake will be welcome and appreciated.

Reference

[1] 3GPP TS 36.211 v19.3.0 - clause 6.4, Physical downlink shared channel, and Table 6.7-1, Number of OFDM symbols used for PDCCH

[2] 3GPP TS 36.212 v19.3.0 - clauses 5.1.1 to 5.1.4 and 5.3.2, CRC, code block segmentation, turbo coding, rate matching and the DL-SCH