diff --git a/openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_ue.c b/openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_ue.c index ff2d67e793..310fa7dc5f 100644 --- a/openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_ue.c +++ b/openair1/PHY/NR_UE_TRANSPORT/nr_ulsch_ue.c @@ -1224,6 +1224,9 @@ void nr_ue_ulsch_procedures(PHY_VARS_NR_UE *UE, is_uci_on_pusch, uci_mapping_template, scrambled_output); + if (UE->phy_sim_test_buf) { + memcpy(UE->phy_sim_test_buf, scrambled_output, (available_bits + 7) / 8); + } #if T_TRACER if (T_ACTIVE(T_UE_PHY_UL_SCRAMBLED_TX_BITS)) { // Get Time Stamp for T-tracer messages diff --git a/openair1/PHY/defs_nr_UE.h b/openair1/PHY/defs_nr_UE.h index d56355298b..935226b238 100644 --- a/openair1/PHY/defs_nr_UE.h +++ b/openair1/PHY/defs_nr_UE.h @@ -511,6 +511,7 @@ typedef struct PHY_VARS_NR_UE_s { void *phy_sim_pdsch_dl_ch_estimates; void *phy_sim_pdsch_dl_ch_estimates_ext; uint8_t *phy_sim_dlsch_b; + uint8_t *phy_sim_test_buf; dynamic_barrier_t process_slot_tx_barriers[NUM_PROCESS_SLOT_TX_BARRIERS]; diff --git a/openair1/SIMULATION/NR_PHY/ulsim.c b/openair1/SIMULATION/NR_PHY/ulsim.c index ec4a7190fa..edab935e15 100644 --- a/openair1/SIMULATION/NR_PHY/ulsim.c +++ b/openair1/SIMULATION/NR_PHY/ulsim.c @@ -160,6 +160,108 @@ openair0_config_t openair0_cfg[MAX_CARDS]; channel_desc_t *UE2gNB[MAX_MOBILES_PER_GNB][NUMBER_OF_gNB_MAX]; +static void copy_bytes_to_packed_bits(const uint8_t *in, const uint32_t num_bits, const bool is_ulsch, uint8_t *out) +{ + if (is_ulsch) { // MATLAB computes CRC for input of MSB first + for (uint_fast32_t b = 0; b < num_bits; b++) { + out[b / 8] |= ((in[b] & 1) << (7 - (b % 8))); + } + } else { + for (uint_fast32_t b = 0; b < num_bits; b++) { + out[b / 8] |= (in[b] << (b % 8)); + } + } +} + +static void prepare_ue_pusch_pdu_from_matlab_vector(const bool uci_on_pusch, + FILE *vect_file, + nfapi_nr_ue_pusch_pdu_t *pusch_config_pdu, + uint8_t *cw_buf) +{ + if (!uci_on_pusch) + return; + + if (vect_file == NULL) + return; + + struct vect_vars { + uint32_t A; + uint32_t oack; + uint32_t ocsi1; + uint32_t ocsi2; + uint32_t cwlen; + uint32_t cwlen_scr; + } __attribute__((packed)); + + struct vect_vars var = {0}; + if (1 != fread(&var, sizeof(var), 1, vect_file)) { + printf("Error reading from matlab vector file\n"); + exit(-1); + } + + const uint16_t buff_len = var.A + var.oack + var.ocsi1 + var.ocsi2; + uint8_t vec_bits[buff_len]; + memset(vec_bits, 0, sizeof(vect_file)); + + uint8_t *p_vec_bits = vec_bits; + if (var.A != fread(p_vec_bits, sizeof(uint8_t), var.A, vect_file)) { + printf("Error reading ULSCH bits from file\n"); + exit(-1); + } + p_vec_bits += var.A; + if (var.oack != fread(p_vec_bits, sizeof(uint8_t), var.oack, vect_file)) { + printf("Error reading ACK bits from file\n"); + exit(-1); + } + p_vec_bits += var.oack; + if (var.ocsi1 != fread(p_vec_bits, sizeof(uint8_t), var.ocsi1, vect_file)) { + printf("Error reading CSI1 bits from file\n"); + exit(-1); + } + p_vec_bits += var.ocsi1; + if (var.ocsi2 != fread(p_vec_bits, sizeof(uint8_t), var.ocsi2, vect_file)) { + printf("Error reading CSI2 bits from file\n"); + exit(-1); + } + + if (var.cwlen != fread(cw_buf, sizeof(uint8_t), var.cwlen, vect_file)) { + printf("Error reading cw bits from file\n"); + exit(-1); + } + + memset(cw_buf, 0, var.cwlen_scr); + if (var.cwlen_scr != fread(cw_buf, sizeof(uint8_t), var.cwlen_scr, vect_file)) { + printf("Error reading cw bits from file\n"); + exit(-1); + } + + uint16_t tb_buf_size = (var.A + 7) / 8; + pusch_config_pdu->pusch_data.tb_size = tb_buf_size; + pusch_config_pdu->tx_request_body.pdu_length = tb_buf_size; + uint8_t *pb = pusch_config_pdu->tx_request_body.fapiTxPdu; + memset(pb, 0, tb_buf_size); + p_vec_bits = vec_bits; + copy_bytes_to_packed_bits(p_vec_bits, var.A, true, pb); + + pusch_config_pdu->pusch_uci.harq_ack_bit_length = var.oack; + pb = (uint8_t *)&pusch_config_pdu->pusch_uci.harq_payload; + memset(pb, 0, sizeof(pusch_config_pdu->pusch_uci.harq_payload)); + p_vec_bits += var.A; + copy_bytes_to_packed_bits(p_vec_bits, var.oack, false, pb); + + pusch_config_pdu->pusch_uci.csi_part1_bit_length = var.ocsi1; + pb = (uint8_t *)&pusch_config_pdu->pusch_uci.csi_part1_payload; + memset(pb, 0, sizeof(pusch_config_pdu->pusch_uci.csi_part1_payload)); + p_vec_bits += var.oack; + copy_bytes_to_packed_bits(p_vec_bits, var.ocsi1, false, pb); + + pusch_config_pdu->pusch_uci.csi_part2_bit_length = var.ocsi2; + pb = (uint8_t *)&pusch_config_pdu->pusch_uci.csi_part2_payload; + memset(pb, 0, sizeof(pusch_config_pdu->pusch_uci.csi_part2_payload)); + p_vec_bits += var.ocsi1; + copy_bytes_to_packed_bits(p_vec_bits, var.ocsi2, false, pb); +} + configmodule_interface_t *uniqCfg = NULL; int main(int argc, char *argv[]) { @@ -204,6 +306,8 @@ int main(int argc, char *argv[]) int print_perf = 0; cpuf = get_cpu_freq_GHz(); int msg3_flag = 0; + bool uci_on_pusch = false; + bool no_phase_pre_comp = false; int rv_index = 0; float roundStats; double effRate; @@ -227,6 +331,7 @@ int main(int argc, char *argv[]) UE_nr_rxtx_proc_t UE_proc; FILE *scg_fd=NULL; + FILE *uci_ulsch_matlab_vec = NULL; int file_offset = 0; double DS_TDL = .03; @@ -252,7 +357,8 @@ int main(int argc, char *argv[]) int c; bool setAffinity=false; char gNBthreads[128]="n"; - while ((c = getopt(argc, argv, "--:O:a:b:c:d:ef:g:h:i:jk:m:n:p:q:r:s:t:u:v:w:y:z:A:C:F:G:H:I:M:N:PR:S:T:U:L:ZW:E:X:Y:")) != -1) { + while ((c = getopt(argc, argv, "--:O:a:b:c:d:ef:g:h:i:jk:m:n:o::p:q:r:s:t:u:v:w:y:z:A:C:F:G:H:I:M:N:PR:S:T:U:L:ZW:E:X:Y:")) + != -1) { /* ignore long options starting with '--', option '-O' and their arguments that are handled by configmodule */ /* with this opstring getopt returns 1 for non-option arguments, refer to 'man 3 getopt' */ if (c == 1 || c == '-' || c == 'O') @@ -360,6 +466,20 @@ int main(int argc, char *argv[]) Imcs = atoi(optarg); break; + case 'o': + uci_on_pusch = true; + // UCI on PUSCH is not implemented in OAI gNB yet. + // So this flag is needed to verify in MATLAB + no_phase_pre_comp = true; + if (optarg) { // -o with file input: use matlab vector + uci_ulsch_matlab_vec = fopen(optarg, "rb"); + if (uci_ulsch_matlab_vec == NULL) { + printf("Error opening %s\n", optarg); + exit(-1); + } + } + break; + case 'W': precod_nbr_layers = atoi(optarg); break; @@ -539,6 +659,7 @@ int main(int argc, char *argv[]) printf("-k 3/4 sampling\n"); printf("-m MCS value\n"); printf("-n Number of trials to simulate\n"); + printf("-o Enable UCI on PUSCH. Optionally accepts input file (without space). This feature is not yet available in gNB so only used to verify with MATLAB generated vector\n"); printf("-p Use extended prefix mode\n"); printf("-q MCS table\n"); printf("-r Number of allocated resource blocks for PUSCH\n"); @@ -786,7 +907,8 @@ int main(int argc, char *argv[]) UE->if_inst->phy_config_request = nr_ue_phy_config_request; UE->if_inst->dl_indication = nr_ue_dl_indication; UE->if_inst->ul_indication = nr_ue_ul_indication; - + UE->no_phase_pre_comp = no_phase_pre_comp; + UE_mac->if_module = nr_ue_if_module_init(0); initFloatingCoresTpool(threadCnt, &nrUE_params.Tpool, false, "UE-tpool"); @@ -924,6 +1046,9 @@ int main(int argc, char *argv[]) } unsigned int available_bits = nr_get_G(nb_rb, nb_symb_sch, nb_re_dmrs, number_dmrs_symbols, unav_res, mod_order, precod_nbr_layers); + uint8_t cw_buf[available_bits]; + memset(cw_buf, 0, available_bits); + UE->phy_sim_test_buf = calloc(1, (available_bits + 7) / 8); printf("[ULSIM]: VALUE OF G: %u, TBS: %u\n", available_bits, TBS); int frame_length_complex_samples = gNB->frame_parms.samples_per_subframe * NR_NUMBER_OF_SUBFRAMES_PER_FRAME; @@ -1222,6 +1347,20 @@ int main(int argc, char *argv[]) // pusch_config_pdu->pdu_bit_map |= PUSCH_PDU_BITMAP_DFTS_OFDM; pusch_config_pdu->num_dmrs_cdm_grps_no_data = num_dmrs_cdm_grps_no_data; } + if (uci_on_pusch) { + const nfapi_nr_ue_pusch_uci_t pusch_uci = { + .alpha_scaling = 3, + .beta_offset_csi1 = 13, + .beta_offset_csi2 = 13, + .beta_offset_harq_ack = 11, + .harq_ack_bit_length = 3, + .harq_payload = 3, + //.csi_part1_bit_length = 4, + //.csi_part1_payload = 15 + }; + pusch_config_pdu->pusch_uci = pusch_uci; + prepare_ue_pusch_pdu_from_matlab_vector(uci_on_pusch, uci_ulsch_matlab_vec, pusch_config_pdu, cw_buf); + } if (do_SRS == 1) { fapi_nr_ul_config_request_pdu_t *ul_config1 = &ul_config.ul_config_list[1]; @@ -1457,7 +1596,7 @@ int main(int argc, char *argv[]) } if ((ulsch_gNB->last_iteration_cnt >= ulsch_gNB->max_ldpc_iterations) || ul_proc_error == 1) { - error_flag = 1; + error_flag = uci_on_pusch ? 0 : 1; n_errors[round]++; crc_status = 1; } else @@ -1474,15 +1613,29 @@ int main(int argc, char *argv[]) available_bits, (ptrsSymbPerSlot * ptrsRePerSymb * mod_order * precod_nbr_layers)); } - for (i = 0; i < available_bits; i++) { - const uint8_t current_bit = (UE->ul_harq_processes[harq_pid].f[i / 8] >> (i & 7)) & 1; - if (((current_bit == 0) && (pusch_vars->llr[i] <= 0)) || ((current_bit == 1) && (pusch_vars->llr[i] >= 0))) { - /*if(errors_scrambling == 0) - printf("\x1B[34m" "[frame %d][trial %d]\t1st bit in error in unscrambling = %d\n" "\x1B[0m", frame, trial, i);*/ - errors_scrambling[round]++; + if (uci_on_pusch) { + for (i = 0; i < available_bits; i++) { + const uint8_t current_bit = (UE->phy_sim_test_buf[i / 8] >> (i & 7)) & 1; + const uint8_t test_vector_bit = cw_buf[i] & 1; + if (current_bit != test_vector_bit) + errors_scrambling[round]++; + } + } else { + for (i = 0; i < available_bits; i++) { + const uint8_t current_bit = (UE->ul_harq_processes[harq_pid].f[i / 8] >> (i & 7)) & 1; + if (((current_bit == 0) && (pusch_vars->llr[i] <= 0)) || ((current_bit == 1) && (pusch_vars->llr[i] >= 0))) { + errors_scrambling[round]++; + } } } round++; + if (uci_on_pusch && uci_ulsch_matlab_vec && (errors_scrambling[round] == 0)) { + ret = 0; + printf("*************\n"); + printf("UCI on PUSCH test OK against MATLAB generated codeword\n"); + printf("*************\n"); + break; + } } // round if (n_trials == 1 && errors_scrambling[0] > 0) { @@ -1655,5 +1808,10 @@ int main(int argc, char *argv[]) free(filename_csv); } + if (uci_ulsch_matlab_vec) + fclose(uci_ulsch_matlab_vec); + + free_and_zero(UE->phy_sim_test_buf); + return ret; } diff --git a/openair1/SIMULATION/TOOLS/uci_on_pusch_decode.m b/openair1/SIMULATION/TOOLS/uci_on_pusch_decode.m new file mode 100644 index 0000000000..122b4912fb --- /dev/null +++ b/openair1/SIMULATION/TOOLS/uci_on_pusch_decode.m @@ -0,0 +1,79 @@ +% ULSIM command line +% ./nr_ulsim -m 27 -u 1 -R 51 -r 51 -s 300 -L 4 -o + +carrier = nrCarrierConfig; +carrier.NSizeGrid = 51; +carrier.NSlot = 8; +carrier.SubcarrierSpacing = 30; +carrier.CyclicPrefix = "normal"; + +pusch = nrPUSCHConfig; +pusch.PRBSet = 0:carrier.NSizeGrid-1; +pusch.SymbolAllocation = [0,12]; +pusch.MappingType = "B"; +pusch.NID = 0; +pusch.RNTI = 4660; +pusch.NumLayers = 1; +pusch.TransformPrecoding = false; +pusch.TransmissionScheme = "nonCodebook"; +pusch.NumAntennaPorts = 1; +pusch.TPMI = 0; +pusch.Modulation = "64QAM"; + +pusch.DMRS.DMRSConfigurationType = 1; +pusch.DMRS.DMRSTypeAPosition = 2; +pusch.DMRS.NumCDMGroupsWithoutData = 1; +pusch.DMRS.DMRSAdditionalPosition = 0; +pusch.DMRS.NIDNSCID = 0; +pusch.DMRS.NRSID = 0; +pusch.DMRS.NSCID = 0; +pusch.DMRS.GroupHopping = 0; +pusch.DMRS.SequenceHopping = 0; + +pusch.BetaOffsetACK = 20; +pusch.BetaOffsetCSI1 = 6.25; +pusch.BetaOffsetCSI2 = 1; +pusch.UCIScaling = 1; + +[puschIndices,puschIndicesInfo] = nrPUSCHIndices(carrier,pusch); + +dmrsLayerSymbols = nrPUSCHDMRS(carrier,pusch); +dmrsLayerIndices = nrPUSCHDMRSIndices(carrier,pusch); + +run("../../../cmake_targets/ran_build/build/txsig0.m"); +rxGrid = nrOFDMDemodulate(carrier,txs0); +[estChannelGrid,noiseEst] = nrChannelEstimate(carrier,rxGrid,dmrsLayerIndices,dmrsLayerSymbols); +[puschRx,puschHest] = nrExtractResources(puschIndices,rxGrid,estChannelGrid); +[puschEq,csi] = nrEqualizeMMSE(puschRx,puschHest,noiseEst); + +decodeULSCH = nrULSCHDecoder; +decodeULSCH.MultipleHARQProcesses = false; +decodeULSCH.CBGTransmission = false; +decodeULSCH.TargetCodeRate = 910 / 1024; +decodeULSCH.LDPCDecodingAlgorithm = 'Normalized min-sum'; +decodeULSCH.MaximumLDPCIterationCount = 6; +decodeULSCH.TransportBlockLength = 37896; + +oack = 3; +ocsi1 = 0; +ocsi2 = 0; +[ulschLLRs,rxSymbols] = nrPUSCHDecode(carrier,pusch,decodeULSCH.TargetCodeRate,... + decodeULSCH.TransportBlockLength,oack,ocsi1,ocsi2,puschEq,noiseEst); +rmInfo = nrULSCHInfo(pusch,decodeULSCH.TargetCodeRate,... + decodeULSCH.TransportBlockLength,oack,ocsi1,ocsi2); +[culsch,cack,ccsi1,ccsi2] = nrULSCHDemultiplex(pusch,decodeULSCH.TargetCodeRate,... + decodeULSCH.TransportBlockLength,oack,ocsi1,ocsi2,ulschLLRs); + +plot(rxSymbols,'*'); + +decodeULSCH.reset(); +rv = 0; % Redundancy version for decoding +[decodedBits,blkerr] = decodeULSCH(culsch,pusch.Modulation,pusch.NumLayers,rv); +% Display the decoded bits +disp('Block error:'); +disp(blkerr); + +ucibits = nrUCIDecode(cack,oack); +% Display the decoded UCI bits +disp('Decoded UCI bits:'); +disp(ucibits); diff --git a/openair1/SIMULATION/TOOLS/uci_on_pusch_encode.m b/openair1/SIMULATION/TOOLS/uci_on_pusch_encode.m new file mode 100644 index 0000000000..0b83d211d2 --- /dev/null +++ b/openair1/SIMULATION/TOOLS/uci_on_pusch_encode.m @@ -0,0 +1,85 @@ +% ulsim command for same pusch config +% ./nr_ulsim -m 27 -u 1 -R 51 -r 51 -s 300 -o uci_on_pusch_7.bin + +carrier = nrCarrierConfig; +carrier.NSizeGrid = 51; +carrier.NSlot = 8; +carrier.SubcarrierSpacing = 30; +carrier.CyclicPrefix = "normal"; + +pusch = nrPUSCHConfig; +pusch.PRBSet = 0:carrier.NSizeGrid-1; +pusch.SymbolAllocation = [0,12]; +pusch.MappingType = "B"; +pusch.NID = 0; +pusch.RNTI = 4660; +pusch.NumLayers = 1; +pusch.TransformPrecoding = false; +pusch.TransmissionScheme = "nonCodebook"; +pusch.NumAntennaPorts = 1; +pusch.TPMI = 0; +pusch.Modulation = "64QAM"; + +pusch.DMRS.DMRSConfigurationType = 1; +pusch.DMRS.DMRSTypeAPosition = 2; +pusch.DMRS.NumCDMGroupsWithoutData = 1; +pusch.DMRS.DMRSAdditionalPosition = 0; +pusch.DMRS.NIDNSCID = 0; +pusch.DMRS.NRSID = 0; +pusch.DMRS.NSCID = 0; +pusch.DMRS.GroupHopping = 0; +pusch.DMRS.SequenceHopping = 0; + +pusch.BetaOffsetACK = 20; +pusch.BetaOffsetCSI1 = 6.25; +pusch.BetaOffsetCSI2 = 1; +pusch.UCIScaling = 1; + +A = 37896; +rate = 910 / 1024; +rv = 0; +modulation = pusch.Modulation; +nlayers = pusch.NumLayers; % Number of layers for decoding + +oack = 7; +ocsi1 = 0; +ocsi2 = 0; +cbsInfo = nrULSCHInfo(pusch, rate, A, oack, ocsi1, ocsi2); % Get ULSCH information + +ack = randi([0 1],oack,1); +csi1 = randi([0 1],ocsi1,1); +csi2 = randi([0 1],ocsi2,1); +cack = nrUCIEncode(ack,cbsInfo.GACK,pusch.Modulation); +ccsi1 = []; +ccsi2 = []; + +tb_data = randi([0 1],A,1); +% Transport block CRC attachment +tbIn = nrCRCEncode(tb_data,cbsInfo.CRC); + +% Code block segmentation and CRC attachment +cbsIn = nrCodeBlockSegmentLDPC(tbIn,cbsInfo.BGN); + +% LDPC encoding +enc = nrLDPCEncode(cbsIn,cbsInfo.BGN); + +% Rate matching and code block concatenation +chIn = nrRateMatchLDPC(enc,cbsInfo.GULSCH,rv,modulation,nlayers); +culsch = chIn; + + +% Get the codeword and locations of each type (data and UCI) +[cw,indInfo] = nrULSCHMultiplex(pusch,rate,A,culsch,cack,ccsi1,ccsi2); +cw_scr = nrPUSCHScramble(cw,pusch.NID,pusch.RNTI); + +% Write to bin file +fid = fopen('uci_on_pusch_7.bin', 'wb'); +to_write = [A,oack,ocsi1,ocsi2,length(cw),length(cw_scr)]; +fwrite(fid, to_write, 'uint32'); % Write the data to the binary file +fwrite(fid, tb_data, "uint8"); +fwrite(fid, ack, "uint8"); +fwrite(fid, csi1, "uint8"); +fwrite(fid, csi2, "uint8"); +fwrite(fid, cw, "uint8"); +fwrite(fid, cw_scr, "uint8"); +fclose(fid); % Close the file after writing