Commit 66442aa4 authored by Ahmed Hussein's avatar Ahmed Hussein Committed by Thomas Schlichter

Implementation of LLR computation for 4, 16, and 64 QAM (tested)

parent 54ebd39b
Branches unavailable
2025.w08 2025.w07 2025.w06 2025.w05 2025.w04 2025.w03 2025.w02 2024.w51 2024.w50 2024.w49 2024.w48 2024.w47 2024.w46 2024.w45 2024.w44 2024.w43 2024.w42 2024.w41 2024.w40 2024.w39 2024.w38 2024.w36 2024.w35 2024.w34 2024.w33 2024.w32 2024.w31 2024.w30 2024.w29 2024.w28 2024.w27 2024.w26 2024.w25 2024.w24 2024.w23 2024.w22 2024.w21 2024.w18 2024.w17 2024.w16 2024.w15 2024.w14 2024.w13 2024.w12 2024.w11 2024.w10 2024.w09 2024.w08 2024.w06 2024.w05 2024.w04 2024.w03 2024.w02 2024.w01 2023.w51 2023.w50 2023.w49 2023.w48 2023.w47 2023.w45 2023.w43 2023.w42 2023.w41 2023.w40 2023.w39 2023.w38 2023.w37 2023.w36 2023.w34 2023.w33 2023.w32 2023.w31 2023.w30 2023.w29 2023.w28 2023.w27 2023.w26 2023.w25 2023.w24 2023.w23 2023.w22 2023.w21 2023.w20 2023.w19 2023.w18 2023.w18b 2023.w16 2023.w15 2023.w14 2023.w13 2023.w12 2023.w11 2023.w11b 2023.w10 2023.w10b 2023.w09 2023.w08 2023.w08b 2023.w07 2023.w06 2023.w05 2023.w03 2023.w02 2022.42 2022.41 2022.w51 2022.w50 2022.w49 2022.w48 2022.w47 2022.w46 2022.w45 2022.w43 2022.w42 2022.w42b 2022.w41 2022.w40 2022.w39 2022.w38 2022.w37 2022.w37b 2022.w36 2022.w35 2022.w33 2022.w32 2022.w31 2022.w31b 2022.w30 2022.w29 2022.w26 2022.w25 2022.w24 2022.w24b 2022.w23 2022.w22 2022.w21 2022.w20 2022.w19 2022.w18 2022.w17 2022.w15 2022.w15b 2022.w14a 2022.w13 2022.w13b 2022.w13a 2022.w12 2022.w10 2022.w09 2022.w09b 2022.w08 2022.w08b 2022.w07 2022.w07b 2022.w06 2022.w06a 2022.w05 2022.w05b 2022.w03_hotfix 2022.w03_b 2022.w02 2022.w01 2021.wk46 2021.wk14_a 2021.wk13_d 2021.wk13_c 2021.w51_c 2021.w51_a 2021.w50_a 2021.w49_b 2021.w49_a 2021.w48 2021.w47 2021.w46 2021.w46-powder 2021.w45 2021.w45_b 2021.w44 2021.w43 2021.w42 2021.w37 2021.w36 2021.w35 2021.w34 2021.w33 2021.w32 2021.w31 2021.w30 2021.w29 2021.w28 2021.w27 2021.w26 2021.w25 2021.w24 2021.w23 2021.w22 2021.w20 2021.w19 2021.w18_b 2021.w18_a 2021.w17_b 2021.w16 2021.w15 2021.w14 2021.w13_a 2021.w12 2021.w11 2021.w10 2021.w09 2021.w08 2021.w06 2021.w05 2021.w04 2021.w02 2020.w51_2 2020.w51 2020.w50 2020.w49 2020.w48_2 2020.w48 2020.w47 2020.w46_2 2020.w46 2020.w45_2 2020.w45 2020.w44 2020.w42_2 2020.w42 2020.w41 2020.w39 2020.w38 2020.w37 2020.w36 2020.w34 2020.w33 2020.w31 2020.w30 2020.w29 2020.w28 2020.w26 2020.w25 2020.w24 2020.w23 2020.w22 2020.w19 2020.w17 2020.w16 2020.w15 2020.w11 2020.w09 2020.w06 2020.w05 2020.w04 2020.w03 v2.2.0 v2.1.0 v2.0.0 setparam nr-ip-over-lte-v.1.5 nr-ip-over-lte-v.1.4 nr-ip-over-lte-v.1.3 nr-ip-over-lte-v.1.2 nr-ip-over-lte-v.1.1 nr-ip-over-lte-v.1.0 flexran-eol develop-nr-2020w03 develop-nr-2020w02 develop-nr-2019w51 develop-nr-2019w50 develop-nr-2019w48 develop-nr-2019w47 develop-nr-2019w45 develop-nr-2019w43 develop-nr-2019w42 develop-nr-2019w40 develop-nr-2019w28 benetel_phase_rotation benetel_gnb_rel_2.0 benetel_gnb_rel_1.0 benetel_enb_rel_2.0 benetel_enb_rel_1.0 ARC_1.3
No related merge requests found
...@@ -1285,6 +1285,7 @@ set(PHY_SRC_UE ...@@ -1285,6 +1285,7 @@ set(PHY_SRC_UE
${OPENAIR1_DIR}/PHY/NR_TRANSPORT/nr_ulsch.c ${OPENAIR1_DIR}/PHY/NR_TRANSPORT/nr_ulsch.c
${OPENAIR1_DIR}/PHY/NR_TRANSPORT/nr_tbs_tools.c ${OPENAIR1_DIR}/PHY/NR_TRANSPORT/nr_tbs_tools.c
${OPENAIR1_DIR}/PHY/NR_TRANSPORT/nr_sch_dmrs.c ${OPENAIR1_DIR}/PHY/NR_TRANSPORT/nr_sch_dmrs.c
${OPENAIR1_DIR}/PHY/NR_TRANSPORT/nr_ulsch_llr_computation.c
${OPENAIR1_DIR}/PHY/NR_REFSIG/nr_gold.c ${OPENAIR1_DIR}/PHY/NR_REFSIG/nr_gold.c
${OPENAIR1_DIR}/PHY/TOOLS/file_output.c ${OPENAIR1_DIR}/PHY/TOOLS/file_output.c
${OPENAIR1_DIR}/PHY/TOOLS/cadd_vv.c ${OPENAIR1_DIR}/PHY/TOOLS/cadd_vv.c
......
...@@ -80,8 +80,8 @@ int phy_init_nr_gNB(PHY_VARS_gNB *gNB, ...@@ -80,8 +80,8 @@ int phy_init_nr_gNB(PHY_VARS_gNB *gNB,
NR_DL_FRAME_PARMS *const fp = &gNB->frame_parms; NR_DL_FRAME_PARMS *const fp = &gNB->frame_parms;
nfapi_nr_config_request_t *cfg = &gNB->gNB_config; nfapi_nr_config_request_t *cfg = &gNB->gNB_config;
NR_gNB_COMMON *const common_vars = &gNB->common_vars; NR_gNB_COMMON *const common_vars = &gNB->common_vars;
/*LTE_eNB_PUSCH **const pusch_vars = gNB->pusch_vars; NR_gNB_PUSCH **const pusch_vars = gNB->pusch_vars;
LTE_eNB_SRS *const srs_vars = gNB->srs_vars; /*LTE_eNB_SRS *const srs_vars = gNB->srs_vars;
LTE_eNB_PRACH *const prach_vars = &gNB->prach_vars;*/ LTE_eNB_PRACH *const prach_vars = &gNB->prach_vars;*/
int i; int i;
...@@ -200,39 +200,38 @@ int phy_init_nr_gNB(PHY_VARS_gNB *gNB, ...@@ -200,39 +200,38 @@ int phy_init_nr_gNB(PHY_VARS_gNB *gNB,
for (i=0; i<64; i++) prach_vars->prach_ifft[0][i] = (int32_t *)malloc16_clear(1024*2*sizeof(int32_t)); for (i=0; i<64; i++) prach_vars->prach_ifft[0][i] = (int32_t *)malloc16_clear(1024*2*sizeof(int32_t));
prach_vars->rxsigF[0] = (int16_t **)malloc16_clear(64*sizeof(int16_t *)); prach_vars->rxsigF[0] = (int16_t **)malloc16_clear(64*sizeof(int16_t *));
*/
for (int ulsch_id=0; ulsch_id<NUMBER_OF_NR_ULSCH_MAX; ulsch_id++) { for (int UE_id=0; UE_id<NUMBER_OF_UE_MAX; UE_id++) {
//FIXME //FIXME
pusch_vars[UE_id] = (NR_gNB_PUSCH *)malloc16_clear( sizeof(NR_gNB_PUSCH) );
pusch_vars[UE_id] = (LTE_eNB_PUSCH *)malloc16_clear( NUMBER_OF_UE_MAX*sizeof(LTE_eNB_PUSCH) ); pusch_vars[UE_id]->rxdataF_ext = (int32_t **)malloc16( 2*sizeof(int32_t *) );
pusch_vars[UE_id]->rxdataF_ext = (int32_t **)malloc16( 2*sizeof(int32_t *) ); pusch_vars[UE_id]->rxdataF_ext2 = (int32_t **)malloc16( 2*sizeof(int32_t *) );
pusch_vars[UE_id]->rxdataF_ext2 = (int32_t **)malloc16( 2*sizeof(int32_t *) ); pusch_vars[UE_id]->drs_ch_estimates = (int32_t **)malloc16( 2*sizeof(int32_t *) );
pusch_vars[UE_id]->drs_ch_estimates = (int32_t **)malloc16( 2*sizeof(int32_t *) );
pusch_vars[UE_id]->drs_ch_estimates_time = (int32_t **)malloc16( 2*sizeof(int32_t *) ); pusch_vars[UE_id]->drs_ch_estimates_time = (int32_t **)malloc16( 2*sizeof(int32_t *) );
pusch_vars[UE_id]->rxdataF_comp = (int32_t **)malloc16( 2*sizeof(int32_t *) ); pusch_vars[UE_id]->rxdataF_comp = (int32_t **)malloc16( 2*sizeof(int32_t *) );
pusch_vars[UE_id]->ul_ch_mag = (int32_t **)malloc16( 2*sizeof(int32_t *) ); pusch_vars[UE_id]->ul_ch_mag = (int32_t **)malloc16( 2*sizeof(int32_t *) );
pusch_vars[UE_id]->ul_ch_magb = (int32_t **)malloc16( 2*sizeof(int32_t *) ); pusch_vars[UE_id]->ul_ch_magb = (int32_t **)malloc16( 2*sizeof(int32_t *) );
for (i=0; i<2; i++) { for (i=0; i<2; i++) {
// RK 2 times because of output format of FFT! // RK 2 times because of output format of FFT!
// FIXME We should get rid of this // FIXME We should get rid of this
pusch_vars[UE_id]->rxdataF_ext[i] = (int32_t *)malloc16_clear( sizeof(int32_t)*cfg->rf_config.ul_carrier_bandwidth.value*12*fp->symbols_per_slot ); pusch_vars[UE_id]->rxdataF_ext[i] = (int32_t *)malloc16_clear( sizeof(int32_t)*cfg->rf_config.ul_carrier_bandwidth.value*12*fp->symbols_per_slot );
pusch_vars[UE_id]->rxdataF_ext2[i] = (int32_t *)malloc16_clear( sizeof(int32_t)*cfg->rf_config.ul_carrier_bandwidth.value*12*fp->symbols_per_slot ); pusch_vars[UE_id]->rxdataF_ext2[i] = (int32_t *)malloc16_clear( sizeof(int32_t)*cfg->rf_config.ul_carrier_bandwidth.value*12*fp->symbols_per_slot );
pusch_vars[UE_id]->drs_ch_estimates[i] = (int32_t *)malloc16_clear( sizeof(int32_t)*cfg->rf_config.ul_carrier_bandwidth.value*12*fp->symbols_per_slot ); pusch_vars[UE_id]->drs_ch_estimates[i] = (int32_t *)malloc16_clear( sizeof(int32_t)*cfg->rf_config.ul_carrier_bandwidth.value*12*fp->symbols_per_slot );
pusch_vars[UE_id]->drs_ch_estimates_time[i] = (int32_t *)malloc16_clear( 2*sizeof(int32_t)*fp->ofdm_symbol_size ); pusch_vars[UE_id]->drs_ch_estimates_time[i] = (int32_t *)malloc16_clear( 2*sizeof(int32_t)*fp->ofdm_symbol_size );
pusch_vars[UE_id]->rxdataF_comp[i] = (int32_t *)malloc16_clear( sizeof(int32_t)*cfg->rf_config.ul_carrier_bandwidth.value*12*fp->symbols_per_slot ); pusch_vars[UE_id]->rxdataF_comp[i] = (int32_t *)malloc16_clear( sizeof(int32_t)*cfg->rf_config.ul_carrier_bandwidth.value*12*fp->symbols_per_slot );
pusch_vars[UE_id]->ul_ch_mag[i] = (int32_t *)malloc16_clear( fp->symbols_per_slot*sizeof(int32_t)*cfg->rf_config.ul_carrier_bandwidth.value*12 ); pusch_vars[UE_id]->ul_ch_mag[i] = (int32_t *)malloc16_clear( fp->symbols_per_slot*sizeof(int32_t)*cfg->rf_config.ul_carrier_bandwidth.value*12 );
pusch_vars[UE_id]->ul_ch_magb[i] = (int32_t *)malloc16_clear( fp->symbols_per_slot*sizeof(int32_t)*cfg->rf_config.ul_carrier_bandwidth.value*12 ); pusch_vars[UE_id]->ul_ch_magb[i] = (int32_t *)malloc16_clear( fp->symbols_per_slot*sizeof(int32_t)*cfg->rf_config.ul_carrier_bandwidth.value*12 );
} }
pusch_vars[UE_id]->llr = (int16_t *)malloc16_clear( (8*((3*8*6144)+12))*sizeof(int16_t) ); pusch_vars[UE_id]->llr = (int16_t *)malloc16_clear( (8*((3*8*6144)+12))*sizeof(int16_t) ); // [hna] 6144 is LTE and (8*((3*8*6144)+12)) is not clear
} //UE_id } //UE_id
/*
for (UE_id=0; UE_id<NUMBER_OF_UE_MAX; UE_id++) for (UE_id=0; UE_id<NUMBER_OF_UE_MAX; UE_id++)
gNB->UE_stats_ptr[UE_id] = &gNB->UE_stats[UE_id]; gNB->UE_stats_ptr[UE_id] = &gNB->UE_stats[UE_id];
gNB->pdsch_config_dedicated->p_a = dB0; //defaul value until overwritten by RRCConnectionReconfiguration
*/ */
gNB->pdsch_config_dedicated->p_a = dB0; //defaul value until overwritten by RRCConnectionReconfiguration
return (0); return (0);
} }
/* /*
...@@ -283,8 +282,8 @@ void phy_free_nr_gNB(PHY_VARS_gNB *gNB) { ...@@ -283,8 +282,8 @@ void phy_free_nr_gNB(PHY_VARS_gNB *gNB) {
//NR_DL_FRAME_PARMS* const fp = &gNB->frame_parms; //NR_DL_FRAME_PARMS* const fp = &gNB->frame_parms;
//nfapi_nr_config_request_t *cfg = &gNB->gNB_config; //nfapi_nr_config_request_t *cfg = &gNB->gNB_config;
NR_gNB_COMMON *const common_vars = &gNB->common_vars; NR_gNB_COMMON *const common_vars = &gNB->common_vars;
/*LTE_eNB_PUSCH **const pusch_vars = gNB->pusch_vars; NR_gNB_PUSCH **const pusch_vars = gNB->pusch_vars;
LTE_eNB_SRS *const srs_vars = gNB->srs_vars; /*LTE_eNB_SRS *const srs_vars = gNB->srs_vars;
LTE_eNB_PRACH *const prach_vars = &gNB->prach_vars;*/ LTE_eNB_PRACH *const prach_vars = &gNB->prach_vars;*/
uint32_t ***pdcch_dmrs = gNB->nr_gold_pdcch_dmrs; uint32_t ***pdcch_dmrs = gNB->nr_gold_pdcch_dmrs;
......
/*
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The OpenAirInterface Software Alliance licenses this file to You under
* the OAI Public License, Version 1.1 (the "License"); you may not use this file
* except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.openairinterface.org/?page_id=698
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*-------------------------------------------------------------------------------
* For more information about the OpenAirInterface (OAI) Software Alliance:
* contact@openairinterface.org
*/
/*! \file PHY/NR_TRANSPORT/nr_transport_proto.h.c
* \brief Function prototypes for PHY physical/transport channel processing and generation
* \author Ahmed Hussein
* \date 2019
* \version 0.1
* \company Fraunhofer IIS
* \email: ahmed.hussein@iis.fraunhofer.de
* \note
* \warning
*/
#include "PHY/defs_nr_common.h"
/** \brief This function generates log-likelihood ratios (decoder input) for single-stream QPSK received waveforms.
@param rxdataF_comp Compensated channel output
@param ulsch_llr llr output
@param nb_re number of REs for this allocation
@param symbol OFDM symbol index in sub-frame
*/
void nr_ulsch_qpsk_llr(int32_t *rxdataF_comp,
int16_t *ulsch_llr,
uint32_t nb_re,
uint8_t symbol);
/** \brief This function generates log-likelihood ratios (decoder input) for single-stream 16 QAM received waveforms.
@param rxdataF_comp Compensated channel output
@param ul_ch_mag uplink channel magnitude multiplied by the 1st amplitude threshold in QAM 16
@param ulsch_llr llr output
@param nb_re number of RBs for this allocation
@param symbol OFDM symbol index in sub-frame
*/
void nr_ulsch_16qam_llr(int32_t *rxdataF_comp,
int32_t **ul_ch_mag,
int16_t *ulsch_llr,
uint32_t nb_re,
uint8_t symbol);
/** \brief This function generates log-likelihood ratios (decoder input) for single-stream 64 QAM received waveforms.
@param rxdataF_comp Compensated channel output
@param ul_ch_mag uplink channel magnitude multiplied by the 1st amplitude threshold in QAM 64
@param ul_ch_magb uplink channel magnitude multiplied by the 2bd amplitude threshold in QAM 64
@param ulsch_llr llr output
@param nb_re number of REs for this allocation
@param symbol OFDM symbol index in sub-frame
*/
void nr_ulsch_64qam_llr(int32_t *rxdataF_comp,
int32_t **ul_ch_mag,
int32_t **ul_ch_magb,
int16_t *ulsch_llr,
uint32_t nb_re,
uint8_t symbol);
/** \brief This function computes the log-likelihood ratios for 4, 16, and 64 QAM
@param rxdataF_comp Compensated channel output
@param ul_ch_mag uplink channel magnitude multiplied by the 1st amplitude threshold in QAM 64
@param ul_ch_magb uplink channel magnitude multiplied by the 2bd amplitude threshold in QAM 64
@param ulsch_llr llr output
@param nb_re number of REs for this allocation
@param symbol OFDM symbol index in sub-frame
@param mod_order modulation order
*/
void nr_ulsch_compute_llr(int32_t *rxdataF_comp,
int32_t **ul_ch_mag,
int32_t **ul_ch_magb,
int16_t *ulsch_llr,
uint32_t nb_re,
uint8_t symbol,
uint8_t mod_order);
\ No newline at end of file
/*
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The OpenAirInterface Software Alliance licenses this file to You under
* the OAI Public License, Version 1.1 (the "License"); you may not use this file
* except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.openairinterface.org/?page_id=698
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*-------------------------------------------------------------------------------
* For more information about the OpenAirInterface (OAI) Software Alliance:
* contact@openairinterface.org
*/
/*! \file PHY/NR_TRANSPORT/nr_ulsch_llr_computation.c
* \brief Top-level routines for LLR computation of the PDSCH physical channel
* \author Ahmed Hussein
* \date 2019
* \version 0.1
* \company Fraunhofer IIS
* \email: ahmed.hussein@iis.fraunhofer.de
* \note
* \warning
*/
#include "PHY/defs_nr_common.h"
#include "PHY/sse_intrin.h"
#include "PHY/impl_defs_top.h"
__m128i xmm0 __attribute__ ((aligned(16)));
__m128i xmm1 __attribute__ ((aligned(16)));
__m128i xmm2 __attribute__ ((aligned(16)));
//----------------------------------------------------------------------------------------------
// QPSK
//----------------------------------------------------------------------------------------------
void nr_ulsch_qpsk_llr(int32_t *rxdataF_comp,
int16_t *ulsch_llr,
uint32_t nb_re,
uint8_t symbol)
{
int i;
uint32_t *rxF = (uint32_t*)rxdataF_comp;
uint32_t *llr32 = (uint32_t*)ulsch_llr;
if (!llr32) {
LOG_E(PHY,"nr_ulsch_qpsk_llr: llr is null, symbol %d, llr32 = %p\n",symbol, llr32);
}
for (i = 0; i < nb_re; i++) {
*llr32 = *rxF;
rxF++;
llr32++;
}
}
//----------------------------------------------------------------------------------------------
// 16-QAM
//----------------------------------------------------------------------------------------------
void nr_ulsch_16qam_llr(int32_t *rxdataF_comp,
int32_t **ul_ch_mag,
int16_t *ulsch_llr,
uint32_t nb_re,
uint8_t symbol)
{
#if defined(__x86_64__) || defined(__i386__)
__m128i *rxF = (__m128i*)rxdataF_comp;
// __m128i *ch_mag; // [hna] This should be uncommented once channel estimation is implemented
__m128i llr128[2];
uint32_t *llr32;
// [hna] temp_channel and one_over_sqrt_2 are for temporary use until channel estimation is implemented
// else ul_ch_mag and ul_ch_magb should be used after channel estimation has benn implemented
__m128i temp_channel;
int16_t one_over_sqrt_2 = 23170;
#elif defined(__arm__)
int16x8_t *rxF = (int16x8_t*)&rxdataF_comp;
// int16x8_t *ch_mag; // [hna] This should be uncommented once channel estimation is implemented
int16x8_t xmm0;
int16_t *llr16;
#endif
int i;
unsigned char len_mod4 = 0;
#if defined(__x86_64__) || defined(__i386__)
llr32 = (uint32_t*)ulsch_llr;
#elif defined(__arm__)
llr16 = (int16_t*)ulsch_llr;
#endif
// [hna] This should be uncommented once channel estimation is implemented
// ------------------------------------------------------------
// #if defined(__x86_64__) || defined(__i386__)
// ch_mag = (__m128i*)&ul_ch_mag[0][(symbol*nb_rb*12)];
// #elif defined(__arm__)
// ch_mag = (int16x8_t*)&ul_ch_mag[0][(symbol*nb_rb*12)];
// #endif
// ------------------------------------------------------------
len_mod4 = nb_re&3;
nb_re >>= 2; // length in quad words (4 REs)
nb_re += (len_mod4 == 0 ? 0 : 1);
temp_channel = _mm_set1_epi16((int16_t)((QAM64_n1 * one_over_sqrt_2)>>15));
for (i=0; i<nb_re; i++) {
#if defined(__x86_64__) || defined(__i386)
xmm0 = _mm_abs_epi16(rxF[i]); // registers of even index in xmm0-> |y_R|, registers of odd index in xmm0-> |y_I|
xmm0 = _mm_subs_epi16(temp_channel,xmm0); // registers of even index in xmm0-> |y_R|-|h|^2, registers of odd index in xmm0-> |y_I|-|h|^2
llr128[0] = _mm_unpacklo_epi32(rxF[i],xmm0); // llr128[0] contains the llrs of the 1st and 2nd REs
llr128[1] = _mm_unpackhi_epi32(rxF[i],xmm0); // llr128[1] contains the llrs of the 3rd and 4th REs
// 1st RE
llr32[0] = _mm_extract_epi32(llr128[0],0); // llr32[0] low 16 bits-> y_R , high 16 bits-> y_I
llr32[1] = _mm_extract_epi32(llr128[0],1); // llr32[1] low 16 bits-> |h|-|y_R|^2, high 16 bits-> |h|-|y_I|^2
// 2nd RE
llr32[2] = _mm_extract_epi32(llr128[0],2); // llr32[2] low 16 bits-> y_R , high 16 bits-> y_I
llr32[3] = _mm_extract_epi32(llr128[0],3); // llr32[3] low 16 bits-> |h|-|y_R|^2, high 16 bits-> |h|-|y_I|^2
// 3rd RE
llr32[4] = _mm_extract_epi32(llr128[1],0); // llr32[4] low 16 bits-> y_R , high 16 bits-> y_I
llr32[5] = _mm_extract_epi32(llr128[1],1); // llr32[5] low 16 bits-> |h|-|y_R|^2, high 16 bits-> |h|-|y_I|^2
// 4th RE
llr32[6] = _mm_extract_epi32(llr128[1],2); // llr32[6] low 16 bits-> y_R , high 16 bits-> y_I
llr32[7] = _mm_extract_epi32(llr128[1],3); // llr32[7] low 16 bits-> |h|-|y_R|^2, high 16 bits-> |h|-|y_I|^2
llr32+=8;
#elif defined(__arm__)
xmm0 = vabsq_s16(rxF[i]);
xmm0 = vqsubq_s16((*(__m128i*)&ones[0]),xmm0);
llr16[0] = vgetq_lane_s16(rxF[i],0);
llr16[1] = vgetq_lane_s16(rxF[i],1);
llr16[2] = vgetq_lane_s16(xmm0,0);
llr16[3] = vgetq_lane_s16(xmm0,1);
llr16[4] = vgetq_lane_s16(rxF[i],2);
llr16[5] = vgetq_lane_s16(rxF[i],3);
llr16[6] = vgetq_lane_s16(xmm0,2);
llr16[7] = vgetq_lane_s16(xmm0,3);
llr16[8] = vgetq_lane_s16(rxF[i],4);
llr16[9] = vgetq_lane_s16(rxF[i],5);
llr16[10] = vgetq_lane_s16(xmm0,4);
llr16[11] = vgetq_lane_s16(xmm0,5);
llr16[12] = vgetq_lane_s16(rxF[i],6);
llr16[13] = vgetq_lane_s16(rxF[i],6);
llr16[14] = vgetq_lane_s16(xmm0,7);
llr16[15] = vgetq_lane_s16(xmm0,7);
llr16+=16;
#endif
}
#if defined(__x86_64__) || defined(__i386__)
_mm_empty();
_m_empty();
#endif
}
//----------------------------------------------------------------------------------------------
// 64-QAM
//----------------------------------------------------------------------------------------------
void nr_ulsch_64qam_llr(int32_t *rxdataF_comp,
int32_t **ul_ch_mag,
int32_t **ul_ch_magb,
int16_t *ulsch_llr,
uint32_t nb_re,
uint8_t symbol)
{
#if defined(__x86_64__) || defined(__i386__)
__m128i *rxF = (__m128i*)rxdataF_comp;
// __m128i *ch_mag,*ch_magb; // [hna] This should be uncommented once channel estimation is implemented
// [hna] temp_channel and one_over_sqrt_2 are for temporary use until channel estimation is implemented
// else ul_ch_mag and ul_ch_magb should be used after channel estimation has been implemented
__m128i temp_channel[2];
int16_t one_over_sqrt_2 = 23170;
#elif defined(__arm__)
int16x8_t *rxF = (int16x8_t*)&rxdataF_comp;
// int16x8_t *ch_mag,*ch_magb; // [hna] This should be uncommented once channel estimation is implemented
int16x8_t xmm1,xmm2;
#endif
int i;
unsigned char len_mod4;
// [hna] This should be uncommented once channel estimation is implemented
// -------------------------------------------------------------------------
// #if defined(__x86_64__) || defined(__i386__)
// ch_mag = (__m128i*)&ul_ch_mag[0][(symbol*frame_parms->N_RB_UL*12)];
// ch_magb = (__m128i*)&ul_ch_magb[0][(symbol*frame_parms->N_RB_UL*12)];
// #elif defined(__arm__)
// ch_mag = (int16x8_t*)&ul_ch_mag[0][(symbol*frame_parms->N_RB_UL*12)];
// ch_magb = (int16x8_t*)&ul_ch_magb[0][(symbol*frame_parms->N_RB_UL*12)];
// #endif
// -------------------------------------------------------------------------
len_mod4 =nb_re&3;
nb_re=nb_re>>2; // length in quad words (4 REs)
nb_re+=((len_mod4==0)?0:1);
temp_channel[0] = _mm_set1_epi16((int16_t)((QAM64_n1 * one_over_sqrt_2)>>15));
temp_channel[1] = _mm_set1_epi16((int16_t)((QAM64_n2 * one_over_sqrt_2)>>15));
for (i=0; i<nb_re; i++) {
#if defined(__x86_64__) || defined(__i386__)
xmm1 = _mm_abs_epi16(rxF[i]);
xmm1 = _mm_subs_epi16(temp_channel[0],xmm1);
xmm2 = _mm_abs_epi16(xmm1);
xmm2 = _mm_subs_epi16(temp_channel[1],xmm2);
#elif defined(__arm__)
xmm1 = vabsq_s16(rxF[i]);
xmm1 = vsubq_s16(ch_mag[i],xmm1);
xmm2 = vabsq_s16(xmm1);
xmm2 = vsubq_s16(ch_magb[i],xmm2);
#endif
// ---------------------------------------s
// 1st RE
// ---------------------------------------
ulsch_llr[0] = ((short *)&rxF[i])[0];
ulsch_llr[1] = ((short *)&rxF[i])[1];
#if defined(__x86_64__) || defined(__i386__)
ulsch_llr[2] = _mm_extract_epi16(xmm1,0);
ulsch_llr[3] = _mm_extract_epi16(xmm1,1);
ulsch_llr[4] = _mm_extract_epi16(xmm2,0);
ulsch_llr[5] = _mm_extract_epi16(xmm2,1);
#elif defined(__arm__)
ulsch_llr[2] = vgetq_lane_s16(xmm1,0);
ulsch_llr[3] = vgetq_lane_s16(xmm1,1);
ulsch_llr[4] = vgetq_lane_s16(xmm2,0);
ulsch_llr[5] = vgetq_lane_s16(xmm2,1);
#endif
// ---------------------------------------
ulsch_llr+=6;
// ---------------------------------------
// 2nd RE
// ---------------------------------------
ulsch_llr[0] = ((short *)&rxF[i])[2];
ulsch_llr[1] = ((short *)&rxF[i])[3];
#if defined(__x86_64__) || defined(__i386__)
ulsch_llr[2] = _mm_extract_epi16(xmm1,2);
ulsch_llr[3] = _mm_extract_epi16(xmm1,3);
ulsch_llr[4] = _mm_extract_epi16(xmm2,2);
ulsch_llr[5] = _mm_extract_epi16(xmm2,3);
#elif defined(__arm__)
ulsch_llr[2] = vgetq_lane_s16(xmm1,2);
ulsch_llr[3] = vgetq_lane_s16(xmm1,3);
ulsch_llr[4] = vgetq_lane_s16(xmm2,2);
ulsch_llr[5] = vgetq_lane_s16(xmm2,3);
#endif
// ---------------------------------------
ulsch_llr+=6;
// ---------------------------------------
// 3rd RE
// ---------------------------------------
ulsch_llr[0] = ((short *)&rxF[i])[4];
ulsch_llr[1] = ((short *)&rxF[i])[5];
#if defined(__x86_64__) || defined(__i386__)
ulsch_llr[2] = _mm_extract_epi16(xmm1,4);
ulsch_llr[3] = _mm_extract_epi16(xmm1,5);
ulsch_llr[4] = _mm_extract_epi16(xmm2,4);
ulsch_llr[5] = _mm_extract_epi16(xmm2,5);
#elif defined(__arm__)
ulsch_llr[2] = vgetq_lane_s16(xmm1,4);
ulsch_llr[3] = vgetq_lane_s16(xmm1,5);
ulsch_llr[4] = vgetq_lane_s16(xmm2,4);
ulsch_llr[5] = vgetq_lane_s16(xmm2,5);
#endif
// ---------------------------------------
ulsch_llr+=6;
// ---------------------------------------
// 4th RE
// ---------------------------------------
ulsch_llr[0] = ((short *)&rxF[i])[6];
ulsch_llr[1] = ((short *)&rxF[i])[7];
#if defined(__x86_64__) || defined(__i386__)
ulsch_llr[2] = _mm_extract_epi16(xmm1,6);
ulsch_llr[3] = _mm_extract_epi16(xmm1,7);
ulsch_llr[4] = _mm_extract_epi16(xmm2,6);
ulsch_llr[5] = _mm_extract_epi16(xmm2,7);
#elif defined(__arm__)
ulsch_llr[2] = vgetq_lane_s16(xmm1,6);
ulsch_llr[3] = vgetq_lane_s16(xmm1,7);
ulsch_llr[4] = vgetq_lane_s16(xmm2,6);
ulsch_llr[5] = vgetq_lane_s16(xmm2,7);
#endif
// ---------------------------------------
ulsch_llr+=6;
}
#if defined(__x86_64__) || defined(__i386__)
_mm_empty();
_m_empty();
#endif
}
void nr_ulsch_compute_llr(int32_t *rxdataF_comp,
int32_t **ul_ch_mag,
int32_t **ul_ch_magb,
int16_t *ulsch_llr,
uint32_t nb_re,
uint8_t symbol,
uint8_t mod_order)
{
switch(mod_order){
case 2:
nr_ulsch_qpsk_llr(rxdataF_comp,
ulsch_llr,
nb_re,
symbol);
break;
case 4:
nr_ulsch_16qam_llr(rxdataF_comp,
ul_ch_mag,
ulsch_llr,
nb_re,
symbol);
break;
case 6:
nr_ulsch_64qam_llr(rxdataF_comp,
ul_ch_mag,
ul_ch_magb,
ulsch_llr,
nb_re,
symbol);
break;
default:
LOG_E(PHY,"nr_ulsch_compute_llr: invalid Qm value, symbol = %d, Qm = %d\n",symbol, mod_order);
break;
}
}
\ No newline at end of file
...@@ -204,7 +204,7 @@ typedef struct { ...@@ -204,7 +204,7 @@ typedef struct {
/// Scrambled "b"-sequences (for definition see 36-211 V8.6 2009-03, p.14) /// Scrambled "b"-sequences (for definition see 36-211 V8.6 2009-03, p.14)
uint8_t b_tilde[MAX_NUM_NR_CHANNEL_BITS]; uint8_t b_tilde[MAX_NUM_NR_CHANNEL_BITS];
/// Modulated "d"-sequences (for definition see 36-211 V8.6 2009-03, p.14) /// Modulated "d"-sequences (for definition see 36-211 V8.6 2009-03, p.14)
int32_t d_mod[MAX_NUM_NR_RE]; int32_t d_mod[MAX_NUM_NR_RE] __attribute__ ((aligned(16)));
/// Transform-coded "z"-sequences (for definition see 36-211 V8.6 2009-03, p.14-15) /// Transform-coded "z"-sequences (for definition see 36-211 V8.6 2009-03, p.14-15)
int32_t z[MAX_NUM_NR_RE]; int32_t z[MAX_NUM_NR_RE];
/* /*
......
...@@ -354,6 +354,43 @@ typedef struct { ...@@ -354,6 +354,43 @@ typedef struct {
} NR_gNB_COMMON; } NR_gNB_COMMON;
typedef struct {
/// \brief Holds the received data in the frequency domain for the allocated RBs in repeated format.
/// - first index: rx antenna id [0..nb_antennas_rx[
/// - second index: ? [0..2*ofdm_symbol_size[
int32_t **rxdataF_ext;
/// \brief Holds the received data in the frequency domain for the allocated RBs in normal format.
/// - first index: rx antenna id [0..nb_antennas_rx[
/// - second index (definition from phy_init_lte_eNB()): ? [0..12*N_RB_UL*frame_parms->symbols_per_tti[
int32_t **rxdataF_ext2;
/// \brief Hold the channel estimates in time domain based on DRS.
/// - first index: rx antenna id [0..nb_antennas_rx[
/// - second index: ? [0..4*ofdm_symbol_size[
int32_t **drs_ch_estimates_time;
/// \brief Hold the channel estimates in frequency domain based on DRS.
/// - first index: rx antenna id [0..nb_antennas_rx[
/// - second index: ? [0..12*N_RB_UL*frame_parms->symbols_per_tti[
int32_t **drs_ch_estimates;
/// \brief Holds the compensated signal.
/// - first index: rx antenna id [0..nb_antennas_rx[
/// - second index: ? [0..12*N_RB_UL*frame_parms->symbols_per_tti[
int32_t **rxdataF_comp;
/// \brief Magnitude of the UL channel estimates. Used for 2nd-bit level thresholds in LLR computation
/// - first index: rx antenna id [0..nb_antennas_rx[
/// - second index: ? [0..12*N_RB_UL*frame_parms->symbols_per_tti[
int32_t **ul_ch_mag;
/// \brief Magnitude of the UL channel estimates scaled for 3rd bit level thresholds in LLR computation
/// - first index: rx antenna id [0..nb_antennas_rx[
/// - second index: ? [0..12*N_RB_UL*frame_parms->symbols_per_tti[
int32_t **ul_ch_magb;
/// measured RX power based on DRS
int ulsch_power[2];
/// \brief llr values.
/// - first index: ? [0..1179743] (hard coded)
int16_t *llr;
} NR_gNB_PUSCH;
/// Context data structure for RX/TX portion of slot processing /// Context data structure for RX/TX portion of slot processing
typedef struct { typedef struct {
/// Component Carrier index /// Component Carrier index
...@@ -569,13 +606,13 @@ typedef struct PHY_VARS_gNB_s { ...@@ -569,13 +606,13 @@ typedef struct PHY_VARS_gNB_s {
Sched_Rsp_t Sched_INFO; Sched_Rsp_t Sched_INFO;
NR_gNB_PDCCH pdcch_vars; NR_gNB_PDCCH pdcch_vars;
NR_gNB_PBCH pbch; NR_gNB_PBCH pbch;
LTE_eNB_PHICH phich_vars[2]; // LTE_eNB_PHICH phich_vars[2];
NR_gNB_COMMON common_vars; NR_gNB_COMMON common_vars;
/* LTE_eNB_UCI uci_vars[NUMBER_OF_UE_MAX]; /* LTE_eNB_UCI uci_vars[NUMBER_OF_UE_MAX];
LTE_eNB_SRS srs_vars[NUMBER_OF_UE_MAX]; LTE_eNB_SRS srs_vars[NUMBER_OF_UE_MAX];
LTE_eNB_PUSCH *pusch_vars[NUMBER_OF_UE_MAX];
LTE_eNB_PRACH prach_vars;*/ LTE_eNB_PRACH prach_vars;*/
NR_gNB_PUSCH *pusch_vars[NUMBER_OF_UE_MAX];
NR_gNB_DLSCH_t *dlsch[NUMBER_OF_NR_DLSCH_MAX][2]; // Nusers times two spatial streams NR_gNB_DLSCH_t *dlsch[NUMBER_OF_NR_DLSCH_MAX][2]; // Nusers times two spatial streams
NR_gNB_ULSCH_t *ulsch[NUMBER_OF_NR_ULSCH_MAX+1][2]; // [Nusers times + number of RA][2 codewords], index 0 in [NUMBER_OF_UE_MAX+1] is for RA NR_gNB_ULSCH_t *ulsch[NUMBER_OF_NR_ULSCH_MAX+1][2]; // [Nusers times + number of RA][2 codewords], index 0 in [NUMBER_OF_UE_MAX+1] is for RA
// LTE_eNB_ULSCH_t *ulsch[NUMBER_OF_UE_MAX+1]; // Nusers + number of RA // LTE_eNB_ULSCH_t *ulsch[NUMBER_OF_UE_MAX+1]; // Nusers + number of RA
......
...@@ -48,16 +48,14 @@ ...@@ -48,16 +48,14 @@
#include "PHY/NR_TRANSPORT/nr_ulsch.h" #include "PHY/NR_TRANSPORT/nr_ulsch.h"
#include "PHY/NR_TRANSPORT/nr_sch_dmrs.h" #include "PHY/NR_TRANSPORT/nr_sch_dmrs.h"
#include "PHY/NR_UE_TRANSPORT/nr_transport_proto_ue.h" #include "PHY/NR_UE_TRANSPORT/nr_transport_proto_ue.h"
#include "PHY/NR_TRANSPORT/nr_transport_proto.h"
#include "SCHED_NR/sched_nr.h" #include "SCHED_NR/sched_nr.h"
//#include "PHY/MODULATION/modulation_common.h" //#include "PHY/MODULATION/modulation_common.h"
//#include "common/config/config_load_configmodule.h" //#include "common/config/config_load_configmodule.h"
//#include "UTIL/LISTS/list.h" //#include "UTIL/LISTS/list.h"
//#include "common/ran_context.h" //#include "common/ran_context.h"
//#define DEBUG_ULSCHSIM //#define DEBUG_ULSCHSIM
PHY_VARS_gNB *gNB; PHY_VARS_gNB *gNB;
PHY_VARS_NR_UE *UE; PHY_VARS_NR_UE *UE;
RAN_CONTEXT_t RC; RAN_CONTEXT_t RC;
...@@ -487,6 +485,7 @@ int main(int argc, char **argv) { ...@@ -487,6 +485,7 @@ int main(int argc, char **argv) {
uint8_t bit_index; uint8_t bit_index;
uint32_t errors_scrambling; uint32_t errors_scrambling;
uint32_t scrambling_index; uint32_t scrambling_index;
uint8_t symbol;
int16_t **tx_layers; int16_t **tx_layers;
int32_t *mod_symbols[MAX_NUM_NR_RE]; int32_t *mod_symbols[MAX_NUM_NR_RE];
uint16_t n_dmrs; uint16_t n_dmrs;
...@@ -573,7 +572,7 @@ int main(int argc, char **argv) { ...@@ -573,7 +572,7 @@ int main(int argc, char **argv) {
nr_modulation(scrambled_output[cwd], // assume one codeword for the moment nr_modulation(scrambled_output[cwd], // assume one codeword for the moment
available_bits, available_bits,
mod_order, mod_order,
ulsch_ue[cwd]->d_mod); (int16_t *)ulsch_ue[cwd]->d_mod);
/////////// ///////////
//////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////
...@@ -718,12 +717,12 @@ m, l, k, ((int16_t*)txdataF[ap])[(sample_offsetF)<<1], ...@@ -718,12 +717,12 @@ m, l, k, ((int16_t*)txdataF[ap])[(sample_offsetF)<<1],
n_errors = 0; n_errors = 0;
n_false_positive = 0; n_false_positive = 0;
SNR_lin = pow(10, SNR / 10.0); SNR_lin = pow(10, SNR / 10.0);
sigma = 1.0 / sqrt(2 * SNR_lin); sigma = 1.0 / sqrt(2 * SNR_lin);
//AWGN //AWGN
sigma2_dB = 10*log10((double)txlev)-SNR; sigma2_dB = 10*log10((double)txlev)-SNR;
sigma2 = pow(10,sigma2_dB/10); sigma2 = pow(10,sigma2_dB/10);
for (trial = 0; trial < n_trials; trial++) { for (trial = 0; trial < n_trials; trial++) {
...@@ -795,37 +794,70 @@ m, l, k, ((int16_t*)txdataF[ap])[(sample_offsetF)<<1], ...@@ -795,37 +794,70 @@ m, l, k, ((int16_t*)txdataF[ap])[(sample_offsetF)<<1],
#endif #endif
//////////////////////////////////////////////////////////// //----------------------------------------------------------
//////////////////// ULSCH unscrambling //////////////////// //-------------------- LLRs computation --------------------
//////////////////////////////////////////////////////////// //----------------------------------------------------------
nr_ulsch_unscrambling(channel_output_fixed, available_bits, 0, Nid_cell, n_rnti); int sch_sym_start = NR_SYMBOLS_PER_SLOT-nb_symb_sch;
uint32_t nb_re;
uint32_t d_mod_offset = 0;
uint32_t llr_offset = 0;
for(symbol = sch_sym_start; symbol < 14; symbol++) {
if (symbol == 2) // [hna] here it is assumed that symbol 2 carries 6 DMRS REs (dmrs-type 1)
nb_re = nb_rb*6;
else
nb_re = nb_rb*12;
nr_ulsch_compute_llr(&ulsch_ue[0]->d_mod[d_mod_offset],
gNB->pusch_vars[UE_id]->ul_ch_mag,
gNB->pusch_vars[UE_id]->ul_ch_magb,
&gNB->pusch_vars[UE_id]->llr[llr_offset],
nb_re,
symbol,
rel15_ul->Qm);
d_mod_offset = d_mod_offset + nb_re; // [hna] d_mod is incremented by nb_re
llr_offset = llr_offset + (nb_re * rel15_ul->Qm); // [hna] llr is incremented by (nb_re*mod_order) because each RE has (mod_order) coded bit (i.e LLRs)
}
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////
//----------------------------------------------------------
//------------------- ULSCH unscrambling -------------------
//----------------------------------------------------------
nr_ulsch_unscrambling(gNB->pusch_vars[UE_id]->llr, available_bits, 0, Nid_cell, n_rnti);
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
////////////////////// ULSCH decoding //////////////////////
////////////////////////////////////////////////////////////
//----------------------------------------------------------
//--------------------- ULSCH decoding ---------------------
//----------------------------------------------------------
ret = nr_ulsch_decoding(gNB, UE_id, channel_output_fixed, frame_parms, frame, ret = nr_ulsch_decoding(gNB, UE_id, gNB->pusch_vars[UE_id]->llr, frame_parms, frame,
nb_symb_sch, subframe, harq_pid, is_crnti, llr8_flag); nb_symb_sch, subframe, harq_pid, is_crnti, llr8_flag);
if (ret > ulsch_gNB->max_ldpc_iterations) if (ret > ulsch_gNB->max_ldpc_iterations)
n_errors++; n_errors++;
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////// //----------------------------------------------------------
/////////////////////// count errors /////////////////////// //---------------------- count errors ----------------------
//////////////////////////////////////////////////////////// //----------------------------------------------------------
for (i = 0; i < TBS; i++) { for (i = 0; i < TBS; i++) {
if(((ulsch_ue[0]->g[i] == 0) && (channel_output_fixed[i] < 0)) || ((ulsch_ue[0]->g[i] == 1) && (channel_output_fixed[i] >= 0))) { if(((ulsch_ue[0]->g[i] == 0) && (gNB->pusch_vars[UE_id]->llr[i] <= 0)) ||
((ulsch_ue[0]->g[i] == 1) && (gNB->pusch_vars[UE_id]->llr[i] >= 0)))
{
if(errors_scrambling == 0)
printf("First bit in error = %d\n",i);
errors_scrambling++; errors_scrambling++;
} }
...@@ -838,15 +870,19 @@ m, l, k, ((int16_t*)txdataF[ap])[(sample_offsetF)<<1], ...@@ -838,15 +870,19 @@ m, l, k, ((int16_t*)txdataF[ap])[(sample_offsetF)<<1],
} }
////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
if (errors_scrambling > 0) {
if (n_trials == 1)
printf("errors_scrambling %d (trial %d)\n", errors_scrambling, trial);
}
if (errors_bit > 0) { if (errors_bit > 0) {
n_false_positive++; n_false_positive++;
if (n_trials == 1) if (n_trials == 1)
printf("errors_bit %d (trial %d)\n", errors_bit, trial); printf("errors_bit %d (trial %d)\n", errors_bit, trial);
} }
} } // [hna] for (trial = 0; trial < n_trials; trial++)
printf("*****************************************\n"); printf("*****************************************\n");
printf("SNR %f, BLER %f (false positive %f)\n", SNR, printf("SNR %f, BLER %f (false positive %f)\n", SNR,
...@@ -858,7 +894,8 @@ m, l, k, ((int16_t*)txdataF[ap])[(sample_offsetF)<<1], ...@@ -858,7 +894,8 @@ m, l, k, ((int16_t*)txdataF[ap])[(sample_offsetF)<<1],
printf("PUSCH test OK\n"); printf("PUSCH test OK\n");
break; break;
} }
} } // [hna] for (SNR = snr0; SNR < snr1; SNR += snr_step)
for (i = 0; i < 2; i++) { for (i = 0; i < 2; i++) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment