diff --git a/executables/nr-softmodem.c b/executables/nr-softmodem.c index 162f4beec957262955d7f45e23c3a24ca3ff2ed3..b61273523ae9952aeddadb1c6d038be747b5d0f5 100644 --- a/executables/nr-softmodem.c +++ b/executables/nr-softmodem.c @@ -94,6 +94,7 @@ unsigned short config_frames[4] = {2,9,11,13}; #include "openair2/LAYER2/nr_rlc/nr_rlc_oai_api.h" #include "openair2/LAYER2/nr_pdcp/nr_pdcp.h" #include "openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h" +#include "openair2/LAYER2/NR_MAC_gNB/mac_proto.h" #include <time.h> ////////////////////////////////// @@ -889,7 +890,159 @@ void read_pdcp_sm(pdcp_ind_msg_t* data) } } +static +void read_kpm_sm(kpm_ind_data_t* data) +{ + assert(data != NULL); + + // Fill KPM indication header + kpm_ind_hdr_t* hdr = &data->hdr; + hdr->collectStartTime.len = 4; + hdr->collectStartTime.buf = calloc(1, 4); + assert(hdr->collectStartTime.buf != NULL && "memory exhausted"); + + int64_t t = time_now_us(); + uint32_t t_truncated = t / 1000000; +#if BYTE_ORDER == LITTLE_ENDIAN + t_truncated = __bswap_32 (t_truncated); +#endif + + memcpy(hdr->collectStartTime.buf, &t_truncated, 4); + hdr->fileFormatversion = NULL; + hdr->senderName = NULL; + hdr->senderType = NULL; + hdr->vendorName = NULL; + + // Fill KPM indication message + kpm_ind_msg_t* msg = &data->msg; + + // TODO: assign MeaData_len according to eventPeriod/granulPeriod from the action definition or subscription request + msg->MeasData_len = 1; + if (msg->MeasData_len > 0) { + msg->MeasData = calloc(msg->MeasData_len, sizeof(adapter_MeasDataItem_t)); + assert(msg->MeasData != NULL && "Memory exhausted" ); + } + + // get the number of connected UEs + NR_UEs_t *UE_info = &RC.nrmac[mod_id]->UE_info; + size_t num_ues = 0; + UE_iterator(UE_info->list, ue) { + if (ue) + num_ues += 1; + } + + if (num_ues > 0) { + // get the info to calculate the resource utilization + NR_ServingCellConfigCommon_t *scc = RC.nrmac[mod_id]->common_channels[0].ServingCellConfigCommon; + int cur_slot = RC.nrmac[mod_id]->slot; + // int num_dl_slots = scc->tdd_UL_DL_ConfigurationCommon->pattern1.nrofDownlinkSlots; + // get total number of available resource blocks + int n_rb_sched = 0; + if (UE_info->list[0] != NULL) { + /* Get bwpSize and TDA from the first UE */ + /* This is temporary and it assumes all UEs have the same BWP and TDA*/ + NR_UE_info_t *UE = UE_info->list[0]; + NR_UE_sched_ctrl_t *sched_ctrl = &UE->UE_sched_ctrl; + NR_UE_DL_BWP_t *current_BWP = &UE->current_DL_BWP; + const int tda = get_dl_tda(RC.nrmac[mod_id], scc, cur_slot); + int startSymbolIndex, nrOfSymbols; + const struct NR_PDSCH_TimeDomainResourceAllocationList *tdaList = current_BWP->tdaList; + AssertFatal(tda < tdaList->list.count, "time_domain_allocation %d>=%d\n", tda, tdaList->list.count); + const int startSymbolAndLength = tdaList->list.array[tda]->startSymbolAndLength; + SLIV2SL(startSymbolAndLength, &startSymbolIndex, &nrOfSymbols); + const int coresetid = sched_ctrl->coreset->controlResourceSetId; + const uint16_t bwpSize = coresetid == 0 ? RC.nrmac[mod_id]->cset0_bwp_size : current_BWP->BWPSize; + const uint16_t BWPStart = coresetid == 0 ? RC.nrmac[mod_id]->cset0_bwp_start : current_BWP->BWPStart; + const uint16_t slbitmap = SL_to_bitmap(startSymbolIndex, nrOfSymbols); + uint16_t *vrb_map = RC.nrmac[mod_id]->common_channels[0].vrb_map; + uint16_t rballoc_mask[bwpSize]; + for (int i = 0; i < bwpSize; i++) { + // calculate mask: init with "NOT" vrb_map: + // if any RB in vrb_map is blocked (1), the current RBG will be 0 + rballoc_mask[i] = (~vrb_map[i + BWPStart]) & 0x3fff; //bitwise not and 14 symbols + // if all the pdsch symbols are free + if ((rballoc_mask[i] & slbitmap) == slbitmap) { + n_rb_sched++; + } + } + } + + // TODO: assign the MeasData every granulPeriod + for (size_t i = 0; i < msg->MeasData_len; i++) { + adapter_MeasDataItem_t* item = &msg->MeasData[i]; + + // TODO: assign measRecord_len according to + // (1) the length of Measurements Information List IE (format1) or + // (2) Measurements Information Condition UE List IE (format2) + // from the action definition or subscription request + + // TODO: only support KPM format 1, and it only can handle one UE's information + // assume to record one data: DL resource utilization + item->measRecord_len = 1; + if (item->measRecord_len > 0) { + item->measRecord = calloc(item->measRecord_len, sizeof(adapter_MeasRecord_t)); + assert(item->measRecord != NULL && "Memory exhausted"); + } + + UE_iterator(UE_info->list, UE) + { + int dl_rb_usage = 0; + if (is_xlsch_in_slot(RC.nrmac[mod_id]->dlsch_slot_bitmap[cur_slot / 64], cur_slot)) + dl_rb_usage = UE->mac_stats.dl.current_rbs*100/n_rb_sched; + + // TODO: go through the measRecord according to the Measurements Information (format 1) or Information Condition UE (format 2) List IE + adapter_MeasRecord_t *record_PrbDlUsage = &item->measRecord[0]; + record_PrbDlUsage->type = MeasRecord_int; + record_PrbDlUsage->int_val = dl_rb_usage; + } + + // incompleteFlag = -1, the data is reliable + item->incompleteFlag = -1; + } + // TODO: assign MeasInfo_len according to the action definition or subscription request + msg->MeasInfo_len = 1; + if (msg->MeasInfo_len > 0) { + msg->MeasInfo = calloc(msg->MeasInfo_len, sizeof(MeasInfo_t)); + assert(msg->MeasInfo != NULL && "Memory exhausted" ); + + MeasInfo_t* info = &msg->MeasInfo[0]; + info->measType = MeasurementType_NAME; + char* measName = "PrbDlUsage"; + info->measName.len = strlen(measName); + info->measName.buf = malloc(strlen(measName)); + assert(info->measName.buf != NULL && "memory exhausted"); + memcpy(info->measName.buf, measName, msg->MeasInfo[0].measName.len); + + // TODO: assign labelInfo_len according to the action definition (?) + info->labelInfo_len = 1; + info->labelInfo = calloc(info->labelInfo_len, sizeof(adapter_LabelInfoList_t)); + adapter_LabelInfoList_t* label = &info->labelInfo[0]; + label->noLabel = calloc(1, sizeof(long)); + *(label->noLabel) = 1; + } + } else { + for (size_t i = 0; i < msg->MeasData_len; i++) { + adapter_MeasDataItem_t* item = &msg->MeasData[i]; + item->measRecord_len = 1; + if (item->measRecord_len > 0) { + item->measRecord = calloc(item->measRecord_len, sizeof(adapter_MeasRecord_t)); + assert(item->measRecord != NULL && "Memory exhausted"); + } + + adapter_MeasRecord_t *record_nodata = &item->measRecord[0]; + record_nodata->type = MeasRecord_int; + record_nodata->int_val = 0; + + // incompleteFlag = 0, the data is not reliable + item->incompleteFlag = 0; + } + msg->MeasInfo_len = 0; + msg->MeasInfo = NULL; + } + + msg->granulPeriod = NULL; +} @@ -900,19 +1053,17 @@ void read_RAN(sm_ag_if_rd_t* data) assert(data->type == MAC_STATS_V0 || data->type == RLC_STATS_V0 || data->type == PDCP_STATS_V0 + || data->type == KPM_STATS_V0 ); if(data->type == MAC_STATS_V0 ){ read_mac_sm(&data->mac_stats.msg); - // printf("calling REAd MAC\n"); }else if(data->type == RLC_STATS_V0) { read_rlc_sm(&data->rlc_stats.msg); - // printf("calling REAd RLC\n"); } else if(data->type == PDCP_STATS_V0){ read_pdcp_sm(&data->pdcp_stats.msg); - //printf("calling REAd PDCP\n"); -// } else if(RRC_STATS_V0){ -// read_rrc_sm(&data->rrc_stats); + } else if(data->type == KPM_STATS_V0){ + read_kpm_sm(&data->kpm_stats); } else { assert(0!=0 && "Unknown data type!"); }