Commit e5fd0fcd authored by Ahmed's avatar Ahmed Committed by Thomas Schlichter

added the ulsch decoding functions to ulschsim.c

now ulschsim.c is uplink only with no downlink decoding functions or variables
parent 9947a6bc
......@@ -33,6 +33,7 @@
#include "assertions.h"
#include <math.h>
#include "PHY/NR_TRANSPORT/nr_ulsch.h"
#include "PHY/NR_REFSIG/nr_refsig.h"
#include "PHY/LTE_REFSIG/lte_refsig.h"
#include "SCHED_NR/fapi_nr_l1.h"
......@@ -82,7 +83,9 @@ int phy_init_nr_gNB(PHY_VARS_gNB *gNB,
LTE_eNB_PUSCH **const pusch_vars = gNB->pusch_vars;
LTE_eNB_SRS *const srs_vars = gNB->srs_vars;
LTE_eNB_PRACH *const prach_vars = &gNB->prach_vars;
int i, UE_id;
LOG_I(PHY,"[gNB %d] %s() About to wait for gNB to be configured\n", gNB->Mod_id, __FUNCTION__);
gNB->total_dlsch_bitrate = 0;
gNB->total_transmitted_bits = 0;
......@@ -110,7 +113,6 @@ int phy_init_nr_gNB(PHY_VARS_gNB *gNB,
init_dfts();
// PBCH DMRS gold sequences generation
nr_init_pbch_dmrs(gNB);
// Polar encoder init for PBCH
//PDCCH DMRS init
gNB->nr_gold_pdcch_dmrs = (uint32_t ***)malloc16(fp->slots_per_frame*sizeof(uint32_t **));
uint32_t ***pdcch_dmrs = gNB->nr_gold_pdcch_dmrs;
......@@ -148,6 +150,7 @@ int phy_init_nr_gNB(PHY_VARS_gNB *gNB,
}
nr_init_pdsch_dmrs(gNB, cfg->sch_config.physical_cell_id.value);
/// Transport init necessary for NR synchro
init_nr_transport(gNB);
......@@ -167,11 +170,12 @@ int phy_init_nr_gNB(PHY_VARS_gNB *gNB,
gNB->first_run_I0_measurements =
1; ///This flag used to be static. With multiple gNBs this does no longer work, hence we put it in the structure. However it has to be initialized with 1, which is performed here.
common_vars->rxdata = (int32_t **)NULL;
common_vars->txdataF = (int32_t **)malloc16(15*sizeof(int32_t *));
common_vars->rxdataF = (int32_t **)malloc16(64*sizeof(int32_t *));
common_vars->txdataF = (int32_t **)malloc16(15*sizeof(int32_t*));
common_vars->rxdataF = (int32_t **)malloc16(64*sizeof(int32_t*));
for (i=0;i<15;i++){
common_vars->txdataF[i] = (int32_t*)malloc16_clear(fp->samples_per_frame_wCP*sizeof(int32_t) ); // [hna] samples_per_frame without CP
for (i=0; i<15; i++) {
common_vars->txdataF[i] = (int32_t *)malloc16_clear(fp->samples_per_frame_wCP*sizeof(int32_t) );
LOG_D(PHY,"[INIT] common_vars->txdataF[%d] = %p (%lu bytes)\n",
i,common_vars->txdataF[i],
fp->samples_per_frame_wCP*sizeof(int32_t));
......@@ -417,6 +421,7 @@ void nr_phy_config_request(NR_PHY_Config_t *phy_config) {
gNB_config->rf_config.ul_carrier_bandwidth.value,
gNB_config->sch_config.physical_cell_id.value,
fp->dl_CarrierFreq );
nr_init_frame_parms(gNB_config, fp);
if (RC.gNB[Mod_id][CC_id]->configured == 1) {
......@@ -436,6 +441,7 @@ void init_nr_transport(PHY_VARS_gNB *gNB) {
LOG_I(PHY, "Initialise nr transport\n");
for (i=0; i<NUMBER_OF_UE_MAX; i++) {
LOG_I(PHY,"Allocating Transport Channel Buffers for DLSCH, UE %d\n",i);
for (j=0; j<2; j++) {
......@@ -445,29 +451,38 @@ void init_nr_transport(PHY_VARS_gNB *gNB) {
LOG_E(PHY,"Can't get gNB dlsch structures for UE %d \n", i);
exit(-1);
}/* else {
gNB->dlsch[i][j]->rnti=0;
LOG_D(PHY,"dlsch[%d][%d] => %p rnti:%d\n",i,j,gNB->dlsch[i][j], gNB->dlsch[i][j]->rnti);
}*/
///////////////////////// Initializing gNB ULSCH /////////////////////////
LOG_I(PHY,"Allocating Transport Channel Buffer for ULSCH, UE %d\n",i);
// ULSCH for RA
if(i==0) {
gNB->ulsch[i][j] = new_gNB_ulsch(5, fp->N_RB_UL, 0);
if (!gNB->ulsch[i][j]) {
LOG_E(PHY,"Can't get gNB ulsch structures\n");
exit(-1);
}
}
//LOG_I(PHY,"Allocating Transport Channel Buffer for ULSCH, UE %d\n",i);
//gNB->ulsch[1+i] = new_gNB_ulsch(MAX_TURBO_ITERATIONS,fp->N_RB_UL, 0);
/*if (!gNB->ulsch[1+i]) {
// ULSCH for data
gNB->ulsch[i+1][j] = new_gNB_ulsch(5, fp->N_RB_UL, 0);
if (!gNB->ulsch[i+1][j]) {
LOG_E(PHY,"Can't get gNB ulsch structures\n");
exit(-1);
}*/
}
//////////////////////////////////////////////////////////////////////////
}
// this is the transmission mode for the signalling channels
// this will be overwritten with the real transmission mode by the RRC once the UE is connected
//gNB->transmission_mode[i] = fp->nb_antenna_ports_gNB==1 ? 1 : 2;
}
// ULSCH for RA
//gNB->ulsch[0] = new_gNB_ulsch(MAX_TURBO_ITERATIONS, fp->N_RB_UL, 0);
/*if (!gNB->ulsch[0]) {
LOG_E(PHY,"Can't get gNB ulsch structures\n");
exit(-1);
}*/
gNB->dlsch_SI = new_gNB_dlsch(1,8,NSOFT, 0, fp, cfg);
LOG_D(PHY,"gNB %d.%d : SI %p\n",gNB->Mod_id,gNB->CC_id,gNB->dlsch_SI);
gNB->dlsch_ra = new_gNB_dlsch(1,8,NSOFT, 0, fp, cfg);
......
/*
* 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
*/
* 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
*/
#include <string.h>
#include <math.h>
......@@ -32,6 +32,7 @@
#include "SIMULATION/TOOLS/sim.h"
#include "SIMULATION/RF/rf.h"
#include "PHY/types.h"
#include "PHY/defs_nr_common.h"
#include "PHY/defs_nr_UE.h"
......@@ -43,6 +44,7 @@
#include "PHY/MODULATION/modulation_UE.h"
#include "PHY/NR_TRANSPORT/nr_transport.h"
#include "PHY/NR_TRANSPORT/nr_dlsch.h"
#include "PHY/NR_TRANSPORT/nr_ulsch.h"
#include "PHY/NR_UE_TRANSPORT/nr_transport_proto_ue.h"
#include "SCHED_NR/sched_nr.h"
......@@ -93,8 +95,7 @@ NR_IF_Module_init(int Mod_id) {
return (NULL);
}
void exit_function(const char *file, const char *function, const int line,
const char *s) {
void exit_function(const char *file, const char *function, const int line, const char *s) {
const char *msg = s == NULL ? "no comment" : s;
printf("Exiting at: %s:%d %s(), %s\n", file, line, function, msg);
exit(-1);
......@@ -121,6 +122,7 @@ char quantize(double D, double x, unsigned char B) {
}
int main(int argc, char **argv) {
char c;
int i,sf; //,j,l,aa;
double SNR, SNR_lin, snr0 = -2.0, snr1 = 2.0;
......@@ -144,7 +146,7 @@ int main(int argc, char **argv) {
//char input_val_str[50],input_val_str2[50];
//uint16_t NB_RB=25;
SCM_t channel_model = AWGN; //Rayleigh1_anticorr;
uint16_t N_RB_DL = 106, N_RB_UL = 106, mu = 1;
uint8_t N_RB_DL = 106, N_RB_UL = 106, mu = 1;
unsigned char frame_type = 0;
unsigned char pbch_phase = 0;
int frame = 0, subframe = 0;
......@@ -152,7 +154,7 @@ int main(int argc, char **argv) {
//int frame_length_complex_samples_no_prefix;
NR_DL_FRAME_PARMS *frame_parms;
//nfapi_nr_config_request_t *gNB_config;
uint8_t Kmimo = 0;
// uint8_t Kmimo = 0;
uint32_t Nsoft = 0;
double sigma;
unsigned char qbits = 8;
......@@ -363,8 +365,7 @@ int main(int argc, char **argv) {
if (snr1set == 0)
snr1 = snr0 + 10;
gNB2UE = new_channel_desc_scm(n_tx, n_rx, channel_model,
61.44e6, //N_RB2sampling_rate(N_RB_DL),
gNB2UE = new_channel_desc_scm(n_tx, n_rx, channel_model, 61.44e6, //N_RB2sampling_rate(N_RB_DL),
40e6, //N_RB2channel_bandwidth(N_RB_DL),
0, 0, 0);
......@@ -378,23 +379,28 @@ int main(int argc, char **argv) {
RC.gNB[0][0] = malloc(sizeof(PHY_VARS_gNB));
gNB = RC.gNB[0][0];
//gNB_config = &gNB->gNB_config;
frame_parms = &gNB->frame_parms; //to be initialized I suppose (maybe not necessary for PBCH)
frame_parms->nb_antennas_tx = n_tx;
frame_parms->nb_antennas_rx = n_rx;
frame_parms->N_RB_DL = N_RB_DL;
frame_parms->N_RB_UL = N_RB_UL;
frame_parms->Ncp = extended_prefix_flag ? EXTENDED : NORMAL;
crcTableInit();
nr_phy_config_request_sim(gNB, N_RB_DL, N_RB_DL, mu, Nid_cell);
phy_init_nr_gNB(gNB, 0, 0);
//init_eNB_afterRU();
frame_length_complex_samples = frame_parms->samples_per_subframe;
//frame_length_complex_samples_no_prefix = frame_parms->samples_per_subframe_wCP;
s_re = malloc(2 * sizeof(double *));
s_im = malloc(2 * sizeof(double *));
r_re = malloc(2 * sizeof(double *));
r_im = malloc(2 * sizeof(double *));
txdata = malloc(2 * sizeof(int *));
txdata = malloc(2 * sizeof(int * ));
for (i = 0; i < 2; i++) {
s_re[i] = malloc(frame_length_complex_samples * sizeof(double));
......@@ -406,7 +412,7 @@ int main(int argc, char **argv) {
r_im[i] = malloc(frame_length_complex_samples * sizeof(double));
bzero(r_im[i], frame_length_complex_samples * sizeof(double));
txdata[i] = malloc(frame_length_complex_samples * sizeof(int));
bzero(r_re[i], frame_length_complex_samples * sizeof(int));
bzero(r_re[i], frame_length_complex_samples * sizeof(int)); // [hna] r_re should be txdata
}
if (pbch_file_fd != NULL) {
......@@ -440,13 +446,6 @@ int main(int argc, char **argv) {
//init_nr_ue_transport(UE, 0);
for (sf = 0; sf < 2; sf++) {
for (i = 0; i < 2; i++) {
UE->dlsch[sf][0][i] = new_nr_ue_dlsch(Kmimo, 8, Nsoft, 5, N_RB_DL,
0);
if (!UE->dlsch[sf][0][i]) {
printf("Can't get ue dlsch structures\n");
exit(-1);
}
UE->ulsch[sf][0][i] = new_nr_ue_ulsch(N_RB_UL, 8, 0);
......@@ -455,18 +454,13 @@ int main(int argc, char **argv) {
exit(-1);
}
UE->dlsch[sf][0][i]->rnti = n_rnti;
}
}
UE->dlsch_SI[0] = new_nr_ue_dlsch(1, 1, Nsoft, 5, N_RB_DL, 0);
UE->dlsch_ra[0] = new_nr_ue_dlsch(1, 1, Nsoft, 5, N_RB_DL, 0);
unsigned char harq_pid = 0; //dlsch->harq_ids[subframe];
NR_gNB_DLSCH_t *dlsch = gNB->dlsch[0][0];
nfapi_nr_dl_config_dlsch_pdu_rel15_t *rel15 = &dlsch->harq_processes[harq_pid]->dlsch_pdu.dlsch_pdu_rel15;
NR_UE_ULSCH_t *ulsch = UE->ulsch[0][0][0];
unsigned char harq_pid = 0; //dlsch->harq_ids[subframe];
//time_stats_t *rm_stats, *te_stats, *i_stats;
uint8_t is_crnti = 0, llr8_flag = 0;
unsigned int TBS = 8424;
......@@ -476,60 +470,55 @@ int main(int argc, char **argv) {
unsigned char mod_order;
uint8_t Nl = 1;
uint8_t rvidx = 0;
dlsch->rnti = 1;
ulsch->nb_re_dmrs = nb_re_dmrs; //[adk] A HOT FIX until cearting nfapi_nr_ul_config_ulsch_pdu_rel15_t
/*dlsch->harq_processes[0]->mcs = Imcs;
dlsch->harq_processes[0]->rvidx = rvidx;*/
//printf("dlschsim harqid %d nb_rb %d, mscs %d\n",dlsch->harq_ids[subframe],
// dlsch->harq_processes[0]->nb_rb,dlsch->harq_processes[0]->mcs,dlsch->harq_processes[0]->Nl);
uint8_t UE_id = 1;
NR_gNB_ULSCH_t *ulsch_gNB = gNB->ulsch[UE_id][0];
nfapi_nr_ul_config_ulsch_pdu_rel15_t *rel15_ul = &ulsch_gNB->harq_processes[harq_pid]->ulsch_pdu.ulsch_pdu_rel15;
NR_UE_ULSCH_t *ulsch_ue = UE->ulsch[0][0][0];
ulsch_ue->nb_re_dmrs = nb_re_dmrs; //[adk] A HOT FIX until cearting nfapi_nr_ul_config_ulsch_pdu_rel15_t
mod_order = nr_get_Qm(Imcs, 1);
available_bits = nr_get_G(nb_rb, nb_symb_sch, nb_re_dmrs, length_dmrs, mod_order, 1);
TBS = nr_compute_tbs(Imcs, nb_rb, nb_symb_sch, nb_re_dmrs, length_dmrs, Nl);
printf("available bits %d TBS %d mod_order %d\n", available_bits, TBS, mod_order);
//dlsch->harq_ids[subframe]= 0;
rel15->n_prb = nb_rb;
rel15->nb_symbols = nb_symb_sch;
rel15->modulation_order = mod_order;
rel15->nb_layers = Nl;
rel15->nb_re_dmrs = nb_re_dmrs;
rel15->transport_block_size = TBS;
double *modulated_input = malloc16(sizeof(double) * 16 * 68 * 384);
/////////// setting rel15_ul parameters ///////////
rel15_ul->number_rbs = nb_rb;
rel15_ul->number_symbols = nb_symb_sch;
rel15_ul->Qm = mod_order;
rel15_ul->mcs = Imcs;
rel15_ul->rv = rvidx;
rel15_ul->n_layers = Nl;
///////////////////////////////////////////////////
double *modulated_input = malloc16(sizeof(double) * 16 * 68 * 384); // [hna] 16 segments, 68*Zc
short *channel_output_fixed = malloc16(sizeof(short) * 16 * 68 * 384);
short *channel_output_uncoded = malloc16(sizeof(unsigned short) * 16 * 68 * 384);
unsigned int errors_bit_uncoded = 0;
unsigned char *estimated_output;
// unsigned char *estimated_output;
unsigned char *estimated_output_bit;
unsigned char *test_input_bit;
unsigned int errors_bit = 0;
test_input_bit = (unsigned char *) malloc16(sizeof(unsigned char) * 16 * 68 * 384);
estimated_output = (unsigned char *) malloc16(sizeof(unsigned char) * 16 * 68 * 384);
// estimated_output = (unsigned char *) malloc16(sizeof(unsigned char) * 16 * 68 * 384);
estimated_output_bit = (unsigned char *) malloc16(sizeof(unsigned char) * 16 * 68 * 384);
NR_UE_DLSCH_t *dlsch0_ue = UE->dlsch[0][0][0];
NR_DL_UE_HARQ_t *harq_process = dlsch0_ue->harq_processes[harq_pid];
harq_process->mcs = Imcs;
harq_process->Nl = Nl;
harq_process->nb_rb = nb_rb;
harq_process->Qm = mod_order;
harq_process->rvidx = rvidx;
printf("harq process ue mcs = %d Qm = %d, symb %d\n", harq_process->mcs, harq_process->Qm, nb_symb_sch);
unsigned char *test_input;
test_input = (unsigned char *) malloc16(sizeof(unsigned char) * TBS / 8);
for (i = 0; i < TBS / 8; i++)
test_input[i] = (unsigned char) rand();
estimated_output = harq_process->b;
// estimated_output = ulsch_gNB->harq_processes[harq_pid]->b;
/////////////////////////[adk] preparing UL harq_process parameters/////////////////////////
///////////
NR_UL_UE_HARQ_t *harq_process_ul_ue = ulsch->harq_processes[harq_pid];
NR_UL_UE_HARQ_t *harq_process_ul_ue = ulsch_ue->harq_processes[harq_pid];
if (harq_process_ul_ue){
if (harq_process_ul_ue) {
harq_process_ul_ue->mcs = Imcs;
harq_process_ul_ue->Nl = Nl;
......@@ -543,7 +532,6 @@ int main(int argc, char **argv) {
///////////
////////////////////////////////////////////////////////////////////////////////////////////
#ifdef DEBUG_ULSCHSIM
for (i = 0; i < TBS / 8; i++) printf("test_input[i]=%d \n",test_input[i]);
#endif
......@@ -558,17 +546,23 @@ int main(int argc, char **argv) {
///////////
if (input_fd == NULL) {
nr_dlsch_encoding(test_input, subframe, dlsch, frame_parms);
nr_ulsch_encoding(ulsch, frame_parms, harq_pid);
nr_ulsch_encoding(ulsch_ue, frame_parms, harq_pid);
}
///////////
////////////////////////////////////////////////////////////////////
for (SNR = snr0; SNR < snr1; SNR += snr_step) {
n_errors = 0;
n_false_positive = 0;
for (trial = 0; trial < n_trials; trial++) {
errors_bit_uncoded = 0;
for (i = 0; i < available_bits; i++) {
#ifdef DEBUG_CODER
if ((i&0xf)==0)
printf("\ne %d..%d: ",i,i+15);
......@@ -576,17 +570,18 @@ int main(int argc, char **argv) {
/*
if (i<16){
printf("dlsch_encoder output f[%d] = %d\n",i,dlsch->harq_processes[0]->f[i]);
printf("ulsch_encoder output f[%d] = %d\n",i,ulsch->harq_processes[0]->f[i]);
printf("ulsch_encoder output f[%d] = %d\n",i,ulsch_ue->harq_processes[0]->f[i]);
}
*/
if (ulsch->harq_processes[0]->f[i] == 0)
if (ulsch_ue->harq_processes[0]->f[i] == 0)
modulated_input[i] = 1.0; ///sqrt(2); //QPSK
else
modulated_input[i] = -1.0; ///sqrt(2);
//if (i<16) printf("modulated_input[%d] = %d\n",i,modulated_input[i]);
//SNR =10;
SNR_lin = pow(10, SNR / 10.0);
sigma = 1.0 / sqrt(2 * SNR_lin);
#if 1
......@@ -610,28 +605,29 @@ int main(int argc, char **argv) {
else
channel_output_uncoded[i] = 0;
if (channel_output_uncoded[i] != dlsch->harq_processes[harq_pid]->f[i])
if (channel_output_uncoded[i] != ulsch_ue->harq_processes[harq_pid]->f[i])
errors_bit_uncoded = errors_bit_uncoded + 1;
}
//if (errors_bit_uncoded>10)
printf("errors bits uncoded %u\n", errors_bit_uncoded);
#ifdef DEBUG_CODER
printf("\n");
exit(-1);
#endif
ret = nr_dlsch_decoding(UE, channel_output_fixed, &UE->frame_parms,
dlsch0_ue, dlsch0_ue->harq_processes[0], frame, nb_symb_sch,
subframe, harq_pid, is_crnti, llr8_flag);
if (ret > dlsch0_ue->max_ldpc_iterations)
ret = nr_ulsch_decoding(gNB, UE_id, channel_output_fixed, frame_parms,
frame, nb_symb_sch, subframe, harq_pid, is_crnti, llr8_flag);
if (ret > ulsch_gNB->max_ldpc_iterations)
n_errors++;
//count errors
errors_bit = 0;
for (i = 0; i < TBS; i++) {
estimated_output_bit[i] = (ulsch->harq_processes[0]->b[i / 8] & (1 << (i & 7))) >> (i & 7);
estimated_output_bit[i] = (ulsch_gNB->harq_processes[harq_pid]->b[i/8] & (1 << (i & 7))) >> (i & 7);
test_input_bit[i] = (test_input[i / 8] & (1 << (i & 7))) >> (i & 7); // Further correct for multiple segments
if (estimated_output_bit[i] != test_input_bit[i]) {
......@@ -647,15 +643,15 @@ int main(int argc, char **argv) {
}
}
printf("*****************************************\n");
printf("SNR %f, BLER %f (false positive %f)\n", SNR,
(float) n_errors / (float) n_trials,
(float) n_false_positive / (float) n_trials);
printf("*****************************************\n");
if ((float) n_errors / (float) n_trials < target_error_rate) {
printf("PDSCH test OK\n");
if ((float) n_errors / (float) n_trials < target_error_rate)
break;
}
}
/*LOG_M("txsigF0.m","txsF0", gNB->common_vars.txdataF[0],frame_length_complex_samples_no_prefix,1,1);
if (gNB->frame_parms.nb_antennas_tx>1)
......@@ -690,24 +686,29 @@ int main(int argc, char **argv) {
}
}*/
for (i = 0; i < 2; i++) {
printf("gNB %d\n", i);
printf("----------------------\n");
printf("freeing codeword %d\n", i);
printf("----------------------\n");
if (gNB->dlsch[0][i])
free_gNB_dlsch(gNB->dlsch[0][i]);
printf("gNB ulsch[0][%d]\n", i); // [hna] ulsch[0] is for RA
for (sf = 0; sf < 2; sf++) {
free_gNB_ulsch(gNB->ulsch[0][i]);
printf("UE %d\n", i);
printf("gNB ulsch[%d][%d]\n",UE_id, i);
if (UE->dlsch[sf][0][i])
free_nr_ue_dlsch(UE->dlsch[sf][0][i]);
free_gNB_ulsch(gNB->ulsch[UE_id][i]);
for (sf = 0; sf < 2; sf++) {
printf("UE ulsch[%d][0][%d]\n", sf, i);
if (UE->ulsch[sf][0][i])
free_nr_ue_ulsch(UE->ulsch[sf][0][i]);
}
printf("\n");
}
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