diff --git a/ci-scripts/args_parse.py b/ci-scripts/args_parse.py index c1a13eb9bf..cbf4159da2 100644 --- a/ci-scripts/args_parse.py +++ b/ci-scripts/args_parse.py @@ -47,6 +47,7 @@ def ArgsParse(argvs,CiTestObj,RAN,HTML,EPC,CONTAINERS,HELP,SCA,PHYSIM,CLUSTER): py_param_file_present = False py_params={} + force_local = False while len(argvs) > 1: myArgv = argvs.pop(1) # 0th is this file's name @@ -54,6 +55,9 @@ def ArgsParse(argvs,CiTestObj,RAN,HTML,EPC,CONTAINERS,HELP,SCA,PHYSIM,CLUSTER): if re.match('^\-\-help$', myArgv, re.IGNORECASE): HELP.GenericHelp(CONST.Version) sys.exit(0) + if re.match('^\-\-local$', myArgv, re.IGNORECASE): + force_local = True + #--apply= as parameters file, to replace inline parameters elif re.match('^\-\-Apply=(.+)$', myArgv, re.IGNORECASE): @@ -271,4 +275,4 @@ def ArgsParse(argvs,CiTestObj,RAN,HTML,EPC,CONTAINERS,HELP,SCA,PHYSIM,CLUSTER): HELP.GenericHelp(CONST.Version) sys.exit('Invalid Parameter: ' + myArgv) - return py_param_file_present, py_params, mode + return py_param_file_present, py_params, mode, force_local diff --git a/ci-scripts/cls_cmd.py b/ci-scripts/cls_cmd.py index 8784b70831..b338a6938f 100644 --- a/ci-scripts/cls_cmd.py +++ b/ci-scripts/cls_cmd.py @@ -217,6 +217,8 @@ class RemoteCmd(Cmd): return client def _lookup_ssh_config(hostname): + if is_local(hostname): + raise ValueError("Using localhost as SSH target is not allowed: use LocalCmd instead.") ssh_config = paramiko.SSHConfig() user_config_file = os.path.expanduser("~/.ssh/config") if os.path.exists(user_config_file): diff --git a/ci-scripts/helpreadme.py b/ci-scripts/helpreadme.py index 9ecba905ac..e1640fc899 100644 --- a/ci-scripts/helpreadme.py +++ b/ci-scripts/helpreadme.py @@ -43,6 +43,8 @@ def GenericHelp(vers): print(' InitiateHtml, FinalizeHtml') print(' TerminateeNB, TerminateHSS, TerminateMME, TerminateSPGW') print(' LogCollectBuild, LogCollecteNB, LogCollectHSS, LogCollectMME, LogCollectSPGW, LogCollectPing, LogCollectIperf') + print(' --local Force local execution: rewrites the test xml script before running to always execute on localhost. Assumes') + print(' images are available locally, will not remove any images and will run inside the current repo directory') def GitSrvHelp(repository,branch,commit,mergeallow,targetbranch): print(' --ranRepository=[OAI RAN Repository URL] -- ' + repository) diff --git a/ci-scripts/main.py b/ci-scripts/main.py index bba0f3f57f..7a625eaa7d 100644 --- a/ci-scripts/main.py +++ b/ci-scripts/main.py @@ -212,13 +212,17 @@ def ExecuteActionWithParam(action): elif action == 'Initialize_UE' or action == 'Attach_UE' or action == 'Detach_UE' or action == 'Terminate_UE' or action == 'CheckStatusUE' or action == 'DataEnable_UE' or action == 'DataDisable_UE': CiTestObj.ue_ids = test.findtext('id').split(' ') - if test.findtext('nodes'): - CiTestObj.nodes = test.findtext('nodes').split(' ') - if len(CiTestObj.ue_ids) != len(CiTestObj.nodes): - logging.error('Number of Nodes are not equal to the total number of UEs') - sys.exit("Mismatch in number of Nodes and UIs") + if force_local: + # Change all execution targets to localhost + CiTestObj.nodes = ['localhost'] * len(CiTestObj.ue_ids) else: - CiTestObj.nodes = [None] * len(CiTestObj.ue_ids) + if test.findtext('nodes'): + CiTestObj.nodes = test.findtext('nodes').split(' ') + if len(CiTestObj.ue_ids) != len(CiTestObj.nodes): + logging.error('Number of Nodes are not equal to the total number of UEs') + sys.exit("Mismatch in number of Nodes and UIs") + else: + CiTestObj.nodes = [None] * len(CiTestObj.ue_ids) if action == 'Initialize_UE': success = CiTestObj.InitializeUE(HTML) elif action == 'Attach_UE': @@ -238,13 +242,17 @@ def ExecuteActionWithParam(action): CiTestObj.ping_args = test.findtext('ping_args') CiTestObj.ping_packetloss_threshold = test.findtext('ping_packetloss_threshold') CiTestObj.ue_ids = test.findtext('id').split(' ') - if test.findtext('nodes'): - CiTestObj.nodes = test.findtext('nodes').split(' ') - if len(CiTestObj.ue_ids) != len(CiTestObj.nodes): - logging.error('Number of Nodes are not equal to the total number of UEs') - sys.exit("Mismatch in number of Nodes and UIs") + if force_local: + # Change all execution targets to localhost + CiTestObj.nodes = ['localhost'] * len(CiTestObj.ue_ids) else: - CiTestObj.nodes = [None] * len(CiTestObj.ue_ids) + if test.findtext('nodes'): + CiTestObj.nodes = test.findtext('nodes').split(' ') + if len(CiTestObj.ue_ids) != len(CiTestObj.nodes): + logging.error('Number of Nodes are not equal to the total number of UEs') + sys.exit("Mismatch in number of Nodes and UIs") + else: + CiTestObj.nodes = [None] * len(CiTestObj.ue_ids) ping_rttavg_threshold = test.findtext('ping_rttavg_threshold') or '' success = CiTestObj.Ping(HTML,EPC,CONTAINERS) @@ -252,15 +260,19 @@ def ExecuteActionWithParam(action): CiTestObj.iperf_args = test.findtext('iperf_args') CiTestObj.ue_ids = test.findtext('id').split(' ') CiTestObj.svr_id = test.findtext('svr_id') or None - if test.findtext('nodes'): - CiTestObj.nodes = test.findtext('nodes').split(' ') - if len(CiTestObj.ue_ids) != len(CiTestObj.nodes): - logging.error('Number of Nodes are not equal to the total number of UEs') - sys.exit("Mismatch in number of Nodes and UIs") + if force_local: + # Change all execution targets to localhost + CiTestObj.nodes = ['localhost'] * len(CiTestObj.ue_ids) else: - CiTestObj.nodes = [None] * len(CiTestObj.ue_ids) + if test.findtext('nodes'): + CiTestObj.nodes = test.findtext('nodes').split(' ') + if len(CiTestObj.ue_ids) != len(CiTestObj.nodes): + logging.error('Number of Nodes are not equal to the total number of UEs') + sys.exit("Mismatch in number of Nodes and UIs") + else: + CiTestObj.nodes = [None] * len(CiTestObj.ue_ids) if test.findtext('svr_node'): - CiTestObj.svr_node = test.findtext('svr_node') + CiTestObj.svr_node = test.findtext('svr_node') if not force_local else 'localhost' CiTestObj.iperf_packetloss_threshold = test.findtext('iperf_packetloss_threshold') CiTestObj.iperf_bitrate_threshold = test.findtext('iperf_bitrate_threshold') or '90' CiTestObj.iperf_profile = test.findtext('iperf_profile') or 'balanced' @@ -356,6 +368,9 @@ def ExecuteActionWithParam(action): elif action == 'Undeploy_Object': success = CONTAINERS.UndeployObject(HTML, RAN) elif action == 'Create_Workspace': + if force_local: + # Do not create a working directory when running locally. Current repo directory will be used + return True success = CONTAINERS.Create_Workspace(HTML) elif action == 'Run_Physim': @@ -375,6 +390,9 @@ def ExecuteActionWithParam(action): success = CONTAINERS.Push_Image_to_Local_Registry(HTML, svr_id) elif action == 'Pull_Local_Registry' or action == 'Clean_Test_Server_Images': + if force_local: + # Do not pull or remove images when running locally. User is supposed to handle image creation & cleanup + return True svr_id = test.findtext('svr_id') images = test.findtext('images').split() # hack: for FlexRIC, we need to overwrite the tag to use @@ -463,7 +481,9 @@ CLUSTER = cls_cluster.Cluster() #----------------------------------------------------------- import args_parse -py_param_file_present, py_params, mode = args_parse.ArgsParse(sys.argv,CiTestObj,RAN,HTML,EPC,CONTAINERS,HELP,SCA,PHYSIM,CLUSTER) +# Force local execution, move all execution targets to localhost +force_local = False +py_param_file_present, py_params, mode, force_local = args_parse.ArgsParse(sys.argv,CiTestObj,RAN,HTML,EPC,CONTAINERS,HELP,SCA,PHYSIM,CLUSTER) diff --git a/ci-scripts/run_locally.sh b/ci-scripts/run_locally.sh new file mode 100755 index 0000000000..c8e9a289f0 --- /dev/null +++ b/ci-scripts/run_locally.sh @@ -0,0 +1,35 @@ +#!/bin/bash + +set -e +SHORT_COMMIT_SHA=$(git rev-parse --short=8 HEAD) +COMMIT_SHA=$(git rev-parse HEAD) +CURRENT_BRANCH=$(git rev-parse --abbrev-ref HEAD) +REPO_PATH=$(dirname $(realpath $0))/../ +TESTCASE=$1 + +if [ $# -eq 0 ] + then + echo "Provide a testcase as an argument" + exit 1 +fi + +# The script assumes you've build the following images: +# +# docker build . -f docker/Dockerfile.gNB.ubuntu22 -t oai-gnb +# docker build . -f docker/Dockerfile.nrUE.ubuntu22 -t oai-nr-ue +# +# The images above depend on the following images: +# +# docker build . -f docker/Dockerfile.build.ubuntu22 -t ran-build +# dokcer build . -f docker/Dockerfile.base.ubuntu22 -t ran-base + +docker tag oai-nr-ue oai-ci/oai-nr-ue:develop-${SHORT_COMMIT_SHA} +docker tag oai-gnb oai-ci/oai-gnb:develop-${SHORT_COMMIT_SHA} + +python3 main.py --mode=TesteNB --ranRepository=NONE --ranBranch=${CURRENT_BRANCH} \ + --ranCommitID=${COMMIT_SHA} --ranAllowMerge=false \ + --ranTargetBranch=NONE --eNBIPAddress=NONE --eNBUserName=NONE --eNBPassword=NONE \ + --eNBSourceCodePath=NONE --EPCIPAddress=NONE --EPCType=OAI --eNBPassword=NONE \ + --eNBSourceCodePath=${REPO_PATH} --EPCIPAddress=NONE \ + --EPCUserName=NONE --EPCPassword=NONE --EPCSourceCodePath=NONE \ + --XMLTestFile=xml_files/${TESTCASE} --local diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index 229c93b043..f58880b04a 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -3,7 +3,7 @@ add_subdirectory(config/yaml) configure_file(oai_version.h.in oai_version.h @ONLY) -add_library(instrumentation INTERFACE instrumentation.h) +add_library(instrumentation INTERFACE) target_include_directories(instrumentation INTERFACE .) if (TRACY_ENABLE) target_link_libraries(instrumentation INTERFACE Tracy::TracyClient) diff --git a/executables/softmodem-common.c b/executables/softmodem-common.c index 9f39612733..2fed359d82 100644 --- a/executables/softmodem-common.c +++ b/executables/softmodem-common.c @@ -105,8 +105,9 @@ void get_common_options(configmodule_interface_t *cfg, uint32_t execmask) paramdef_t cmdline_params[] = CMDLINE_PARAMS_DESC; checkedparam_t cmdline_CheckParams[] = CMDLINE_PARAMS_CHECK_DESC; + static_assert(sizeofArray(cmdline_params) == sizeofArray(cmdline_CheckParams), + "cmdline_params and cmdline_CheckParams should have the same size"); int numparams = sizeofArray(cmdline_params); - AssertFatal(numparams == sizeofArray(cmdline_CheckParams), "Error in arrays size (%d!=%lu)\n", numparams, sizeofArray(cmdline_CheckParams)); config_set_checkfunctions(cmdline_params, cmdline_CheckParams, numparams); config_get(cfg, cmdline_params, numparams, NULL); nfapi_index = config_paramidx_fromname(cmdline_params, numparams, "nfapi"); @@ -115,9 +116,10 @@ void get_common_options(configmodule_interface_t *cfg, uint32_t execmask) paramdef_t cmdline_logparams[] =CMDLINE_LOGPARAMS_DESC ; checkedparam_t cmdline_log_CheckParams[] = CMDLINE_LOGPARAMS_CHECK_DESC; - int numlogparams = sizeofArray(cmdline_logparams); - AssertFatal(numlogparams == sizeofArray(cmdline_log_CheckParams), "Error in arrays size (%d!=%lu)\n", numlogparams, sizeofArray(cmdline_log_CheckParams)); + static_assert(sizeofArray(cmdline_logparams) == sizeofArray(cmdline_log_CheckParams), + "cmdline_logparams and cmdline_log_CheckParams should have the same size"); + int numlogparams = sizeofArray(cmdline_logparams); config_set_checkfunctions(cmdline_logparams, cmdline_log_CheckParams, numlogparams); config_get(cfg, cmdline_logparams, numlogparams, NULL); diff --git a/openair1/PHY/MODULATION/modulation_common.h b/openair1/PHY/MODULATION/modulation_common.h index a131cdae63..df521b10da 100644 --- a/openair1/PHY/MODULATION/modulation_common.h +++ b/openair1/PHY/MODULATION/modulation_common.h @@ -38,7 +38,7 @@ @param nb_prefix_samples The number of prefix/suffix/zero samples @param etype Type of extension (CYCLIC_PREFIX,CYCLIC_SUFFIX,ZEROS) */ -void PHY_ofdm_mod(int *input, +void PHY_ofdm_mod(const int *input, int *output, int fftsize, unsigned char nb_symbols, diff --git a/openair1/PHY/MODULATION/ofdm_mod.c b/openair1/PHY/MODULATION/ofdm_mod.c index de19d30819..e25f7cd7f2 100644 --- a/openair1/PHY/MODULATION/ofdm_mod.c +++ b/openair1/PHY/MODULATION/ofdm_mod.c @@ -40,6 +40,9 @@ This section deals with basic functions for OFDM Modulation. #include "PHY/LTE_TRANSPORT/transport_common_proto.h" //#define DEBUG_OFDM_MOD +// Use 64-byte alignment for IDFT output buffer to ensure no +// runtime error in case IDFT implementation uses AVX-512. +#define IDFT_OUTPUT_BUFFER_ALIGNMENT 64 void normal_prefix_mod(int32_t *txdataF,int32_t *txdata,uint8_t nsymb,LTE_DL_FRAME_PARMS *frame_parms) { @@ -122,109 +125,90 @@ void nr_normal_prefix_mod(c16_t *txdataF, c16_t *txdata, uint8_t nsymb, const NR } -void PHY_ofdm_mod(int *input, /// pointer to complex input - int *output, /// pointer to complex output - int fftsize, /// FFT_SIZE - unsigned char nb_symbols, /// number of OFDM symbols - unsigned short nb_prefix_samples, /// cyclic prefix length - Extension_t etype /// type of extension - ) +void PHY_ofdm_mod(const int *input, /// pointer to complex input + int *output, /// pointer to complex output + int fftsize, /// FFT_SIZE + unsigned char nb_symbols, /// number of OFDM symbols + unsigned short nb_prefix_samples, /// cyclic prefix length + Extension_t etype /// type of extension +) { + if (nb_symbols == 0) + return; - if(nb_symbols == 0) return; - - int16_t temp[2*2*6144*4] __attribute__((aligned(32))); - int i,j; - - volatile int *output_ptr=(int*)0; - - int *temp_ptr=(int*)0; idft_size_idx_t idft_size = get_idft(fftsize); #ifdef DEBUG_OFDM_MOD printf("[PHY] OFDM mod (size %d,prefix %d) Symbols %d, input %p, output %p\n", - fftsize,nb_prefix_samples,nb_symbols,input,output); + fftsize, + nb_prefix_samples, + nb_symbols, + input, + output); #endif - - - for (i=0; i %p)\n",i,nb_symbols,i*fftsize+(i*nb_prefix_samples),input,&input[i*fftsize],&output[(i*fftsize) + ((i)*nb_prefix_samples)]); + printf("[PHY] symbol %d/%d offset %d (%p,%p -> %p)\n", + i, + nb_symbols, + i * fftsize + (i * nb_prefix_samples), + input, + &input[i * fftsize], + &output[(i * fftsize) + ((i)*nb_prefix_samples)]); #endif // on AVX2 need 256-bit alignment - idft(idft_size, (int16_t *)&input[i * fftsize], (int16_t *)temp, 1); // Copy to frame buffer with Cyclic Extension // Note: will have to adjust for synchronization offset! switch (etype) { - case CYCLIC_PREFIX: - output_ptr = &output[(i*fftsize) + ((1+i)*nb_prefix_samples)]; - temp_ptr = (int *)temp; - - - // msg("Doing cyclic prefix method\n"); - - { - memcpy((void*)output_ptr,(void*)temp_ptr,fftsize<<2); - } - memcpy((void*)&output_ptr[-nb_prefix_samples],(void*)&output_ptr[fftsize-nb_prefix_samples],nb_prefix_samples<<2); - break; - - case CYCLIC_SUFFIX: - - - output_ptr = &output[(i*fftsize)+ (i*nb_prefix_samples)]; - - temp_ptr = (int *)temp; - - // msg("Doing cyclic suffix method\n"); - - for (j=0; j>x; for (int aatx = 0; aatx < nrOfLayers; aatx++) { for (int aarx = 0; aarx < frame_parms->nb_antennas_rx; aarx++) { - //clear average level - avg128U = simde_mm_setzero_si128(); - ul_ch128 = (simde__m128i *)&ul_ch_estimates_ext[aatx * frame_parms->nb_antennas_rx + aarx][symbol * len]; + simde__m128i *ul_ch128 = (simde__m128i *)&ul_ch_estimates_ext[aatx * frame_parms->nb_antennas_rx + aarx][symbol * len]; - for (int i = 0; i < len >> 2; i++) { - avg128U = simde_mm_add_epi32(avg128U, simde_mm_srai_epi32(simde_mm_madd_epi16(ul_ch128[i], ul_ch128[i]), x)); - } - - int32_t *avg32i = (int32_t *)&avg128U; - int64_t avg64 = (int64_t)avg32i[0] + avg32i[1] + avg32i[2] + avg32i[3]; - avg[aatx * frame_parms->nb_antennas_rx + aarx] = avg64 / y; + //compute average level + avg[aatx * frame_parms->nb_antennas_rx + aarx] = simde_mm_average(ul_ch128, len, x, y); + //LOG_D(PHY, "Channel level: %d\n", avg[aatx * frame_parms->nb_antennas_rx + aarx]); } } diff --git a/openair1/PHY/NR_UE_TRANSPORT/dci_nr.c b/openair1/PHY/NR_UE_TRANSPORT/dci_nr.c index 15bbc4e626..36bd5a1ce7 100644 --- a/openair1/PHY/NR_UE_TRANSPORT/dci_nr.c +++ b/openair1/PHY/NR_UE_TRANSPORT/dci_nr.c @@ -226,30 +226,12 @@ void nr_pdcch_channel_level(int32_t rx_size, int nb_rb) { for (int aarx = 0; aarx < frame_parms->nb_antennas_rx; aarx++) { - //clear average level - simde__m128i avg128P = simde_mm_setzero_si128(); + simde__m128i *dl_ch128 = (simde__m128i *)&dl_ch_estimates_ext[aarx][symbol * nb_rb * 12]; - for (int rb = 0; rb < (nb_rb * 3) >> 2; rb++) { - avg128P = simde_mm_add_epi32(avg128P,simde_mm_madd_epi16(dl_ch128[0],dl_ch128[0])); - avg128P = simde_mm_add_epi32(avg128P,simde_mm_madd_epi16(dl_ch128[1],dl_ch128[1])); - avg128P = simde_mm_add_epi32(avg128P,simde_mm_madd_epi16(dl_ch128[2],dl_ch128[2])); - // for (int i=0;i<24;i+=2) printf("pdcch channel re %d (%d,%d)\n",(rb*12)+(i>>1),((int16_t*)dl_ch128)[i],((int16_t*)dl_ch128)[i+1]); - dl_ch128+=3; - /* - if (rb==0) { - print_shorts("dl_ch128",&dl_ch128[0]); - print_shorts("dl_ch128",&dl_ch128[1]); - print_shorts("dl_ch128",&dl_ch128[2]); - } - */ - } - - DevAssert(nb_rb); - avg[aarx] = 0; - for (int i = 0; i < 4; i++) - avg[aarx] += ((int32_t *)&avg128P)[i] / (nb_rb * 9); - LOG_DDD("Channel level : %d\n",avg[aarx]); + //compute average level + avg[aarx] = simde_mm_average(dl_ch128, nb_rb * 12, 0, nb_rb * 12); + //LOG_DDD("Channel level : %d\n", avg[aarx]); } } diff --git a/openair1/PHY/NR_UE_TRANSPORT/nr_dlsch_demodulation.c b/openair1/PHY/NR_UE_TRANSPORT/nr_dlsch_demodulation.c index c0019fb3ec..217a370054 100644 --- a/openair1/PHY/NR_UE_TRANSPORT/nr_dlsch_demodulation.c +++ b/openair1/PHY/NR_UE_TRANSPORT/nr_dlsch_demodulation.c @@ -1098,28 +1098,20 @@ void nr_dlsch_channel_level(uint32_t rx_size_symbol, int32_t avg[MAX_ANT][MAX_ANT], uint32_t len) { - simde__m128i *dl_ch128, avg128D; - //nb_rb*nre = y * 2^x int16_t x = factor2(len); int16_t y = (len) >> x; + uint32_t nb_rb_0 = len / NR_NB_SC_PER_RB + ((len % NR_NB_SC_PER_RB) ? 1 : 0); LOG_D(NR_PHY, "nb_rb_0 %d len %d = %d * 2^(%d)\n", nb_rb_0, len, y, x); + for (int aatx = 0; aatx < n_tx; aatx++) { for (int aarx = 0; aarx < n_rx; aarx++) { - //clear average level - avg128D = simde_mm_setzero_si128(); - dl_ch128 = (simde__m128i *)dl_ch_estimates_ext[(aatx * n_rx) + aarx]; + simde__m128i *dl_ch128 = (simde__m128i *)dl_ch_estimates_ext[(aatx * n_rx) + aarx]; - for (int rb = 0; rb < nb_rb_0; rb++) { - avg128D = simde_mm_add_epi32(avg128D,simde_mm_srai_epi32(simde_mm_madd_epi16(dl_ch128[0],dl_ch128[0]),x)); - avg128D = simde_mm_add_epi32(avg128D,simde_mm_srai_epi32(simde_mm_madd_epi16(dl_ch128[1],dl_ch128[1]),x)); - avg128D = simde_mm_add_epi32(avg128D,simde_mm_srai_epi32(simde_mm_madd_epi16(dl_ch128[2],dl_ch128[2]),x)); - dl_ch128+=3; - } - int32_t *tmp = (int32_t *)&avg128D; - avg[aatx][aarx] = ((int64_t)tmp[0] + tmp[1] + tmp[2] + tmp[3]) / y; - LOG_D(PHY, "Channel level: %d\n", avg[aatx][aarx]); + //compute average level + avg[aatx][aarx] = simde_mm_average(dl_ch128, nb_rb_0 * 12, x, y); + //LOG_D(PHY, "Channel level: %d\n", avg[aatx][aarx]); } } } diff --git a/openair1/PHY/NR_UE_TRANSPORT/nr_pbch.c b/openair1/PHY/NR_UE_TRANSPORT/nr_pbch.c index 5dbab85f12..229191269f 100644 --- a/openair1/PHY/NR_UE_TRANSPORT/nr_pbch.c +++ b/openair1/PHY/NR_UE_TRANSPORT/nr_pbch.c @@ -208,31 +208,14 @@ int nr_pbch_channel_level(struct complex16 dl_ch_estimates_ext[][PBCH_MAX_RE_PER const NR_DL_FRAME_PARMS *frame_parms, int nb_re) { - int16_t nb_rb=nb_re/12; - simde__m128i avg128; - simde__m128i *dl_ch128; - int avg1=0,avg2=0; + int32_t avg2 = 0; for (int aarx=0; aarxnb_antennas_rx; aarx++) { - //clear average level - avg128 = simde_mm_setzero_si128(); - dl_ch128=(simde__m128i *)dl_ch_estimates_ext[aarx]; - for (int rb=0; rbavg2) avg2 = avg1; diff --git a/openair1/PHY/sse_intrin.h b/openair1/PHY/sse_intrin.h index 655c38982e..d6c3157ce8 100644 --- a/openair1/PHY/sse_intrin.h +++ b/openair1/PHY/sse_intrin.h @@ -49,13 +49,6 @@ #define SSE_INTRIN_H -#include -#include -#include -#include -#include -#include -#include #include #include #if defined(__x86_64) || defined(__i386__) @@ -81,31 +74,101 @@ * OAI specific */ -static const short minusConjug128[8]__attribute__((aligned(16))) = {-1,1,-1,1,-1,1,-1,1}; -static inline simde__m128i mulByConjugate128(simde__m128i *a, simde__m128i *b, int8_t output_shift) { - - simde__m128i realPart = simde_mm_madd_epi16(*a,*b); - realPart = simde_mm_srai_epi32(realPart,output_shift); - simde__m128i imagPart = simde_mm_shufflelo_epi16(*b, SIMDE_MM_SHUFFLE(2,3,0,1)); - imagPart = simde_mm_shufflehi_epi16(imagPart, SIMDE_MM_SHUFFLE(2,3,0,1)); - imagPart = simde_mm_sign_epi16(imagPart,*(simde__m128i *)minusConjug128); - imagPart = simde_mm_madd_epi16(imagPart,*a); - imagPart = simde_mm_srai_epi32(imagPart,output_shift); - simde__m128i lowPart = simde_mm_unpacklo_epi32(realPart,imagPart); - simde__m128i highPart = simde_mm_unpackhi_epi32(realPart,imagPart); - return ( simde_mm_packs_epi32(lowPart,highPart)); +static const short minusConjug128[8] __attribute__((aligned(16))) = {-1, 1, -1, 1, -1, 1, -1, 1}; +static inline simde__m128i mulByConjugate128(simde__m128i *a, simde__m128i *b, int8_t output_shift) +{ + simde__m128i realPart = simde_mm_madd_epi16(*a, *b); + realPart = simde_mm_srai_epi32(realPart, output_shift); + simde__m128i imagPart = simde_mm_shufflelo_epi16(*b, SIMDE_MM_SHUFFLE(2, 3, 0, 1)); + imagPart = simde_mm_shufflehi_epi16(imagPart, SIMDE_MM_SHUFFLE(2, 3, 0, 1)); + imagPart = simde_mm_sign_epi16(imagPart, *(simde__m128i *)minusConjug128); + imagPart = simde_mm_madd_epi16(imagPart, *a); + imagPart = simde_mm_srai_epi32(imagPart, output_shift); + simde__m128i lowPart = simde_mm_unpacklo_epi32(realPart, imagPart); + simde__m128i highPart = simde_mm_unpackhi_epi32(realPart, imagPart); + return (simde_mm_packs_epi32(lowPart, highPart)); } -#define displaySamples128(vect) {\ - simde__m128i x=vect; \ - printf("vector: %s = (%hd,%hd) (%hd,%hd) (%hd,%hd) (%hd,%hd)\n", #vect, \ - simde_mm_extract_epi16(x,0), \ - simde_mm_extract_epi16(x,1),\ - simde_mm_extract_epi16(x,2),\ - simde_mm_extract_epi16(x,3),\ - simde_mm_extract_epi16(x,4),\ - simde_mm_extract_epi16(x,5),\ - simde_mm_extract_epi16(x,6),\ - simde_mm_extract_epi16(x,7));\ +#define displaySamples128(vect) \ + { \ + simde__m128i x = vect; \ + printf("vector: %s = (%hd,%hd) (%hd,%hd) (%hd,%hd) (%hd,%hd)\n", \ + #vect, \ + simde_mm_extract_epi16(x, 0), \ + simde_mm_extract_epi16(x, 1), \ + simde_mm_extract_epi16(x, 2), \ + simde_mm_extract_epi16(x, 3), \ + simde_mm_extract_epi16(x, 4), \ + simde_mm_extract_epi16(x, 5), \ + simde_mm_extract_epi16(x, 6), \ + simde_mm_extract_epi16(x, 7)); \ } + +__attribute__((always_inline)) static inline int64_t simde_mm_average_sse(simde__m128i *a, int length, int shift) +{ + // compute average level with shift (64-bit verstion) + simde__m128i avg128 = simde_mm_setzero_si128(); + for (int i = 0; i < length >> 2; i++) { + const simde__m128i in1 = a[i]; + avg128 = simde_mm_add_epi32(avg128, simde_mm_srai_epi32(simde_mm_madd_epi16(in1, in1), shift)); + } + + // Horizontally add pairs + // 1st [A + C, B + D] + simde__m128i sum_pairs = simde_mm_add_epi64(simde_mm_unpacklo_epi32(avg128, simde_mm_setzero_si128()), // [A, B] → [A, 0, B, 0] + simde_mm_unpackhi_epi32(avg128, simde_mm_setzero_si128()) // [C, D] → [C, 0, D, 0] + ); + + // 2nd [A + B + C + D, ...] + simde__m128i total_sum = simde_mm_add_epi64(sum_pairs, simde_mm_shuffle_epi32(sum_pairs, SIMDE_MM_SHUFFLE(1, 0, 3, 2))); + + // Extract horizontal sum as a scalar int64_t result + return simde_mm_cvtsi128_si64(total_sum); +} + +__attribute__((always_inline)) static inline int64_t simde_mm_average_avx2(simde__m256i *a, int length, int shift) +{ + simde__m256i avg256 = simde_mm256_setzero_si256(); + for (int i = 0; i < length >> 3; i++) { + const simde__m256i in1 = simde_mm256_loadu_si256(&a[i]); // unaligned load + avg256 = simde_mm256_add_epi32(avg256, simde_mm256_srai_epi32(simde_mm256_madd_epi16(in1, in1), shift)); + } + + // Split the 256-bit vector into two 128-bit halves and convert to 64-bit + // [A + E, B + F, C + G, D + H] + simde__m256i sum_pairs = simde_mm256_add_epi64( + simde_mm256_cvtepi32_epi64(simde_mm256_castsi256_si128(avg256)), // [A, B, C, D] → [A, 0, B, 0, C, 0, D, 0] + simde_mm256_cvtepi32_epi64(simde_mm256_extracti128_si256(avg256, 1)) // [E, F, G, H] → [E, 0, F, 0, G, 0, H, 0] + ); + + // Horizontal sum within the 256-bit vector + // [A + E + B + F, C + G + D + H] + simde__m128i total_sum = simde_mm_add_epi64(simde_mm256_castsi256_si128(sum_pairs), simde_mm256_extracti128_si256(sum_pairs, 1)); + + // [A + E + B + F + C + G + D + H, ...] + total_sum = simde_mm_add_epi64(total_sum, simde_mm_shuffle_epi32(total_sum, SIMDE_MM_SHUFFLE(1, 0, 3, 2))); + + // Extract horizontal sum as a scalar int64_t result + return simde_mm_cvtsi128_si64(total_sum); +} + +__attribute__((always_inline)) static inline int32_t simde_mm_average(simde__m128i *a, int length, int shift, int16_t scale) +{ + int64_t avg = 0; + +#if defined(__x86_64__) || defined(__i386__) + if (__builtin_cpu_supports("avx2")) { + avg += simde_mm_average_avx2((simde__m256i *)a, length, shift); + + // tail processing by SSE + a += ((length & ~7) >> 2); + length -= (length & ~7); + } +#endif + + avg += simde_mm_average_sse(a, length, shift); + + return (uint32_t)(avg / scale); +} + #endif // SSE_INTRIN_H diff --git a/openair2/E1AP/e1ap_setup.c b/openair2/E1AP/e1ap_setup.c index 4ec94bc9b5..fc59a881fa 100644 --- a/openair2/E1AP/e1ap_setup.c +++ b/openair2/E1AP/e1ap_setup.c @@ -94,6 +94,8 @@ MessageDef *RCconfig_NR_CU_E1(const E1_t *entity) paramlist_def_t PLMNParamList = {GNB_CONFIG_STRING_PLMN_LIST, NULL, 0}; /* map parameter checking array instances to parameter definition array instances */ checkedparam_t config_check_PLMNParams[] = PLMNPARAMS_CHECK; + static_assert(sizeofArray(config_check_PLMNParams) == sizeofArray(PLMNParams), + "config_check_PLMNParams and PLMNParams should have the same size"); for (int I = 0; I < sizeofArray(PLMNParams); ++I) PLMNParams[I].chkPptr = &(config_check_PLMNParams[I]); diff --git a/openair2/ENB_APP/enb_config.c b/openair2/ENB_APP/enb_config.c index 9d0d81cfc8..6daae2907d 100644 --- a/openair2/ENB_APP/enb_config.c +++ b/openair2/ENB_APP/enb_config.c @@ -266,9 +266,13 @@ int RCconfig_RRC(uint32_t i, eNB_RRC_INST *rrc) { paramlist_def_t ENBParamList = {ENB_CONFIG_STRING_ENB_LIST,NULL,0}; checkedparam_t config_check_CCparams[] = CCPARAMS_CHECK; paramdef_t CCsParams[] = CCPARAMS_DESC(ccparams_lte); + static_assert(sizeofArray(config_check_CCparams) == sizeofArray(CCsParams), + "config_check_CCparams and CCsParams should have the same size"); paramlist_def_t CCsParamList = {ENB_CONFIG_STRING_COMPONENT_CARRIERS,NULL,0}; paramdef_t eMTCParams[] = EMTCPARAMS_DESC((&eMTCconfig)); checkedparam_t config_check_eMTCparams[] = EMTCPARAMS_CHECK; + static_assert(sizeofArray(config_check_eMTCparams) == sizeofArray(eMTCParams), + "config_check_eMTCparams and eMTCParamsCCsParams should have the same size"); srb1_params_t srb1_params; memset((void *)&srb1_params,0,sizeof(srb1_params_t)); paramdef_t SRB1Params[] = SRB1PARAMS_DESC(srb1_params); @@ -320,6 +324,8 @@ int RCconfig_RRC(uint32_t i, eNB_RRC_INST *rrc) { paramlist_def_t PLMNParamList = {ENB_CONFIG_STRING_PLMN_LIST, NULL, 0}; /* map parameter checking array instances to parameter definition array instances */ checkedparam_t config_check_PLMNParams [] = PLMNPARAMS_CHECK; + static_assert(sizeofArray(config_check_PLMNParams) == sizeofArray(PLMNParams), + "config_check_PLMNParams and PLMNParams should have the same size"); for (int I = 0; I < sizeofArray(PLMNParams); ++I) PLMNParams[I].chkPptr = &(config_check_PLMNParams[I]); @@ -1910,6 +1916,8 @@ int RCconfig_M2(MessageDef *msg_p, uint32_t i) { paramlist_def_t PLMNParamList = {ENB_CONFIG_STRING_PLMN_LIST, NULL, 0}; /* map parameter checking array instances to parameter definition array instances */ checkedparam_t config_check_PLMNParams [] = PLMNPARAMS_CHECK; + static_assert(sizeofArray(config_check_PLMNParams) == sizeofArray(PLMNParams), + "config_check_PLMNParams and PLMNParams should have the same size"); for (int I = 0; I < sizeofArray(PLMNParams); ++I) PLMNParams[I].chkPptr = &(config_check_PLMNParams[I]); @@ -2159,6 +2167,8 @@ int RCconfig_S1( paramdef_t CCsParams[] = CCPARAMS_DESC(ccparams_lte); /* map parameter checking array instances to parameter definition array instances */ checkedparam_t config_check_CCparams[] = CCPARAMS_CHECK; + static_assert(sizeofArray(config_check_CCparams) == sizeofArray(CCsParams), + "config_check_CCparams and CCsParams should have the same size"); for (int I = 0; I < sizeofArray(CCsParams); I++) { CCsParams[I].chkPptr = &(config_check_CCparams[I]); @@ -2166,6 +2176,8 @@ int RCconfig_S1( /* map parameter checking array instances to parameter definition array instances */ checkedparam_t config_check_PLMNParams [] = PLMNPARAMS_CHECK; + static_assert(sizeofArray(config_check_PLMNParams) == sizeofArray(PLMNParams), + "config_check_PLMNParams and PLMNParams should have the same size"); for (int I = 0; I < sizeofArray(PLMNParams); ++I) { PLMNParams[I].chkPptr = &(config_check_PLMNParams[I]); @@ -2413,6 +2425,8 @@ int RCconfig_X2(MessageDef *msg_p, uint32_t i) { config_get(config_get_if(), ENBSParams, sizeofArray(ENBSParams), NULL); checkedparam_t config_check_CCparams[] = CCPARAMS_CHECK; paramdef_t CCsParams[] = CCPARAMS_DESC(ccparams_lte); + static_assert(sizeofArray(config_check_CCparams) == sizeofArray(CCsParams), + "config_check_CCparams and CCsParams should have the same size"); paramlist_def_t CCsParamList = {ENB_CONFIG_STRING_COMPONENT_CARRIERS, NULL, 0}; /* map parameter checking array instances to parameter definition array instances */ @@ -2450,6 +2464,8 @@ int RCconfig_X2(MessageDef *msg_p, uint32_t i) { paramlist_def_t PLMNParamList = {ENB_CONFIG_STRING_PLMN_LIST, NULL, 0}; /* map parameter checking array instances to parameter definition array instances */ checkedparam_t config_check_PLMNParams [] = PLMNPARAMS_CHECK; + static_assert(sizeofArray(config_check_PLMNParams) == sizeofArray(PLMNParams), + "config_check_PLMNParams and PLMNParams should have the same size"); for (int I = 0; I < sizeofArray(PLMNParams); ++I) PLMNParams[I].chkPptr = &(config_check_PLMNParams[I]); diff --git a/openair2/ENB_APP/enb_paramdef.h b/openair2/ENB_APP/enb_paramdef.h index 5fd4cdce70..7bc8f677d8 100644 --- a/openair2/ENB_APP/enb_paramdef.h +++ b/openair2/ENB_APP/enb_paramdef.h @@ -749,43 +749,6 @@ typedef struct ccparams_lte_s { { { NULL } } , \ { { NULL } } , \ { { NULL } } , \ - { { NULL } } , \ - { { NULL } } , \ - { { NULL } } , \ - { { NULL } } , \ - { { NULL } } , \ - { { NULL } } , \ - { { NULL } } , \ - { { NULL } } , \ - { { NULL } } , \ - { { NULL } } , \ - { { NULL } } , \ - { { NULL } } , \ - { { NULL } } , \ - { { NULL } } , \ - { { NULL } } , \ - { { NULL } } , \ - { { NULL } } , \ - { { NULL } } , \ - { { NULL } } , \ - { { NULL } } , \ - { { NULL } } , \ - { { NULL } } , \ - { { NULL } } , \ - { { NULL } } , \ - { { NULL } } , \ - { { NULL } } , \ - { { NULL } } , \ - { { NULL } } , \ - { { NULL } } , \ - { { NULL } } , \ - { { NULL } } , \ - { { NULL } } , \ - { { NULL } } , \ - { { NULL } } , \ - { { NULL } } , \ - { { NULL } } , \ - { { NULL } } \ } /*-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------*/ diff --git a/openair2/ENB_APP/enb_paramdef_emtc.h b/openair2/ENB_APP/enb_paramdef_emtc.h index 9d7bfb98e0..52f5b2a60f 100644 --- a/openair2/ENB_APP/enb_paramdef_emtc.h +++ b/openair2/ENB_APP/enb_paramdef_emtc.h @@ -351,6 +351,7 @@ typedef struct ccparams_eMTC_s { { {NULL}} , \ { {NULL}} , \ { {NULL}} , \ + { {NULL}} , \ } // clang-format on diff --git a/openair2/GNB_APP/gnb_config.c b/openair2/GNB_APP/gnb_config.c index 7555d1f5d0..3b8981353d 100644 --- a/openair2/GNB_APP/gnb_config.c +++ b/openair2/GNB_APP/gnb_config.c @@ -1118,6 +1118,8 @@ static int read_du_cell_info(configmodule_interface_t *cfg, paramdef_t PLMNParams[] = GNBPLMNPARAMS_DESC; /* map parameter checking array instances to parameter definition array instances */ checkedparam_t config_check_PLMNParams[] = PLMNPARAMS_CHECK; + static_assert(sizeofArray(config_check_PLMNParams) == sizeofArray(PLMNParams), + "config_check_PLMNParams and PLMNParams should have the same size"); for (int I = 0; I < sizeof(PLMNParams) / sizeof(paramdef_t); ++I) PLMNParams[I].chkPptr = &(config_check_PLMNParams[I]); paramlist_def_t PLMNParamList = {GNB_CONFIG_STRING_PLMN_LIST, NULL, 0}; @@ -1151,6 +1153,8 @@ static int read_du_cell_info(configmodule_interface_t *cfg, paramdef_t SNSSAIParams[] = GNBSNSSAIPARAMS_DESC; paramlist_def_t SNSSAIParamList = {GNB_CONFIG_STRING_SNSSAI_LIST, NULL, 0}; checkedparam_t config_check_SNSSAIParams[] = SNSSAIPARAMS_CHECK; + static_assert(sizeofArray(config_check_SNSSAIParams) == sizeofArray(SNSSAIParams), + "config_check_SNSSAIParams and SNSSAIParams should have the same size"); for (int J = 0; J < sizeofArray(SNSSAIParams); ++J) SNSSAIParams[J].chkPptr = &(config_check_SNSSAIParams[J]); char snssaistr[MAX_OPTNAME_SIZE * 2 + 8]; @@ -1288,6 +1292,8 @@ void RCconfig_nr_macrlc(configmodule_interface_t *cfg) paramdef_t GNBParams[] = GNBPARAMS_DESC; /* map parameter checking array instances to parameter definition array instances */ checkedparam_t config_check_GNBParams[] = GNBPARAMS_CHECK; + static_assert(sizeofArray(config_check_GNBParams) == sizeofArray(GNBParams), + "config_check_GNBParams and GNBParams should have the same size"); for (int i = 0; i < sizeofArray(GNBParams); ++i) GNBParams[i].chkPptr = &(config_check_GNBParams[i]); config_getlist(cfg, &GNBParamList, GNBParams, sizeofArray(GNBParams), NULL); @@ -1296,6 +1302,8 @@ void RCconfig_nr_macrlc(configmodule_interface_t *cfg) paramlist_def_t MacRLC_ParamList = {CONFIG_STRING_MACRLC_LIST, NULL, 0}; /* map parameter checking array instances to parameter definition array instances */ checkedparam_t config_check_MacRLCParams[] = MACRLCPARAMS_CHECK; + static_assert(sizeofArray(config_check_MacRLCParams) == sizeofArray(MacRLC_Params), + "config_check_MacRLCParams and MacRLC_Params should have the same size"); for (int i = 0; i < sizeofArray(MacRLC_Params); ++i) MacRLC_Params[i].chkPptr = &(config_check_MacRLCParams[i]); config_getlist(config_get_if(), &MacRLC_ParamList, MacRLC_Params, sizeofArray(MacRLC_Params), NULL); @@ -1906,6 +1914,8 @@ gNB_RRC_INST *RCconfig_NRRRC() paramlist_def_t PLMNParamList = {GNB_CONFIG_STRING_PLMN_LIST, NULL, 0}; /* map parameter checking array instances to parameter definition array instances */ checkedparam_t config_check_PLMNParams [] = PLMNPARAMS_CHECK; + static_assert(sizeofArray(config_check_PLMNParams) == sizeofArray(PLMNParams), + "config_check_PLMNParams and PLMNParams should have the same size"); for (int I = 0; I < sizeofArray(PLMNParams); ++I) PLMNParams[I].chkPptr = &(config_check_PLMNParams[I]); @@ -2013,7 +2023,11 @@ int RCconfig_NR_NG(MessageDef *msg_p, uint32_t i) { paramlist_def_t SNSSAIParamList = {GNB_CONFIG_STRING_SNSSAI_LIST, NULL, 0}; /* map parameter checking array instances to parameter definition array instances */ checkedparam_t config_check_PLMNParams [] = PLMNPARAMS_CHECK; + static_assert(sizeofArray(config_check_PLMNParams) == sizeofArray(PLMNParams), + "config_check_PLMNParams and PLMNParams should have the same size"); checkedparam_t config_check_SNSSAIParams [] = SNSSAIPARAMS_CHECK; + static_assert(sizeofArray(config_check_SNSSAIParams) == sizeofArray(SNSSAIParams), + "config_check_SNSSAIParams and SNSSAIParams should have the same size"); for (int I = 0; I < sizeofArray(PLMNParams); ++I) PLMNParams[I].chkPptr = &(config_check_PLMNParams[I]); @@ -2277,6 +2291,8 @@ int RCconfig_NR_X2(MessageDef *msg_p, uint32_t i) { paramlist_def_t PLMNParamList = {GNB_CONFIG_STRING_PLMN_LIST, NULL, 0}; /* map parameter checking array instances to parameter definition array instances */ checkedparam_t config_check_PLMNParams [] = PLMNPARAMS_CHECK; + static_assert(sizeofArray(config_check_PLMNParams) == sizeofArray(PLMNParams), + "config_check_PLMNParams and PLMNParams should have the same size"); for (int I = 0; I < sizeofArray(PLMNParams); ++I) PLMNParams[I].chkPptr = &(config_check_PLMNParams[I]); diff --git a/openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.c b/openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.c index 08cfd7303c..6c15ec2943 100644 --- a/openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.c +++ b/openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.c @@ -2822,6 +2822,7 @@ uint8_t compute_precoding_information(NR_PUSCH_Config_t *pusch_Config, dci_field_t srs_resource_indicator, nr_srs_feedback_t *srs_feedback, const uint8_t *nrOfLayers, + int *tpmi, uint32_t *val) { // It is only applicable to codebook based transmission. This field occupies 0 bits for non-codebook based @@ -2841,6 +2842,7 @@ uint8_t compute_precoding_information(NR_PUSCH_Config_t *pusch_Config, long max_rank = *pusch_Config->maxRank; long *ul_FullPowerTransmission = pusch_Config->ext1 ? pusch_Config->ext1->ul_FullPowerTransmission_r16 : NULL; long *codebookSubset = pusch_Config->codebookSubset; + int ul_tpmi = tpmi ? *tpmi : srs_feedback ? srs_feedback->tpmi : -1; if (pusch_antenna_ports == 2) { @@ -2854,21 +2856,21 @@ uint8_t compute_precoding_information(NR_PUSCH_Config_t *pusch_Config, if (ul_FullPowerTransmission && *ul_FullPowerTransmission == NR_PUSCH_Config__ext1__ul_FullPowerTransmission_r16_fullpowerMode1) { nbits = 2; if (val && srs_feedback) { - AssertFatal(srs_feedback->tpmi <= 2,"TPMI %d is invalid!\n", srs_feedback->tpmi); - *val = srs_feedback->tpmi; + AssertFatal(ul_tpmi <= 2,"TPMI %d is invalid!\n", ul_tpmi); + *val = ul_tpmi; } } else { if (codebookSubset && *codebookSubset == NR_PUSCH_Config__codebookSubset_nonCoherent) { nbits = 1; if (val && srs_feedback) { - AssertFatal(srs_feedback->tpmi <= 1,"TPMI %d is invalid!\n", srs_feedback->tpmi); - *val = srs_feedback->tpmi; + AssertFatal(ul_tpmi <= 1,"TPMI %d is invalid!\n", ul_tpmi); + *val = ul_tpmi; } } else { nbits = 3; if (val && srs_feedback) { - AssertFatal(srs_feedback->tpmi <= 5,"TPMI %d is invalid!\n", srs_feedback->tpmi); - *val = srs_feedback->tpmi; + AssertFatal(ul_tpmi <= 5,"TPMI %d is invalid!\n", ul_tpmi); + *val = ul_tpmi; } } } @@ -2881,25 +2883,25 @@ uint8_t compute_precoding_information(NR_PUSCH_Config_t *pusch_Config, if (ul_FullPowerTransmission && *ul_FullPowerTransmission == NR_PUSCH_Config__ext1__ul_FullPowerTransmission_r16_fullpowerMode1) { nbits = 2; if (val && srs_feedback) { - AssertFatal((*nrOfLayers==1 && srs_feedback->tpmi <= 2) || (*nrOfLayers==2 && srs_feedback->tpmi == 0), - "TPMI %d is invalid!\n", srs_feedback->tpmi); - *val = *nrOfLayers==1 ? table_7_3_1_1_2_4A_1layer[srs_feedback->tpmi] : 2; + AssertFatal((*nrOfLayers==1 && ul_tpmi <= 2) || (*nrOfLayers==2 && ul_tpmi == 0), + "TPMI %d is invalid!\n", ul_tpmi); + *val = *nrOfLayers==1 ? table_7_3_1_1_2_4A_1layer[ul_tpmi] : 2; } } else { if (codebookSubset && *codebookSubset == NR_PUSCH_Config__codebookSubset_nonCoherent) { nbits = 2; if (val && srs_feedback) { - AssertFatal((*nrOfLayers==1 && srs_feedback->tpmi <= 1) || (*nrOfLayers==2 && srs_feedback->tpmi == 0), - "TPMI %d is invalid!\n", srs_feedback->tpmi); - *val = *nrOfLayers==1 ? srs_feedback->tpmi : 2; + AssertFatal((*nrOfLayers==1 && ul_tpmi <= 1) || (*nrOfLayers==2 && ul_tpmi == 0), + "TPMI %d is invalid!\n", ul_tpmi); + *val = *nrOfLayers==1 ? ul_tpmi : 2; } } else { nbits = 4; if (val && srs_feedback) { - AssertFatal((*nrOfLayers==1 && srs_feedback->tpmi <= 5) || (*nrOfLayers==2 && srs_feedback->tpmi <= 2), - "TPMI %d is invalid!\n", srs_feedback->tpmi); - *val = *nrOfLayers==1 ? table_7_3_1_1_2_4_1layer_fullyAndPartialAndNonCoherent[srs_feedback->tpmi] : - table_7_3_1_1_2_4_2layers_fullyAndPartialAndNonCoherent[srs_feedback->tpmi]; + AssertFatal((*nrOfLayers==1 && ul_tpmi <= 5) || (*nrOfLayers==2 && ul_tpmi <= 2), + "TPMI %d is invalid!\n", ul_tpmi); + *val = *nrOfLayers==1 ? table_7_3_1_1_2_4_1layer_fullyAndPartialAndNonCoherent[ul_tpmi] : + table_7_3_1_1_2_4_2layers_fullyAndPartialAndNonCoherent[ul_tpmi]; } } } @@ -2918,36 +2920,36 @@ uint8_t compute_precoding_information(NR_PUSCH_Config_t *pusch_Config, if (codebookSubset && *codebookSubset == NR_PUSCH_Config__codebookSubset_nonCoherent) { nbits = 3; if (val && srs_feedback) { - AssertFatal(srs_feedback->tpmi <= 3 || srs_feedback->tpmi == 13, "TPMI %d is invalid!\n", srs_feedback->tpmi); + AssertFatal(ul_tpmi <= 3 || ul_tpmi == 13, "TPMI %d is invalid!\n", ul_tpmi); } } else { nbits = 4; if (val && srs_feedback) { - AssertFatal(srs_feedback->tpmi <= 15, "TPMI %d is invalid!\n", srs_feedback->tpmi); + AssertFatal(ul_tpmi <= 15, "TPMI %d is invalid!\n", ul_tpmi); } } if (val && srs_feedback) { - *val = table_7_3_1_1_2_3A[srs_feedback->tpmi]; + *val = table_7_3_1_1_2_3A[ul_tpmi]; } } else { if (codebookSubset && *codebookSubset == NR_PUSCH_Config__codebookSubset_nonCoherent) { nbits = 2; if (val && srs_feedback) { - AssertFatal(srs_feedback->tpmi <= 3, "TPMI %d is invalid!\n", srs_feedback->tpmi); + AssertFatal(ul_tpmi <= 3, "TPMI %d is invalid!\n", ul_tpmi); } } else if (codebookSubset && *codebookSubset == NR_PUSCH_Config__codebookSubset_partialAndNonCoherent) { nbits = 4; if (val && srs_feedback) { - AssertFatal(srs_feedback->tpmi <= 11, "TPMI %d is invalid!\n", srs_feedback->tpmi); + AssertFatal(ul_tpmi <= 11, "TPMI %d is invalid!\n", ul_tpmi); } } else { nbits = 5; if (val && srs_feedback) { - AssertFatal(srs_feedback->tpmi <= 27, "TPMI %d is invalid!\n", srs_feedback->tpmi); + AssertFatal(ul_tpmi <= 27, "TPMI %d is invalid!\n", ul_tpmi); } } if (val && srs_feedback) { - *val = srs_feedback->tpmi; + *val = ul_tpmi; } } } else { @@ -2965,48 +2967,48 @@ uint8_t compute_precoding_information(NR_PUSCH_Config_t *pusch_Config, if (codebookSubset && *codebookSubset == NR_PUSCH_Config__codebookSubset_nonCoherent) { nbits = 4; if (val && srs_feedback) { - AssertFatal((*nrOfLayers==1 && (srs_feedback->tpmi <= 3 || srs_feedback->tpmi==13)) || (*nrOfLayers==2 && srs_feedback->tpmi <= 6), - "TPMI %d is invalid!\n", srs_feedback->tpmi); + AssertFatal((*nrOfLayers==1 && (ul_tpmi <= 3 || ul_tpmi==13)) || (*nrOfLayers==2 && ul_tpmi <= 6), + "TPMI %d is invalid!\n", ul_tpmi); } } else { nbits = 5; if (val && srs_feedback) { - AssertFatal((*nrOfLayers==1 && srs_feedback->tpmi <= 15) || (*nrOfLayers==2 && srs_feedback->tpmi <= 13), - "TPMI %d is invalid!\n", srs_feedback->tpmi); + AssertFatal((*nrOfLayers==1 && ul_tpmi <= 15) || (*nrOfLayers==2 && ul_tpmi <= 13), + "TPMI %d is invalid!\n", ul_tpmi); } } if (val && srs_feedback) { - *val = *nrOfLayers==1 ? table_7_3_1_1_2_2A_1layer[srs_feedback->tpmi] : table_7_3_1_1_2_2A_2layers[srs_feedback->tpmi]; + *val = *nrOfLayers==1 ? table_7_3_1_1_2_2A_1layer[ul_tpmi] : table_7_3_1_1_2_2A_2layers[ul_tpmi]; } } else { if (codebookSubset && *codebookSubset == NR_PUSCH_Config__codebookSubset_nonCoherent) { nbits = 4; if (val && srs_feedback) { - AssertFatal((*nrOfLayers==1 && (srs_feedback->tpmi <= 3 || srs_feedback->tpmi == 13)) || (*nrOfLayers==2 && srs_feedback->tpmi <= 6) || - (*nrOfLayers==3 && srs_feedback->tpmi <= 1) || (*nrOfLayers==4 && srs_feedback->tpmi == 0), - "TPMI %d is invalid!\n", srs_feedback->tpmi); + AssertFatal((*nrOfLayers==1 && (ul_tpmi <= 3 || ul_tpmi == 13)) || (*nrOfLayers==2 && ul_tpmi <= 6) || + (*nrOfLayers==3 && ul_tpmi <= 1) || (*nrOfLayers==4 && ul_tpmi == 0), + "TPMI %d is invalid!\n", ul_tpmi); } } else { nbits = 6; if (val && srs_feedback) { - AssertFatal((*nrOfLayers==1 && srs_feedback->tpmi <= 15) || (*nrOfLayers==2 && srs_feedback->tpmi <= 13) || - (*nrOfLayers==3 && srs_feedback->tpmi <= 2) || (*nrOfLayers==4 && srs_feedback->tpmi <= 2), - "TPMI %d is invalid!\n", srs_feedback->tpmi); + AssertFatal((*nrOfLayers==1 && ul_tpmi <= 15) || (*nrOfLayers==2 && ul_tpmi <= 13) || + (*nrOfLayers==3 && ul_tpmi <= 2) || (*nrOfLayers==4 && ul_tpmi <= 2), + "TPMI %d is invalid!\n", ul_tpmi); } } if (val && srs_feedback) { switch (*nrOfLayers) { case 1: - *val = table_7_3_1_1_2_2B_1layer[srs_feedback->tpmi]; + *val = table_7_3_1_1_2_2B_1layer[ul_tpmi]; break; case 2: - *val = table_7_3_1_1_2_2B_2layers[srs_feedback->tpmi]; + *val = table_7_3_1_1_2_2B_2layers[ul_tpmi]; break; case 3: - *val = table_7_3_1_1_2_2B_3layers[srs_feedback->tpmi]; + *val = table_7_3_1_1_2_2B_3layers[ul_tpmi]; break; case 4: - *val = table_7_3_1_1_2_2B_4layers[srs_feedback->tpmi]; + *val = table_7_3_1_1_2_2B_4layers[ul_tpmi]; break; default: LOG_E(NR_MAC,"Number of layers %d is invalid!\n", *nrOfLayers); @@ -3017,38 +3019,38 @@ uint8_t compute_precoding_information(NR_PUSCH_Config_t *pusch_Config, if (codebookSubset && *codebookSubset == NR_PUSCH_Config__codebookSubset_nonCoherent) { nbits = 4; if (val && srs_feedback) { - AssertFatal((*nrOfLayers==1 && srs_feedback->tpmi <= 3) || (*nrOfLayers==2 && srs_feedback->tpmi <= 5) || - (*nrOfLayers==3 && srs_feedback->tpmi == 0) || (*nrOfLayers==4 && srs_feedback->tpmi == 0), - "TPMI %d is invalid!\n", srs_feedback->tpmi); + AssertFatal((*nrOfLayers==1 && ul_tpmi <= 3) || (*nrOfLayers==2 && ul_tpmi <= 5) || + (*nrOfLayers==3 && ul_tpmi == 0) || (*nrOfLayers==4 && ul_tpmi == 0), + "TPMI %d is invalid!\n", ul_tpmi); } } else if (codebookSubset && *codebookSubset == NR_PUSCH_Config__codebookSubset_partialAndNonCoherent) { nbits = 5; if (val && srs_feedback) { - AssertFatal((*nrOfLayers==1 && srs_feedback->tpmi <= 11) || (*nrOfLayers==2 && srs_feedback->tpmi <= 13) || - (*nrOfLayers==3 && srs_feedback->tpmi <= 2) || (*nrOfLayers==4 && srs_feedback->tpmi <= 2), - "TPMI %d is invalid!\n", srs_feedback->tpmi); + AssertFatal((*nrOfLayers==1 && ul_tpmi <= 11) || (*nrOfLayers==2 && ul_tpmi <= 13) || + (*nrOfLayers==3 && ul_tpmi <= 2) || (*nrOfLayers==4 && ul_tpmi <= 2), + "TPMI %d is invalid!\n", ul_tpmi); } } else { nbits = 6; if (val && srs_feedback) { - AssertFatal((*nrOfLayers==1 && srs_feedback->tpmi <= 28) || (*nrOfLayers==2 && srs_feedback->tpmi <= 22) || - (*nrOfLayers==3 && srs_feedback->tpmi <= 7) || (*nrOfLayers==4 && srs_feedback->tpmi <= 5), - "TPMI %d is invalid!\n", srs_feedback->tpmi); + AssertFatal((*nrOfLayers==1 && ul_tpmi <= 28) || (*nrOfLayers==2 && ul_tpmi <= 22) || + (*nrOfLayers==3 && ul_tpmi <= 7) || (*nrOfLayers==4 && ul_tpmi <= 5), + "TPMI %d is invalid!\n", ul_tpmi); } } if (val && srs_feedback) { switch (*nrOfLayers) { case 1: - *val = table_7_3_1_1_2_2_1layer[srs_feedback->tpmi]; + *val = table_7_3_1_1_2_2_1layer[ul_tpmi]; break; case 2: - *val = table_7_3_1_1_2_2_2layers[srs_feedback->tpmi]; + *val = table_7_3_1_1_2_2_2layers[ul_tpmi]; break; case 3: - *val = table_7_3_1_1_2_2_3layers[srs_feedback->tpmi]; + *val = table_7_3_1_1_2_2_3layers[ul_tpmi]; break; case 4: - *val = table_7_3_1_1_2_2_4layers[srs_feedback->tpmi]; + *val = table_7_3_1_1_2_2_4layers[ul_tpmi]; break; default: LOG_E(NR_MAC,"Number of layers %d is invalid!\n", *nrOfLayers); @@ -3299,7 +3301,8 @@ uint16_t nr_dci_size(const NR_UE_DL_BWP_t *DL_BWP, size += dci_pdu->srs_resource_indicator.nbits; LOG_D(NR_MAC, "dci_pdu->srs_resource_indicator.nbits %d\n", dci_pdu->srs_resource_indicator.nbits); // Precoding info and number of layers - dci_pdu->precoding_information.nbits = compute_precoding_information(pusch_Config, srs_config, dci_pdu->srs_resource_indicator, NULL, NULL, NULL); + dci_pdu->precoding_information.nbits = + compute_precoding_information(pusch_Config, srs_config, dci_pdu->srs_resource_indicator, NULL, NULL, NULL, NULL); size += dci_pdu->precoding_information.nbits; LOG_D(NR_MAC, "dci_pdu->precoding_informaiton.nbits=%d\n", dci_pdu->precoding_information.nbits); // Antenna ports diff --git a/openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.h b/openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.h index a4930aec47..64f8853e3d 100644 --- a/openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.h +++ b/openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.h @@ -79,6 +79,7 @@ uint8_t compute_precoding_information(NR_PUSCH_Config_t *pusch_Config, dci_field_t srs_resource_indicator, nr_srs_feedback_t *srs_feedback, const uint8_t *nrOfLayers, + int *tpmi, uint32_t *val); NR_PDSCH_TimeDomainResourceAllocationList_t *get_dl_tdalist(const NR_UE_DL_BWP_t *DL_BWP, diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c index c7faf78179..edf0f8c25e 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c @@ -894,6 +894,7 @@ static void nr_generate_Msg3_retransmission(module_id_t module_idP, pusch_pdu, &uldci_payload, NULL, + NULL, ra->Msg3_tda_id, ra->msg3_TPC, 1, // Not toggling NDI in msg3 retransmissions diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c index 1f9cb9c0c1..2c51ee5f21 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c @@ -782,6 +782,7 @@ void config_uldci(const NR_UE_ServingCell_Info_t *sc_info, const nfapi_nr_pusch_pdu_t *pusch_pdu, dci_pdu_rel15_t *dci_pdu_rel15, nr_srs_feedback_t *srs_feedback, + int *tpmi, int time_domain_assignment, uint8_t tpc, uint8_t ndi, @@ -830,6 +831,7 @@ void config_uldci(const NR_UE_ServingCell_Info_t *sc_info, dci_pdu_rel15->srs_resource_indicator, srs_feedback, &pusch_pdu->nrOfLayers, + tpmi, &dci_pdu_rel15->precoding_information.val); // antenna_ports.val = 0 for transform precoder is disabled, dmrs-Type=1, maxLength=1, Rank=1/2/3/4 diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c index 0900148073..7f849253da 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c @@ -2256,6 +2256,7 @@ void nr_schedule_ulsch(module_id_t module_id, frame_t frame, sub_frame_t slot, n uint16_t rnti = UE->rnti; sched_ctrl->SR = false; + int *tpmi = NULL; int8_t harq_id = sched_pusch->ul_harq_pid; if (harq_id < 0) { @@ -2297,6 +2298,7 @@ void nr_schedule_ulsch(module_id_t module_id, frame_t frame, sub_frame_t slot, n * retransmissions */ cur_harq->sched_pusch.time_domain_allocation = sched_pusch->time_domain_allocation; cur_harq->sched_pusch.nrOfLayers = sched_pusch->nrOfLayers; + cur_harq->sched_pusch.tpmi = sched_pusch->tpmi; sched_ctrl->sched_ul_bytes += sched_pusch->tb_size; UE->mac_stats.ul.total_rbs += sched_pusch->rbSize; @@ -2397,6 +2399,10 @@ void nr_schedule_ulsch(module_id_t module_id, frame_t frame, sub_frame_t slot, n else pusch_pdu->data_scrambling_id = *scc->physCellId; pusch_pdu->nrOfLayers = sched_pusch->nrOfLayers; + // If nrOfLayers is the same as in srs_feedback, we use the best TPMI, i.e. the one in srs_feedback. + // Otherwise, we use the valid TPMI that we saved in the first transmission. + if (pusch_pdu->nrOfLayers != (sched_ctrl->srs_feedback.ul_ri + 1)) + tpmi = &sched_pusch->tpmi; pusch_pdu->num_dmrs_cdm_grps_no_data = sched_pusch->dmrs_info.num_dmrs_cdm_grps_no_data; /* FAPI: DMRS */ @@ -2572,6 +2578,7 @@ void nr_schedule_ulsch(module_id_t module_id, frame_t frame, sub_frame_t slot, n pusch_pdu, &uldci_payload, &sched_ctrl->srs_feedback, + tpmi, sched_pusch->time_domain_allocation, UE->UE_sched_ctrl.tpc0, cur_harq->ndi, diff --git a/openair2/LAYER2/NR_MAC_gNB/mac_proto.h b/openair2/LAYER2/NR_MAC_gNB/mac_proto.h index c5df1782fd..3e9f02d7ff 100644 --- a/openair2/LAYER2/NR_MAC_gNB/mac_proto.h +++ b/openair2/LAYER2/NR_MAC_gNB/mac_proto.h @@ -163,6 +163,7 @@ void config_uldci(const NR_UE_ServingCell_Info_t *sc_info, const nfapi_nr_pusch_pdu_t *pusch_pdu, dci_pdu_rel15_t *dci_pdu_rel15, nr_srs_feedback_t *srs_feedback, + int *tpmi, int time_domain_assignment, uint8_t tpc, uint8_t ndi, diff --git a/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h b/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h index 641f6d93d1..54be4358a7 100644 --- a/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h +++ b/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h @@ -422,6 +422,8 @@ typedef struct NR_sched_pusch { int8_t ul_harq_pid; uint8_t nrOfLayers; + int tpmi; + // time_domain_allocation is the index of a list of tda int time_domain_allocation; NR_tda_info_t tda_info; diff --git a/openair2/UTIL/OPT/probe.c b/openair2/UTIL/OPT/probe.c index 25d1b6b1ec..06c02f1654 100644 --- a/openair2/UTIL/OPT/probe.c +++ b/openair2/UTIL/OPT/probe.c @@ -529,8 +529,9 @@ void trace_pdu_implementation(ws_trace_t *t) int init_opt(void) { paramdef_t opt_params[] = OPT_PARAMS_DESC ; checkedparam_t opt_checkParams[] = OPTPARAMS_CHECK_DESC; + static_assert(sizeofArray(opt_params) == sizeofArray(opt_checkParams), + "opt_params and opt_checkParams should have the same size"); int sz=sizeofArray(opt_params); - AssertFatal(sz == sizeofArray(opt_checkParams), "Error in arrays size (%d!=%lu)\n", sz, sizeofArray(opt_checkParams)); config_set_checkfunctions(opt_params, opt_checkParams, sz); config_get(config_get_if(), opt_params, sz, OPT_CONFIGPREFIX); subframesSinceCaptureStart = 0; diff --git a/tools/plots/README.md b/tools/plots/README.md new file mode 100644 index 0000000000..5d5175a882 --- /dev/null +++ b/tools/plots/README.md @@ -0,0 +1,21 @@ +# Plotting tools + +## `ul_bler_vs_snr_graph.py` + +This generates UL BLER vs SNR plots using nr_ulsim. It uses cache so subsequent runs are faster. +Assumes your nr_ulsim is in `../../cmake_targets/ran_build/build`. Run with -h flag to see options + +Example graph: + +![image](./example.png) + +### Cache usage and modifting `nr_ulsim` command + +Modify the script call to `nr_ulsim` if you want a different channel model or other flags in the `nr_ulsim` command. +Remember to clear the cache (remove `cache.pkl` from this folder) so that the results from a different run are not +taken from the cache. + +Same goes for any software modifications of `nr_ulsim` - the cache is not aware of the software version used to generate +the data - remember to clear your cache manually if you are comparing different `nr_ulsim` versions. + +The script also allows to skip usage of the cache with `--rerun` option. diff --git a/tools/plots/example.png b/tools/plots/example.png new file mode 100644 index 0000000000..56c8af15df Binary files /dev/null and b/tools/plots/example.png differ diff --git a/tools/plots/requirements.txt b/tools/plots/requirements.txt new file mode 100644 index 0000000000..dbfeaf55f1 --- /dev/null +++ b/tools/plots/requirements.txt @@ -0,0 +1,2 @@ +matplotlib==3.8.3 +numpy==1.21.5 diff --git a/tools/plots/ul_bler_vs_snr_graph.py b/tools/plots/ul_bler_vs_snr_graph.py new file mode 100755 index 0000000000..83e7aa5093 --- /dev/null +++ b/tools/plots/ul_bler_vs_snr_graph.py @@ -0,0 +1,128 @@ +#!/bin/python3 +import os +import subprocess +import numpy as np +import re +import matplotlib.pyplot as plt +import matplotlib.cm as cm +import pickle +import argparse +import concurrent.futures +import traceback +import multiprocessing + +default_ulsim_cmd = "../../cmake_targets/ran_build/build/nr_ulsim -i 1,0 -t 100 -R 273" +ulsim_args = "-n {} -s {} -S {} -m {} -r {}" + +def find_throughput(line): + ### Example line + ### SNR 10.000000: Channel BLER (0.000000e+00,-nan,-nan,-nan Channel BER (1.548551e-03,-nan,-nan,-nan) Avg round 1.00, Eff Rate 1672.0000 bits/slot, Eff Throughput 100.00, TBS 1672 bits/slot + ### SNR -0.000000: Channel BLER (0.000000e+00,-nan,-nan,-nan Channel BER (1.888043e-01,-nan,-nan,-nan) Avg round 1.00, Eff Rate 2152.0000 bits/slot, Eff Throughput 100.00, TBS 2152 bits/slot + pattern = r"SNR (-?\d+.\d+).*BLER \((-?\d+\.\d*(?:[eE][+-]\d+)?),.*Eff Throughput (\d+.\d+)" + match = re.search(pattern, line) + if match: + snr, bler, thp = match.groups() + return float(snr), float(bler), float(thp) + return None, None, None + +def generate_mcs_thps(ulsim_cmd_with_args, snr, mcs, num_runs = 10, num_rbs = 50, cache = {}): + thp = 0 + bler = 0 + num_runs_cached = 0 + cache_string = "{},{},{}".format(snr, mcs, num_rbs) + if cache_string in cache: + thp, bler, num_runs_cached = cache[cache_string] + if num_runs_cached >= num_runs: + return bler, thp + + num_runs_left = num_runs - num_runs_cached + + ulsim_cmd_formatted = ulsim_cmd_with_args.format(num_runs_left, snr, snr+0.01, mcs, num_rbs) + print(ulsim_cmd_formatted, "result insufficient in cache, running") + process = subprocess.Popen(ulsim_cmd_formatted.split(" "), stdout=subprocess.PIPE, stderr=subprocess.PIPE) + stdout_data = process.communicate()[0] + output = "" + if stdout_data is not None: + output = stdout_data.decode() + for line in output.splitlines(): + _snr, _bler, _thp = find_throughput(line) + if _snr is not None: + bler = (bler * num_runs_cached + _bler * num_runs_left) / num_runs + thp = (thp * num_runs_cached + _thp * num_runs_left) / num_runs + cache[cache_string] = thp, bler, num_runs + return bler, thp + print("error processing "+ ulsim_cmd_with_args) + print(output) + print(process.communicate()[1].decode()) + return None, None + + + +parser = argparse.ArgumentParser(description="UL SNR/BLER plot generator") +parser.add_argument("--snr-start", help="Starting SNR value") +parser.add_argument("--snr-stop", help="Final SNR value") +parser.add_argument("--snr-step", help="SNR step value") +parser.add_argument("--mcs-start", help="First MCS to test") +parser.add_argument("--mcs-stop", help="Last MCS to test") +parser.add_argument("--num-runs", help="Number of simulations per SNR/MCS value") +parser.add_argument("--rerun", help="Do not use cache to read data, run from scrach", action="store_true") +parser.add_argument("--num-rbs", help="Number of RBs") +parser.add_argument("--ulsim-cmd", help="ULSIM command to run. Do not use `sSrnm` flags, as these are used by the script", default=default_ulsim_cmd) + +cpu_count = multiprocessing.cpu_count() +cpu_to_use = max(1, cpu_count - 2) + +parser.add_argument("--num-threads", help="Number of threads to run", default=cpu_to_use) +args = parser.parse_args() + +_snr_start = float(args.snr_start) +_snr_step = float(args.snr_step) +_snr_end = float(args.snr_stop) + _snr_step/2 +_mcs_start = int(args.mcs_start) +_mcs_stop = int(args.mcs_stop) +_num_runs = int(args.num_runs) +_num_rbs = int(args.num_rbs) +ulsim_cmd = args.ulsim_cmd + " " + ulsim_args +num_threads = int(args.num_threads) + +cmap = cm.viridis +plt.figure() +plt.ylim(-0.1, 1.1) +plt.title("BLER vs SNR for MCS{} to MCS{}, nRB={} (num_runs = {})".format(_mcs_start, _mcs_stop, _num_rbs, _num_runs)) +plt.xlabel("SNR [dB]") +plt.ylabel("BLER") + +cache = {} +if not args.rerun: + try: + with open("cache.pkl", "rb+") as f: + cache = pickle.load(f) + except FileNotFoundError: + pass # no cache found + +snrs_list = np.arange(_snr_start, _snr_end, _snr_step) +mcss_list = np.arange(_mcs_start, _mcs_stop + 1, 1) +plot_index = 0 +for mcs in mcss_list: + results = {} + with concurrent.futures.ThreadPoolExecutor(max_workers=num_threads) as executor: + result_to_snr_index = {executor.submit(generate_mcs_thps, ulsim_cmd, snrs_list[snr_index], mcs, _num_runs, _num_rbs, cache): snr_index for (snr_index, _) in enumerate(snrs_list)} + for future in concurrent.futures.as_completed(result_to_snr_index): + snr_index = result_to_snr_index[future] + bler, thp = future.result() + results[snr_index] = bler + blers = np.zeros_like(snrs_list) + for snr_index in results: + blers[snr_index] = results[snr_index] + + num_plots = len(mcss_list) + percentage = float(plot_index) / max(1, (num_plots - 1)) + color = cmap(percentage) + plot_index += 1 + plt.plot(snrs_list, blers, label="MCS{}".format(mcs), color = color) + plt.legend() + plt.savefig('MCS={}-{},SNR={}-{},RBs={}graph.png'.format(_mcs_start, _mcs_stop, _snr_start, _snr_end, _num_rbs)) +if not args.rerun: + with open("cache.pkl", "wb+") as f: + pickle.dump(cache, f) +