SDR  - srsLTE Example - Pdsch_enodeb                                Home : www.sharetechnote.com

 

 

 

 

srsLTE - Example - Pdsch_enodeb

 

This example is implemented by the source srsLTE\examples\Pdsch_enodeb.c. The functionality of this example is to generate I/Q data for LTE downlink radio frame into a file. This would be a very good example of understanding the overall process of generating a various downlink sigan (e.g, PSS, SSS, Reference Signal) and channels (PBCH, PDCCH, PDSCH) and combining all those signals and channels into a radio frame.

 

By setting a simple flag, you can let this function to generate the data into a file (meaning you don't need any RF hardware).

 

Since my main purpose is to understand the details of the code, not building a working hardware. I chose to look into the details of the code that generate various signals and channels into a file. I simplied the code which is directly related to this process and removing all error handling parts just leaving the parts that is directly related to the decoding process.

 

I hope this summary and additional comments help you to undertand the original source code in the project.

 

Now this page is still under progress and it will take a couple of weeks for me to complete the comments for the pages. However, just read through the function names and procedure in the code without any comments would give you some big picture of the process.

 

 

Case 1 : Generating eNB frame into a file

 

/**

 *

 * \section COPYRIGHT

 *

 * Copyright 2013-2015 Software Radio Systems Limited

 *

 * \section LICENSE

 *

 * This file is part of the srsLTE library.

 *

 * srsLTE is free software: you can redistribute it and/or modify

 * it under the terms of the GNU Affero General Public License as

 * published by the Free Software Foundation, either version 3 of

 * the License, or (at your option) any later version.

 *

 * srsLTE is distributed in the hope that it will be useful,

 * but WITHOUT ANY WARRANTY; without even the implied warranty of

 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the

 * GNU Affero General Public License for more details.

 *

 * A copy of the GNU Affero General Public License can be found in

 * the LICENSE file in the top-level directory of this distribution

 * and at http://www.gnu.org/licenses/.

 *

 */

....

 

// As you see, UE_CRNTI is hardcoded in this example.

#define UE_CRNTI 0x1234

 

// You can define the basic Cell Proprties (e.g, Number of PRBs, Antenna Configuration, System BW, Cell ID etc)

srslte_cell_t cell = {

  25,            // nof_prb

  1,            // nof_ports

  0,            // bw idx

  0,            // cell_id

  SRSLTE_CP_NORM,       // cyclic prefix

  SRSLTE_PHICH_R_1,          // PHICH resources      

  SRSLTE_PHICH_NORM    // PHICH length

};

  

 

// Define CFI value and MCS idex

uint32_t cfi=3;

uint32_t mcs_idx = 1, last_mcs_idx = 1;

 

....

 

void usage(char *prog) {

....

}

 

void parse_args(int argc, char **argv) {

....

}

 

// Initialize all of the resourced that are required to generate and store physical layer signal data

void base_init() {

  

  // Create a buffer(array) that will store all the resource elements in the specified number of subframes

  sf_buffer = srslte_vec_malloc(sizeof(cf_t) * sf_n_re);

  

  // Create a buffer(array) that will store all the OFDM symbol data in the specified number of subframes

  output_buffer = srslte_vec_malloc(sizeof(cf_t) * sf_n_samples);

 

  // Open a file to store the generated I/Q data

  if (output_file_name) {

    if (strcmp(output_file_name, "NULL")) {

      if (srslte_filesink_init(&fsink, output_file_name, SRSLTE_COMPLEX_FLOAT_BIN)) {

        ...

      }      

      null_file_sink = false;

    } else {

      null_file_sink = true;

    }

  } else {

.  ..

  }

  

  ....

 

  // Initialize the array to store OFDM data

  if (srslte_ofdm_tx_init(&ifft, SRSLTE_CP_NORM, cell.nof_prb)) {

    ....

  }

  srslte_ofdm_set_normalize(&ifft, true);

  

  // Initialize the array to store PBCH data

 if (srslte_pbch_init(&pbch, cell)) {

    ....

  }

 

  // Initialize the array to store REG data

  if (srslte_regs_init(&regs, cell)) {

    ...

  }

 

  // Initialize the array to store PCFICH data

  if (srslte_pcfich_init(&pcfich, &regs, cell)) {

    ....

  }

 

  // Initialize the array to REGs assigned for CFI

  if (srslte_regs_set_cfi(&regs, cfi)) {

    ....

  }

 

  // Initialize the array to store PDCCH data

  if (srslte_pdcch_init(&pdcch, &regs, cell)) {

    ....

  }

 

  // Initialize the array to store PDSCH data

  if (srslte_pdsch_init(&pdsch, cell)) {

    ....

  }

  

  srslte_pdsch_set_rnti(&pdsch, UE_CRNTI);

  

  if (srslte_softbuffer_tx_init(&softbuffer, cell.nof_prb)) {

    ....

  }

}

 

void base_free() {

....  

}

 

 

bool go_exit = false;

void sig_int_handler(int signo)

{

...

}

 

uint32_t prbset_to_bitmask() {

  uint32_t mask=0;

  int nb = (int) ceilf((float) cell.nof_prb / srslte_ra_type0_P(cell.nof_prb));

  for (int i=0;i<nb;i++) {

    if (i >= prbset_orig && i < prbset_orig + prbset_num) {

      mask = mask | (0x1<<i);     

    }

  }

  return reverse(mask)>>(32-nb);

}

 

// Configure and initialize all the parameters and arrays that is necessary for RACH process

int update_radl() {

  

  bzero(&ra_dl, sizeof(srslte_ra_dl_dci_t));

  ra_dl.harq_process = 0;

  ra_dl.mcs_idx = mcs_idx;

  ra_dl.ndi = 0;

  ra_dl.rv_idx = 0;

  ra_dl.alloc_type = SRSLTE_RA_ALLOC_TYPE0;

  ra_dl.type0_alloc.rbg_bitmask = prbset_to_bitmask();

 

  srslte_ra_pdsch_fprint(stdout, &ra_dl, cell.nof_prb);

  srslte_ra_dl_grant_t dummy_grant;

  srslte_ra_nbits_t dummy_nbits;

  srslte_ra_dl_dci_to_grant(&ra_dl, cell.nof_prb, true, &dummy_grant);

  srslte_ra_dl_grant_to_nbits(&dummy_grant, cfi, cell, 0, &dummy_nbits);

  srslte_ra_dl_grant_fprint(stdout, &dummy_grant);

  printf("Type new MCS index and press Enter: "); fflush(stdout);

 

  return 0;

}

 

/* Read new MCS from stdin */

int update_control() {

  char input[128];

  

  fd_set set;

  FD_ZERO(&set);

  FD_SET(0, &set);

  

  struct timeval to;

  to.tv_sec = 0;

  to.tv_usec = 0;

 

  int n = select(1, &set, NULL, NULL, &to);

  if (n == 1) {

    // stdin ready

    if (fgets(input, sizeof(input), stdin)) {

      if(input[0] == 27) {

        switch(input[2]) {

          case RIGHT_KEY:

            if (prbset_orig  + prbset_num < (int) ceilf((float) cell.nof_prb / srslte_ra_type0_P(cell.nof_prb)))

              prbset_orig++;

            break;

          case LEFT_KEY:

            if (prbset_orig > 0)

              prbset_orig--;

            break;

          case UP_KEY:

            if (prbset_num < (int) ceilf((float) cell.nof_prb / srslte_ra_type0_P(cell.nof_prb)))

              prbset_num++;

            break;

          case DOWN_KEY:

            last_prbset_num = prbset_num;

            if (prbset_num > 0)

              prbset_num--;          

            break;          

        }

      } else {

        last_mcs_idx = mcs_idx;

        mcs_idx = atoi(input);          

      }

      bzero(input,sizeof(input));

      if (update_radl()) {

        printf("Trying with last known MCS index\n");

        mcs_idx = last_mcs_idx;

        prbset_num = last_prbset_num;

        return update_radl();

      }

    }

    return 0;

  } else if (n < 0) {

    // error

    perror("select");

    return -1;

  } else {

    return 0;

  }

}

 

 

int main(int argc, char **argv) {

 

  int nf=0, sf_idx=0, N_id_2=0;

 

  // Create cf_t (complex number) array with the size of SRSLTE_PSS_LEN. This is to store the PSS (Primary Sync

  // Signal) data.  Since PSS is made up of 62 data points, the size of this array (SRSLTE_PSS_LEN) is 62.

  cf_t pss_signal[SRSLTE_PSS_LEN];

 

  // Create a float array with the size of SRSLTE_SSS_LEN. This is to store SSS (Secondary Sync Signal) transmitted

  // on subframe 0.  Since PSS is made up of 62 data points, the size of this array (SRSLTE_SSS_LEN) is 62.

  // Since SSS does not have any imaginary part, it is generated as real number array.

  float sss_signal0[SRSLTE_SSS_LEN]; // for subframe 0

 

  // Create a float array with the size of SRSLTE_SSS_LEN. This is to store SSS (Secondary Sync Signal) transmitted

  // on subframe 5.  Since PSS is made up of 62 data points, the size of this array (SRSLTE_SSS_LEN) is 62.

  // Since SSS does not have any imaginary part, it is generated as real number array.

  float sss_signal5[SRSLTE_SSS_LEN]; // for subframe 5

 

  // Create an array with the size of SRSLTE_BCH_PAYLOAD_LEN. This is to store the binary data of BCH.

  // Since this is BCH data before encoding process, the size of this array(SRSLTE_BCH_PAYLOAD_LEN) is 24.

  uint8_t bch_payload[SRSLTE_BCH_PAYLOAD_LEN];

 

  int i;

 

  // Create cf_t (complex number) pointer arrays with the size of SRSLTE_MAX_PORTS. This is to store the subframe

  // symbols. SRSLTE_MAX_PORTS is 4 as of now.

  cf_t *sf_symbols[SRSLTE_MAX_PORTS];

  cf_t *slot1_symbols[SRSLTE_MAX_PORTS];

  srslte_dci_msg_t dci_msg;

  srslte_dci_location_t locations[SRSLTE_NSUBFRAMES_X_FRAME][30];

  uint32_t sfn;

  srslte_chest_dl_t est;

  

  parse_args(argc, argv);

 

  // Calculate Cell Number (One of the three PSS sequence type) from cell ID

  N_id_2 = cell.id % 3;

 

  // Calculate the total number of REs that is in the PRB region of one subframe. This include RE for all PHY channel

  // and signals (i.e, CRS, PDCCH, PDSCH etc).

  //    SRSLTE_CP_NORM_NSYMB = 7 symbols (the number of OFDM symbole in one slot)

  //    SRSLTE_NRE = 12 (the number of subcarriers in one RB)

  //    cell.nof_prb = Number of PRBs that is specified by user

  sf_n_re = 2 * SRSLTE_CP_NORM_NSYMB * cell.nof_prb * SRSLTE_NRE;

 

  // TBD

  sf_n_samples = 2 * SRSLTE_SLOT_LEN(srslte_symbol_sz(cell.nof_prb));

 

  // Set PHICH Parameters

  //   SRSLTE_PHICH_NORM = 0

  //   SRSLTE_PHICH_R_1 = 2

  cell.phich_length = SRSLTE_PHICH_NORM;

  cell.phich_resources = SRSLTE_PHICH_R_1;

  sfn = 0;

 

  // Calculate the number of RBG(Resource Block Group) for the specified number of PRBs.

  // RBG varies depending on System BW and used for RA Type 0

  prbset_num = (int) ceilf((float) cell.nof_prb / srslte_ra_type0_P(cell.nof_prb));

  last_prbset_num = prbset_num;

  

  /* this *must* be called after setting slot_len_* */

  // Create resources (e.g, arrays) for storing each physical channel data and OFDM signal

  base_init();

 

  // Generate PSS for N_id_2 and store the result into the array pss_signal

  srslte_pss_generate(pss_signal, N_id_2);

 

  // Generate SSS for subframe 0 and 5 for cell.id and store the result into the arrays sss_signal0 and sss_signal5

  srslte_sss_generate(sss_signal0, sss_signal5, cell.id);

  

  // Generate the Cell Specific Reference Signal using parameter set in the structure "cell" and store the result in

  // the array "est"

  if (srslte_chest_dl_init(&est, cell)) {

    ....

  }

 

  // Create subframe buffer for each antenna port

  for (i = 0; i < SRSLTE_MAX_PORTS; i++) { // now there's only 1 port

    // Copy the pointer of sf_buffer to sf_symbols[port].

    // sf_buffer is created in base_init()

    sf_symbols[i] = sf_buffer;  

    

    // Get the pointer to the start of slot1 symbol and assign it to slot1_symbols[port]

    // SRSLTE_SLOT_LEN_RE(cell.nof_prb, cell.cp) would gives RE number of the start of Slot1

    slot1_symbols[i] = &sf_buffer[SRSLTE_SLOT_LEN_RE(cell.nof_prb, cell.cp)];

  }

 

#ifndef DISABLE_RF

  // I am removing this part, because I am not using UART hardware in this example

#endif

 

  // Configure and initialize all the parameters and arrays that is necessary for RACH process

  if (update_radl(sf_idx)) {

    exit(-1);

  }

  

  if (net_port > 0) {

   // I am removing this part because I am interested only in PHY layer

  }

  

  /* Initiate valid DCI locations */

  for (i=0;i<SRSLTE_NSUBFRAMES_X_FRAME;i++) {

    srslte_pdcch_ue_locations(&pdcch, locations[i], 30, i, cfi, UE_CRNTI);

    

  }

    

  nf = 0;

  

  bool send_data = false;

  srslte_softbuffer_tx_reset(&softbuffer);

 

#ifndef DISABLE_RF

  bool start_of_burst = true;

#endif

  

  while ((nf < nof_frames || nof_frames == -1) && !go_exit) {

    for (sf_idx = 0; sf_idx < SRSLTE_NSUBFRAMES_X_FRAME && (nf < nof_frames || nof_frames == -1); sf_idx++) {

      bzero(sf_buffer, sizeof(cf_t) * sf_n_re);

 

      if (sf_idx == 0 || sf_idx == 5) {

        srslte_pss_put_slot(pss_signal, sf_buffer, cell.nof_prb, SRSLTE_CP_NORM);

        srslte_sss_put_slot(sf_idx ? sss_signal5 : sss_signal0, sf_buffer, cell.nof_prb,

            SRSLTE_CP_NORM);

      }

 

      srslte_refsignal_cs_put_sf(cell, 0, est.csr_signal.pilots[0][sf_idx], sf_buffer);

 

      srslte_pbch_mib_pack(&cell, sfn, bch_payload);

      if (sf_idx == 0) {

        srslte_pbch_encode(&pbch, bch_payload, slot1_symbols);

      }

 

      srslte_pcfich_encode(&pcfich, cfi, sf_symbols, sf_idx);       

 

      /* Update DL resource allocation from control port */

      if (update_control(sf_idx)) {

        fprintf(stderr, "Error updating parameters from control port\n");

      }

      

      /* Transmit PDCCH + PDSCH only when there is data to send */

      if (net_port > 0) {

        send_data = net_packet_ready;

        if (net_packet_ready) {

          INFO("Transmitting packet\n",0);

        }

      } else {

        INFO("SF: %d, Generating %d random bits\n", sf_idx, pdsch_cfg.grant.mcs.tbs);

        for (i=0;i<pdsch_cfg.grant.mcs.tbs/8;i++) {

          data[i] = rand()%256;

        }

        /* Uncomment this to transmit on sf 0 and 5 only  */

        if (sf_idx != 0 && sf_idx != 5) {

          send_data = true;

        } else {

          send_data = false;           

        }

      }        

      

      if (send_data) {

              

        /* Encode PDCCH */

        srslte_dci_msg_pack_pdsch(&ra_dl, &dci_msg, SRSLTE_DCI_FORMAT1, cell.nof_prb, false);

        INFO("Putting DCI to location: n=%d, L=%d\n", locations[sf_idx][0].ncce, locations[sf_idx][0].L);

        if (srslte_pdcch_encode(&pdcch, &dci_msg, locations[sf_idx][0], UE_CRNTI, sf_symbols, sf_idx, cfi)) {

          fprintf(stderr, "Error encoding DCI message\n");

          exit(-1);

        }

 

        /* Configure pdsch_cfg parameters */

        srslte_ra_dl_grant_t grant;

        srslte_ra_dl_dci_to_grant(&ra_dl, cell.nof_prb, true, &grant);        

        if (srslte_pdsch_cfg(&pdsch_cfg, cell, &grant, cfi, sf_idx, 0)) {

          fprintf(stderr, "Error configuring PDSCH\n");

          exit(-1);

        }

       

        /* Encode PDSCH */

        if (srslte_pdsch_encode(&pdsch, &pdsch_cfg, &softbuffer, data, sf_symbols)) {

          fprintf(stderr, "Error encoding PDSCH\n");

          exit(-1);

        }        

        if (net_port > 0 && net_packet_ready) {

          if (null_file_sink) {

            srslte_bit_pack_vector(data, data_tmp, pdsch_cfg.grant.mcs.tbs);

            if (srslte_netsink_write(&net_sink, data_tmp, 1+(pdsch_cfg.grant.mcs.tbs-1)/8) < 0) {

              fprintf(stderr, "Error sending data through UDP socket\n");

            }            

          }

          net_packet_ready = false;

          sem_post(&net_sem);

        }

      }

      

      /* Transform to OFDM symbols */

      srslte_ofdm_tx_sf(&ifft, sf_buffer, output_buffer);

      

      /* send to file or usrp */

      if (output_file_name) {

        if (!null_file_sink) {

          srslte_filesink_write(&fsink, output_buffer, sf_n_samples);          

        }

        usleep(1000);

      } else {

#ifndef DISABLE_RF

        // FIXME

        float norm_factor = (float) cell.nof_prb/15/sqrtf(pdsch_cfg.grant.nof_prb);

        srslte_vec_sc_prod_cfc(output_buffer, rf_amp*norm_factor, output_buffer, SRSLTE_SF_LEN_PRB(cell.nof_prb));

        srslte_rf_send2(&rf, output_buffer, sf_n_samples, true, start_of_burst, false);

        start_of_burst=false;

#endif

      }

    }

    nf++;

    sfn = (sfn + 1) % 1024;

  }

 

  base_free();

 

  printf("Done\n");

  exit(0);

}

 

 

Result :-------------------------------------------------------------------------------------

 

Now let's generate a downlink frames as below. You can put the option values as you like. With the following command, a downlink frame with the length of 10 radio frames (100 subframes) and MCS = 9

 

# ~/srsLTE/build/srslte/examples$ ./pdsch_enodeb -o "pdsch.out" -n 10 -m 9

 

linux; GNU C++ version 5.3.1 20160413; Boost_105800; UHD_003.009.004-release

 

 - Resource Allocation Type:        Type 0

   + Resource Block Group Size:     2

   + RBG Bitmap:            0x1fff

 - Modulation and coding scheme index:  9

 - HARQ process:            0

 - New data indicator:          No

 - Redundancy version:          0

 - TPC command for PUCCH:       --

 - PRB Bitmap Assignment 0st slot:

0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,

 - PRB Bitmap Assignment 1st slot:

0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,

 - Number of PRBs:          25

 - Modulation type:         QPSK

 - Transport block size:        4008

Type new MCS index and press Enter: Done

root@root-VirtualBox:~/srsLTE/build/srslte/examples$ ./pdsch_ue -i "pdsch.out" -n 10 -r 1234

linux; GNU C++ version 5.3.1 20160413; Boost_105800; UHD_003.009.004-release

 

 - Cell ID:         0

 - Nof ports:       1

 - CP:              Normal  

 - PRB:             25

 - PHICH Length:    Normal

 - PHICH Resources: 1

 - SFN:             0

Decoded MIB. SFN: 0, offset: 0

CFO:  +0.00 kHz, SNR: 139.3 dB, PDCCH-Miss: 20.00%, PDSCH-BLER:  0.00%

Bye