free5GRAN  V1.0
Samples extraction

Once synchronized, receiver has to extract physical channels samples from received time domain signal. This is done by removing Cyclic Prefixes (CP), performing FFT (for recovering RE grid) and by finally de-mapping channels to extract samples from RE grid. Physical channels extraction differs depending on the studied channel:

CP removal

CP removal is done in FFT implementation, while extracting time-domain signal (file lib/phy/libphy.cpp line 568):

for (int i = 0; i < fft_size; i++){
fft_in[i][0] = real(time_domain_signal[i + offset + cum_sum_symb[symb_index] + cp_lengths[symb_index]]);
fft_in[i][1] = imag(time_domain_signal[i + offset + cum_sum_symb[symb_index] + cp_lengths[symb_index]]);
}

Adding cp_lengths[symb_index] while extracting time domain signal deletes the CP (original symbol length is fft_size + cp_lengths[symb_index] and we only take the fft_size-th last elements).

FFT

FFT function is called from implementation part in src/phy/phy.cpp, for example:

free5GRAN::phy::signal_processing::fft(ssb_signal, ssb_symbols,fft_size,cp_lengths_pbch,&cum_sum_fft[0],free5GRAN::NUM_SYMBOLS_SSB - 1,free5GRAN::NUM_SC_SSB,1,0);

Channel demapping

Channel demapping is done using channel_demapper function implemented in library and called from src/phy/phy.cpp.

PBCH

Receiver deduces PBCH symbols position from PSS position. PBCH and PBCH DMRS samples extraction depends on cell PCI (Further details can be found in TS 38.211 Section 7.3.3.3 and 7.4.1.4.2.

We first compute different channel indexes in RE grid:

free5GRAN::phy::physical_channel::compute_pbch_indexes(ref, pci);

And then perform channel demapping:

free5GRAN::phy::signal_processing::channel_demapper(ssb_symbols, ref, output_channels, channel_indexes, 3, free5GRAN::NUM_SYMBOL_PBCH_SSB, free5GRAN::NUM_SC_SSB);

PDCCH

Receiver performs blind search as PDCCH time and frequency position are not known. MIB data, decoded from PBCH, gives information about CORESET0, which is a set of frequency and time positions (detailed in TS 38.213 Section 13) where PDCCH could be located. Receiver has to search over all the possible positions. Each candidate can be validated by CRC computation. For each candidate, receiver computes PDCCH and PDCCH DMRS positions, as detailed in TS 38.211 Section 7.3.2.5 and 7.4.1.3.2. Before extracting PDCCH, CCE-to-REG mapping has to be computed:

int height_reg_rb = free5GRAN::NUMBER_REG_PER_CCE / pdcch_ss_mon_occ.n_symb_coreset;
int R = 2;
int C = pdcch_ss_mon_occ.n_rb_coreset / (height_reg_rb * R);
int j;
int reg_index[C * R];
for (int c = 0; c < C; c ++){
for (int r = 0; r < R; r ++){
j = c * R + r;
reg_index[j] = (r * C + c + this->pci) % (pdcch_ss_mon_occ.n_rb_coreset/height_reg_rb);
}
}

First, receiver loops over different monitoring slots:

for (int monitoring_slot = 0; monitoring_slot < 2; monitoring_slot ++){
}

For the corresponding slot, we compute the DMRS sequence:

for (int symb = 0; symb < pdcch_ss_mon_occ.n_symb_coreset; symb ++){
free5GRAN::utils::sequence_generator::generate_pdcch_dmrs_sequence(pci, pdcch_ss_mon_occ.n0 + monitoring_slot, pdcch_ss_mon_occ.first_symb_index + symb, global_sequence[symb], pdcch_ss_mon_occ.n_rb_coreset * 3);
}

Then, loop over possibles aggregation levels:

for (int i = 2; i < 5; i ++){
}

and over candidates:

for (int p = 0; p < num_candidates; p ++){
}

Compute channels indexes in RE grid:

free5GRAN::phy::physical_channel::compute_pdcch_indexes(ref, pdcch_ss_mon_occ, agg_level, reg_bundles, height_reg_rb);

And perform channel de-mapping:

free5GRAN::phy::signal_processing::channel_demapper(coreset_0_samples, ref, output_channels, channel_indexes, 2, pdcch_ss_mon_occ.n_symb_coreset, 12 * pdcch_ss_mon_occ.n_rb_coreset);

Finally, perform DMRS sequence CCE-to-REG interleaving:

for (int k = 0 ; k < agg_level; k ++){
for (int reg = 0; reg < free5GRAN::NUMBER_REG_PER_CCE; reg ++){
dmrs_sequence[((agg_level * free5GRAN::NUMBER_REG_PER_CCE * 3) / pdcch_ss_mon_occ.n_symb_coreset) * (reg%pdcch_ss_mon_occ.n_symb_coreset) + k * height_reg_rb * 3 + (reg/pdcch_ss_mon_occ.n_symb_coreset) * 3] = global_sequence[reg%pdcch_ss_mon_occ.n_symb_coreset][reg_bundles[k] * height_reg_rb * 3 + (reg/pdcch_ss_mon_occ.n_symb_coreset) * 3];
dmrs_sequence[((agg_level * free5GRAN::NUMBER_REG_PER_CCE * 3) / pdcch_ss_mon_occ.n_symb_coreset) * (reg%pdcch_ss_mon_occ.n_symb_coreset) + k * height_reg_rb * 3 + (reg/pdcch_ss_mon_occ.n_symb_coreset) * 3 + 1] = global_sequence[reg%pdcch_ss_mon_occ.n_symb_coreset][reg_bundles[k] * height_reg_rb * 3 + (reg/pdcch_ss_mon_occ.n_symb_coreset) * 3 + 1];
dmrs_sequence[((agg_level * free5GRAN::NUMBER_REG_PER_CCE * 3) / pdcch_ss_mon_occ.n_symb_coreset) * (reg%pdcch_ss_mon_occ.n_symb_coreset) + k * height_reg_rb * 3 + (reg/pdcch_ss_mon_occ.n_symb_coreset) * 3 + 2] = global_sequence[reg%pdcch_ss_mon_occ.n_symb_coreset][reg_bundles[k] * height_reg_rb * 3 + (reg/pdcch_ss_mon_occ.n_symb_coreset) * 3 + 2];
}
}

Finally, if CRC is validated, PDCCH is found and nested loops stop.

PDSCH

Receiver extracts PDSCH based on DCI, decoded from PDCCH. It gives time and frequency position of PDSCH (as explained in TS 38.214 Section 5.1). PDSCH DMRS positions vary depending on PDSCH resources allocation.

First, receiver computes channels indexes in RE grid:

free5GRAN::phy::physical_channel::compute_pdsch_indexes(ref, dmrs_symbol_array, L, lrb);

And then performs channel de-mapping:

free5GRAN::phy::signal_processing::channel_demapper(pdsch_samples, ref, output_channels, channel_indexes, 2, L, 12 * lrb);