Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
O
OpenXG-RAN
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Michael Black
OpenXG-RAN
Commits
a0594dd6
Commit
a0594dd6
authored
Feb 24, 2023
by
Robert Schmidt
Browse files
Options
Browse Files
Download
Plain Diff
Merge remote-tracking branch 'origin/goa-5g-kpi-gui' into integration_2023_w08b
parents
263436d8
0740b596
Changes
23
Hide whitespace changes
Inline
Side-by-side
Showing
23 changed files
with
1809 additions
and
94 deletions
+1809
-94
CMakeLists.txt
CMakeLists.txt
+14
-1
cmake_targets/build_oai
cmake_targets/build_oai
+2
-1
cmake_targets/tools/build_helper
cmake_targets/tools/build_helper
+1
-0
doc/Doxyfile
doc/Doxyfile
+4
-2
docker/Dockerfile.build.ubuntu20
docker/Dockerfile.build.ubuntu20
+1
-1
executables/nr-softmodem.c
executables/nr-softmodem.c
+9
-0
executables/nr-uesoftmodem.c
executables/nr-uesoftmodem.c
+4
-0
executables/softmodem-common.c
executables/softmodem-common.c
+5
-1
executables/softmodem-common.h
executables/softmodem-common.h
+11
-6
openair1/PHY/CODING/nrPolar_tools/nr_polar_defs.h
openair1/PHY/CODING/nrPolar_tools/nr_polar_defs.h
+2
-1
openair1/PHY/NR_UE_ESTIMATION/nr_ue_measurements.c
openair1/PHY/NR_UE_ESTIMATION/nr_ue_measurements.c
+22
-22
openair1/PHY/NR_UE_TRANSPORT/nr_dlsch_decoding.c
openair1/PHY/NR_UE_TRANSPORT/nr_dlsch_decoding.c
+13
-0
openair1/PHY/TOOLS/nr_phy_qt_scope.cpp
openair1/PHY/TOOLS/nr_phy_qt_scope.cpp
+1165
-0
openair1/PHY/TOOLS/nr_phy_qt_scope.h
openair1/PHY/TOOLS/nr_phy_qt_scope.h
+468
-0
openair1/PHY/TOOLS/nr_phy_scope.c
openair1/PHY/TOOLS/nr_phy_scope.c
+2
-43
openair1/PHY/TOOLS/phy_scope.h
openair1/PHY/TOOLS/phy_scope.h
+0
-7
openair1/PHY/TOOLS/phy_scope_interface.c
openair1/PHY/TOOLS/phy_scope_interface.c
+41
-0
openair1/PHY/TOOLS/phy_scope_interface.h
openair1/PHY/TOOLS/phy_scope_interface.h
+19
-0
openair1/PHY/TOOLS/readme.md
openair1/PHY/TOOLS/readme.md
+22
-3
openair1/PHY/defs_nr_UE.h
openair1/PHY/defs_nr_UE.h
+0
-2
openair1/PHY/impl_defs_nr.h
openair1/PHY/impl_defs_nr.h
+2
-2
openair1/PHY/phy_extern_nr_ue.h
openair1/PHY/phy_extern_nr_ue.h
+1
-1
openair1/SCHED_NR/phy_frame_config_nr.c
openair1/SCHED_NR/phy_frame_config_nr.c
+1
-1
No files found.
CMakeLists.txt
View file @
a0594dd6
...
...
@@ -1005,7 +1005,7 @@ add_dependencies(ldpc_cl nrLDPC_decoder_kernels_CL)
# Base CUDA setting
##############################################
add_boolean_option
(
BUILD_CUDA False
"Build support for CUDA"
OFF
)
add_boolean_option
(
ENABLE_LDPC_CUDA OFF
"Build support for CUDA"
OFF
)
if
(
ENABLE_LDPC_CUDA
)
find_package
(
CUDA REQUIRED
)
SET
(
CUDA_NVCC_FLAG
"
${
CUDA_NVCC_FLAGS
}
;-arch=sm_60;"
)
...
...
@@ -2040,6 +2040,19 @@ add_library(SIMU STATIC ${SIMUSRC} )
target_include_directories
(
SIMU PUBLIC
${
OPENAIR1_DIR
}
/SIMULATION/TOOLS
${
OPENAIR1_DIR
}
/SIMULATION/RF
)
target_link_libraries
(
SIMU PRIVATE asn1_nr_rrc asn1_lte_rrc
)
# Qt-based scope
add_boolean_option
(
ENABLE_NRQTSCOPE OFF
"Build the Qt-Scope"
OFF
)
if
(
ENABLE_NRQTSCOPE
)
find_package
(
Qt5 REQUIRED COMPONENTS Widgets Charts
)
message
(
"Qt5 Widgets and Charts found for nrqtscope"
)
set
(
QTSCOPE_SOURCE_NR
${
OPENAIR1_DIR
}
/PHY/TOOLS/nr_phy_qt_scope.cpp
)
# Creates rules for calling the Meta-Object Compiler (moc) on the given source files
qt5_wrap_cpp
(
QTSCOPE_SOURCE_NR
${
OPENAIR1_DIR
}
/PHY/TOOLS/nr_phy_qt_scope.h
)
add_library
(
nrqtscope MODULE
${
QTSCOPE_SOURCE_NR
}
)
target_link_libraries
(
nrqtscope PRIVATE Qt5::Widgets Qt5::Charts
)
target_link_libraries
(
nrqtscope PRIVATE asn1_nr_rrc asn1_lte_rrc
)
endif
()
add_library
(
SIMU_ETH
${
OPENAIR1_DIR
}
/SIMULATION/ETH_TRANSPORT/netlink_init.c
${
OPENAIR1_DIR
}
/SIMULATION/ETH_TRANSPORT/multicast_link.c
...
...
cmake_targets/build_oai
View file @
a0594dd6
...
...
@@ -53,9 +53,10 @@ BUILD_COVERITY_SCAN=0
DISABLE_HARDWARE_DEPENDENCY
=
"False"
CMAKE_BUILD_TYPE
=
"RelWithDebInfo"
CMAKE_CMD
=
"
$CMAKE
"
MAKE_CMD
=
make
BUILD_ECLIPSE
=
0
NR
=
"False"
OPTIONAL_LIBRARIES
=
"telnetsrv enbscope uescope nrscope ldpc_cuda ldpc_t1 websrv"
OPTIONAL_LIBRARIES
=
"telnetsrv enbscope uescope nrscope
nrqtscope
ldpc_cuda ldpc_t1 websrv"
RU
=
0
CMAKE_C_FLAGS
=()
CMAKE_CXX_FLAGS
=()
...
...
cmake_targets/tools/build_helper
View file @
a0594dd6
...
...
@@ -551,6 +551,7 @@ check_install_additional_tools (){
PACKAGE_LIST="\
doxygen \
libpthread-stubs0-dev \
libqt5charts5-dev \
tshark \
uml-utilities \
iperf3 \
...
...
doc/Doxyfile
View file @
a0594dd6
...
...
@@ -2218,11 +2218,13 @@ INPUT = \
@CMAKE_CURRENT_SOURCE_DIR@/../openair1/PHY/LTE_ESTIMATION/lte_dl_bf_channel_estimation.c \
@CMAKE_CURRENT_SOURCE_DIR@/../openair1/PHY/LTE_ESTIMATION/lte_sync_timefreq.c \
@CMAKE_CURRENT_SOURCE_DIR@/../openair1/PHY/LTE_ESTIMATION/filt16_32.h \
@CMAKE_CURRENT_SOURCE_DIR@/../openair1/PHY/TOOLS/nr_phy_scope.c \
@CMAKE_CURRENT_SOURCE_DIR@/../openair1/PHY/TOOLS/cdot_prod.c \
@CMAKE_CURRENT_SOURCE_DIR@/../openair1/PHY/TOOLS/nr_phy_scope.h \
@CMAKE_CURRENT_SOURCE_DIR@/../openair1/PHY/TOOLS/phy_scope_interface.h \
@CMAKE_CURRENT_SOURCE_DIR@/../openair1/PHY/TOOLS/phy_scope_interface.c \
@CMAKE_CURRENT_SOURCE_DIR@/../openair1/PHY/TOOLS/nr_phy_scope.h \
@CMAKE_CURRENT_SOURCE_DIR@/../openair1/PHY/TOOLS/nr_phy_scope.c \
@CMAKE_CURRENT_SOURCE_DIR@/../openair1/PHY/TOOLS/nr_phy_qt_scope.h \
@CMAKE_CURRENT_SOURCE_DIR@/../openair1/PHY/TOOLS/nr_phy_qt_scope.cpp \
@CMAKE_CURRENT_SOURCE_DIR@/../openair1/PHY/TOOLS/file_output.c \
@CMAKE_CURRENT_SOURCE_DIR@/../openair1/PHY/TOOLS/dfts_load.c \
@CMAKE_CURRENT_SOURCE_DIR@/../openair1/PHY/TOOLS/alaw_lut.h \
...
...
docker/Dockerfile.build.ubuntu20
View file @
a0594dd6
...
...
@@ -35,4 +35,4 @@ COPY . .
RUN /bin/sh oaienv && \
cd cmake_targets && \
mkdir -p log && \
./build_oai --eNB --gNB --RU --UE --nrUE --ninja --build-lib "telnetsrv enbscope uescope nrscope" -w USRP --verbose-ci --noavx512 -c
./build_oai --eNB --gNB --RU --UE --nrUE --ninja --build-lib "telnetsrv enbscope uescope nrscope
nrqtscope
" -w USRP --verbose-ci --noavx512 -c
executables/nr-softmodem.c
View file @
a0594dd6
...
...
@@ -711,6 +711,15 @@ int main( int argc, char **argv ) {
load_softscope
(
"nr"
,
&
p
);
}
if
(
IS_SOFTMODEM_DOSCOPE_QT
)
{
scopeParms_t
p
;
p
.
argc
=
&
argc
;
p
.
argv
=
argv
;
p
.
gNB
=
RC
.
gNB
[
0
];
p
.
ru
=
RC
.
ru
[
0
];
load_softscope
(
"nrqt"
,
&
p
);
}
if
(
NFAPI_MODE
!=
NFAPI_MODE_PNF
&&
NFAPI_MODE
!=
NFAPI_MODE_VNF
)
{
printf
(
"Not NFAPI mode - call init_eNB_afterRU()
\n
"
);
init_eNB_afterRU
();
...
...
executables/nr-uesoftmodem.c
View file @
a0594dd6
...
...
@@ -522,6 +522,10 @@ int main( int argc, char **argv ) {
memset
(
&
UE_PF_PO
[
0
][
0
],
0
,
sizeof
(
UE_PF_PO_t
)
*
NUMBER_OF_UE_MAX
*
MAX_NUM_CCs
);
set_latency_target
();
if
(
IS_SOFTMODEM_DOSCOPE_QT
)
{
load_softscope
(
"nrqt"
,
PHY_vars_UE_g
[
0
][
0
]);
}
if
(
IS_SOFTMODEM_DOSCOPE
)
{
load_softscope
(
"nr"
,
PHY_vars_UE_g
[
0
][
0
]);
}
...
...
executables/softmodem-common.c
View file @
a0594dd6
...
...
@@ -97,7 +97,7 @@ void get_common_options(uint32_t execmask) {
uint32_t
start_telnetsrv
=
0
,
start_telnetclt
=
0
;
uint32_t
start_websrv
=
0
;
uint32_t
noS1
=
0
,
nokrnmod
=
1
,
nonbiot
=
0
;
uint32_t
rfsim
=
0
,
do_forms
=
0
;
uint32_t
rfsim
=
0
,
do_forms
=
0
,
do_forms_qt
=
0
;
int
nfapi_index
=
0
;
char
*
logmem_filename
=
NULL
;
check_execmask
(
execmask
);
...
...
@@ -161,6 +161,10 @@ void get_common_options(uint32_t execmask) {
set_softmodem_optmask
(
SOFTMODEM_DOSCOPE_BIT
);
}
if
(
do_forms_qt
)
{
set_softmodem_optmask
(
SOFTMODEM_DOSCOPE_QT_BIT
);
}
if
(
start_websrv
)
{
load_module_shlib
(
"websrv"
,
NULL
,
0
,
NULL
);
}
...
...
executables/softmodem-common.h
View file @
a0594dd6
...
...
@@ -67,6 +67,7 @@ extern "C"
#define CONFIG_HLP_ULF "Set the uplink frequency offset for all component carriers\n"
#define CONFIG_HLP_CHOFF "Channel id offset\n"
#define CONFIG_HLP_SOFTS "Enable soft scope and L1 and L2 stats (Xforms)\n"
#define CONFIG_HLP_SOFTS_QT "Enable soft scope and L1 and L2 stats (QT)\n"
#define CONFIG_HLP_ITTIL "Generate ITTI analyzser logs (similar to wireshark logs but with more details)\n"
#define CONFIG_HLP_DLMCS "Set the maximum downlink MCS\n"
#define CONFIG_HLP_STMON "Enable processing timing measurement of lte softmodem on per subframe basis \n"
...
...
@@ -151,17 +152,18 @@ extern int usrp_tx_thread;
{"C" , CONFIG_HLP_DLF, 0, u64ptr:&(downlink_frequency[0][0]), defuintval:0, TYPE_UINT64, 0}, \
{"CO" , CONFIG_HLP_ULF, 0, iptr:&(uplink_frequency_offset[0][0]), defintval:0, TYPE_INT, 0}, \
{"a" , CONFIG_HLP_CHOFF, 0, iptr:&CHAIN_OFFSET, defintval:0, TYPE_INT, 0}, \
{"d" , CONFIG_HLP_SOFTS, PARAMFLAG_BOOL, uptr:(uint32_t *)&do_forms, defintval:0, TYPE_INT8, 0}, \
{"d" , CONFIG_HLP_SOFTS, PARAMFLAG_BOOL, uptr:&do_forms, defintval:0, TYPE_UINT, 0}, \
{"dqt" , CONFIG_HLP_SOFTS_QT, PARAMFLAG_BOOL, uptr:&do_forms_qt, defintval:0, TYPE_UINT, 0}, \
{"q" , CONFIG_HLP_STMON, PARAMFLAG_BOOL, iptr:&opp_enabled, defintval:0, TYPE_INT, 0}, \
{"numerology" , CONFIG_HLP_NUMEROLOGY, PARAMFLAG_BOOL, iptr:&NUMEROLOGY, defintval:1, TYPE_INT, 0}, \
{"band" , CONFIG_HLP_BAND, PARAMFLAG_BOOL, iptr:&BAND, defintval:78, TYPE_INT, 0}, \
{"emulate-rf" , CONFIG_HLP_EMULATE_RF, PARAMFLAG_BOOL, iptr:&EMULATE_RF, defintval:0, TYPE_INT, 0}, \
{"parallel-config", CONFIG_HLP_PARALLEL_CMD, 0, strptr:¶llel_config, defstrval:NULL, TYPE_STRING, 0}, \
{"worker-config", CONFIG_HLP_WORKER_CMD, 0, strptr:&worker_config, defstrval:NULL, TYPE_STRING, 0}, \
{"noS1", CONFIG_HLP_NOS1, PARAMFLAG_BOOL, uptr:&noS1, defintval:0, TYPE_
INT,
0}, \
{"rfsim", CONFIG_HLP_RFSIM, PARAMFLAG_BOOL, uptr:&rfsim, defintval:0, TYPE_
INT,
0}, \
{"nokrnmod", CONFIG_HLP_NOKRNMOD, PARAMFLAG_BOOL, uptr:&nokrnmod, defintval:0, TYPE_
INT,
0}, \
{"nbiot-disable", CONFIG_HLP_DISABLNBIOT, PARAMFLAG_BOOL, uptr:&nonbiot, defuintval:0, TYPE_
INT,
0}, \
{"noS1", CONFIG_HLP_NOS1, PARAMFLAG_BOOL, uptr:&noS1, defintval:0, TYPE_
UINT,
0}, \
{"rfsim", CONFIG_HLP_RFSIM, PARAMFLAG_BOOL, uptr:&rfsim, defintval:0, TYPE_
UINT,
0}, \
{"nokrnmod", CONFIG_HLP_NOKRNMOD, PARAMFLAG_BOOL, uptr:&nokrnmod, defintval:0, TYPE_
UINT,
0}, \
{"nbiot-disable", CONFIG_HLP_DISABLNBIOT, PARAMFLAG_BOOL, uptr:&nonbiot, defuintval:0, TYPE_
UINT,
0}, \
{"chest-freq", CONFIG_HLP_CHESTFREQ, 0, iptr:&CHEST_FREQ, defintval:0, TYPE_INT, 0}, \
{"chest-time", CONFIG_HLP_CHESTTIME, 0, iptr:&CHEST_TIME, defintval:0, TYPE_INT, 0}, \
{"nsa", CONFIG_HLP_NSA, PARAMFLAG_BOOL, iptr:&NSA, defintval:0, TYPE_INT, 0}, \
...
...
@@ -208,6 +210,7 @@ extern int usrp_tx_thread;
{ .s5 = { NULL } }, \
{ .s5 = { NULL } }, \
{ .s5 = { NULL } }, \
{ .s5 = { NULL } }, \
{ .s3a = { config_checkstr_assign_integer, \
{"MONOLITHIC", "PNF", "VNF","UE_STUB_PNF","UE_STUB_OFFNET","STANDALONE_PNF"}, \
{NFAPI_MONOLITHIC, NFAPI_MODE_PNF, NFAPI_MODE_VNF,NFAPI_UE_STUB_PNF,NFAPI_UE_STUB_OFFNET,NFAPI_MODE_STANDALONE_PNF}, \
...
...
@@ -216,7 +219,6 @@ extern int usrp_tx_thread;
{ .s5 = { NULL } }, \
{ .s5 = { NULL } }, \
{ .s5 = { NULL } }, \
{ .s5 = { NULL } }, \
}
#define CONFIG_HLP_NSA "Enable NSA mode \n"
...
...
@@ -264,6 +266,8 @@ extern int usrp_tx_thread;
#define SOFTMODEM_4GUE_BIT (1<<22)
#define SOFTMODEM_5GUE_BIT (1<<23)
#define SOFTMODEM_NOSTATS_BIT (1<<24)
#define SOFTMODEM_DOSCOPE_QT_BIT (1<<25)
#define SOFTMODEM_FUNC_BITS (SOFTMODEM_ENB_BIT | SOFTMODEM_GNB_BIT | SOFTMODEM_5GUE_BIT | SOFTMODEM_4GUE_BIT)
#define MAPPING_SOFTMODEM_FUNCTIONS {{"enb",SOFTMODEM_ENB_BIT},{"gnb",SOFTMODEM_GNB_BIT},{"4Gue",SOFTMODEM_4GUE_BIT},{"5Gue",SOFTMODEM_5GUE_BIT}}
...
...
@@ -275,6 +279,7 @@ extern int usrp_tx_thread;
#define IS_SOFTMODEM_SIML1 ( get_softmodem_optmask() & SOFTMODEM_SIML1_BIT)
#define IS_SOFTMODEM_DLSIM ( get_softmodem_optmask() & SOFTMODEM_DLSIM_BIT)
#define IS_SOFTMODEM_DOSCOPE ( get_softmodem_optmask() & SOFTMODEM_DOSCOPE_BIT)
#define IS_SOFTMODEM_DOSCOPE_QT ( get_softmodem_optmask() & SOFTMODEM_DOSCOPE_QT_BIT)
#define IS_SOFTMODEM_IQPLAYER ( get_softmodem_optmask() & SOFTMODEM_RECPLAY_BIT)
#define IS_SOFTMODEM_TELNETCLT_BIT ( get_softmodem_optmask() & SOFTMODEM_TELNETCLT_BIT)
#define IS_SOFTMODEM_ENB_BIT ( get_softmodem_optmask() & SOFTMODEM_ENB_BIT)
...
...
openair1/PHY/CODING/nrPolar_tools/nr_polar_defs.h
View file @
a0594dd6
...
...
@@ -300,6 +300,7 @@ void nr_sort_asc_int16_1D_array_ind(int32_t *matrix,
void
nr_free_double_2D_array
(
double
**
input
,
uint16_t
xlen
);
#ifndef __cplusplus
void
updateLLR
(
uint8_t
listSize
,
uint16_t
row
,
uint16_t
col
,
...
...
@@ -327,7 +328,7 @@ void updatePathMetric2(double *pathMetric,
int
ylen
,
int
zlen
,
double
llr
[
xlen
][
ylen
][
zlen
]);
#endif
//Also nr_polar_rate_matcher
static
inline
void
nr_polar_interleaver
(
uint8_t
*
input
,
uint8_t
*
output
,
...
...
openair1/PHY/NR_UE_ESTIMATION/nr_ue_measurements.c
View file @
a0594dd6
...
...
@@ -141,8 +141,8 @@ void nr_ue_measurements(PHY_VARS_NR_UE *ue,
for
(
gNB_id
=
0
;
gNB_id
<
ue
->
n_connected_gNB
;
gNB_id
++
)
{
ue
->
measurements
.
rx_power_avg_dB
[
gNB_id
]
=
dB_fixed
(
ue
->
measurements
.
rx_power_avg
[
gNB_id
]);
ue
->
measurements
.
wideband_cqi_tot
[
gNB_id
]
=
dB_fixed2
(
ue
->
measurements
.
rx_power_tot
[
gNB_id
],
ue
->
measurements
.
n0_power_tot
)
;
ue
->
measurements
.
wideband_cqi_avg
[
gNB_id
]
=
dB_fixed2
(
ue
->
measurements
.
rx_power_avg
[
gNB_id
],
ue
->
measurements
.
n0_power_avg
);
ue
->
measurements
.
wideband_cqi_tot
[
gNB_id
]
=
ue
->
measurements
.
rx_power_tot_dB
[
gNB_id
]
-
ue
->
measurements
.
n0_power_tot_dB
;
ue
->
measurements
.
wideband_cqi_avg
[
gNB_id
]
=
ue
->
measurements
.
rx_power_avg_dB
[
gNB_id
]
-
dB_fixed
(
ue
->
measurements
.
n0_power_avg
);
ue
->
measurements
.
rx_rssi_dBm
[
gNB_id
]
=
ue
->
measurements
.
rx_power_avg_dB
[
gNB_id
]
+
30
-
10
*
log10
(
pow
(
2
,
30
))
-
((
int
)
openair0_cfg
[
0
].
rx_gain
[
0
]
-
(
int
)
openair0_cfg
[
0
].
rx_gain_offset
[
0
])
-
dB_fixed
(
ue
->
frame_parms
.
ofdm_symbol_size
);
LOG_D
(
PHY
,
"[gNB %d] Slot %d, RSSI %d dB (%d dBm/RE), WBandCQI %d dB, rxPwrAvg %d, n0PwrAvg %d
\n
"
,
...
...
@@ -185,9 +185,6 @@ void nr_ue_ssb_rsrp_measurements(PHY_VARS_NR_UE *ue,
uint8_t
l_sss
=
(
symbol_offset
+
2
)
%
ue
->
frame_parms
.
symbols_per_slot
;
if
(
ssb_offset
>=
ue
->
frame_parms
.
ofdm_symbol_size
)
ssb_offset
-=
ue
->
frame_parms
.
ofdm_symbol_size
;
uint32_t
rsrp
=
0
;
LOG_D
(
PHY
,
"In %s: [UE %d] slot %d l_sss %d ssb_offset %d
\n
"
,
__FUNCTION__
,
ue
->
Mod_id
,
slot
,
l_sss
,
ssb_offset
);
...
...
@@ -195,15 +192,17 @@ void nr_ue_ssb_rsrp_measurements(PHY_VARS_NR_UE *ue,
for
(
int
aarx
=
0
;
aarx
<
ue
->
frame_parms
.
nb_antennas_rx
;
aarx
++
)
{
int16_t
*
rxF_sss
=
(
int16_t
*
)
&
rxdataF
[
aarx
][
(
l_sss
*
ue
->
frame_parms
.
ofdm_symbol_size
)
+
ssb_offset
];
int16_t
*
rxF_sss
=
(
int16_t
*
)
&
rxdataF
[
aarx
][
l_sss
*
ue
->
frame_parms
.
ofdm_symbol_size
];
for
(
int
k
=
k_start
;
k
<
k_end
;
k
++
){
int
re
=
(
ssb_offset
+
k
)
%
ue
->
frame_parms
.
ofdm_symbol_size
;
#ifdef DEBUG_MEAS_UE
LOG_I
(
PHY
,
"In %s rxF_sss %d %d
\n
"
,
__FUNCTION__
,
rxF_sss
[
k
*
2
],
rxF_sss
[
k
*
2
+
1
]);
LOG_I
(
PHY
,
"In %s rxF_sss %d %d
\n
"
,
__FUNCTION__
,
rxF_sss
[
re
*
2
],
rxF_sss
[
re
*
2
+
1
]);
#endif
rsrp
+=
(((
int32_t
)
rxF_sss
[
k
*
2
]
*
rxF_sss
[
k
*
2
])
+
((
int32_t
)
rxF_sss
[
k
*
2
+
1
]
*
rxF_sss
[
k
*
2
+
1
]));
rsrp
+=
(((
int32_t
)
rxF_sss
[
re
*
2
]
*
rxF_sss
[
re
*
2
])
+
((
int32_t
)
rxF_sss
[
re
*
2
+
1
]
*
rxF_sss
[
re
*
2
+
1
]));
nb_re
++
;
}
...
...
@@ -232,11 +231,11 @@ void nr_ue_rrc_measurements(PHY_VARS_NR_UE *ue,
uint8_t
k
;
int
slot
=
proc
->
nr_slot_rx
;
int
aarx
,
nb_nulls
;
int
aarx
;
int16_t
*
rxF_sss
;
uint8_t
k_left
=
48
;
uint8_t
k_right
=
183
;
uint8_t
k_length
=
8
;
const
uint8_t
k_left
=
48
;
const
uint8_t
k_right
=
183
;
const
uint8_t
k_length
=
8
;
uint8_t
l_sss
=
(
ue
->
symbol_offset
+
2
)
%
ue
->
frame_parms
.
symbols_per_slot
;
unsigned
int
ssb_offset
=
ue
->
frame_parms
.
first_carrier_offset
+
ue
->
frame_parms
.
ssb_start_subcarrier
;
double
rx_gain
=
openair0_cfg
[
0
].
rx_gain
[
0
];
...
...
@@ -248,41 +247,42 @@ void nr_ue_rrc_measurements(PHY_VARS_NR_UE *ue,
for
(
aarx
=
0
;
aarx
<
ue
->
frame_parms
.
nb_antennas_rx
;
aarx
++
)
{
nb_nulls
=
0
;
ue
->
measurements
.
n0_power
[
aarx
]
=
0
;
rxF_sss
=
(
int16_t
*
)
&
rxdataF
[
aarx
][
(
l_sss
*
ue
->
frame_parms
.
ofdm_symbol_size
)
+
ssb_offset
];
rxF_sss
=
(
int16_t
*
)
&
rxdataF
[
aarx
][
l_sss
*
ue
->
frame_parms
.
ofdm_symbol_size
];
//-ve spectrum from SSS
for
(
k
=
k_left
;
k
<
k_left
+
k_length
;
k
++
){
int
re
=
(
ssb_offset
+
k
)
%
ue
->
frame_parms
.
ofdm_symbol_size
;
#ifdef DEBUG_MEAS_RRC
LOG_I
(
PHY
,
"In %s -rxF_sss %d %d
\n
"
,
__FUNCTION__
,
rxF_sss
[
k
*
2
],
rxF_sss
[
k
*
2
+
1
]);
LOG_I
(
PHY
,
"In %s -rxF_sss %d %d
\n
"
,
__FUNCTION__
,
rxF_sss
[
re
*
2
],
rxF_sss
[
re
*
2
+
1
]);
#endif
ue
->
measurements
.
n0_power
[
aarx
]
+=
(((
int32_t
)
rxF_sss
[
k
*
2
]
*
rxF_sss
[
k
*
2
])
+
((
int32_t
)
rxF_sss
[
k
*
2
+
1
]
*
rxF_sss
[
k
*
2
+
1
]));
nb_nulls
++
;
ue
->
measurements
.
n0_power
[
aarx
]
+=
(((
int32_t
)
rxF_sss
[
re
*
2
]
*
rxF_sss
[
re
*
2
])
+
((
int32_t
)
rxF_sss
[
re
*
2
+
1
]
*
rxF_sss
[
re
*
2
+
1
]));
}
//+ve spectrum from SSS
for
(
k
=
k_right
;
k
<
k_right
+
k_length
;
k
++
){
int
re
=
(
ssb_offset
+
k
)
%
ue
->
frame_parms
.
ofdm_symbol_size
;
#ifdef DEBUG_MEAS_RRC
LOG_I
(
PHY
,
"In %s +rxF_sss %d %d
\n
"
,
__FUNCTION__
,
rxF_sss
[
k
*
2
],
rxF_sss
[
k
*
2
+
1
]);
LOG_I
(
PHY
,
"In %s +rxF_sss %d %d
\n
"
,
__FUNCTION__
,
rxF_sss
[
re
*
2
],
rxF_sss
[
re
*
2
+
1
]);
#endif
ue
->
measurements
.
n0_power
[
aarx
]
+=
(((
int32_t
)
rxF_sss
[
k
*
2
]
*
rxF_sss
[
k
*
2
])
+
((
int32_t
)
rxF_sss
[
k
*
2
+
1
]
*
rxF_sss
[
k
*
2
+
1
]));
nb_nulls
++
;
ue
->
measurements
.
n0_power
[
aarx
]
+=
(((
int32_t
)
rxF_sss
[
re
*
2
]
*
rxF_sss
[
re
*
2
])
+
((
int32_t
)
rxF_sss
[
re
*
2
+
1
]
*
rxF_sss
[
re
*
2
+
1
]));
}
ue
->
measurements
.
n0_power
[
aarx
]
/=
nb_nulls
;
ue
->
measurements
.
n0_power
[
aarx
]
/=
2
*
k_length
;
ue
->
measurements
.
n0_power_dB
[
aarx
]
=
(
unsigned
short
)
dB_fixed
(
ue
->
measurements
.
n0_power
[
aarx
]);
ue
->
measurements
.
n0_power_tot
+=
ue
->
measurements
.
n0_power
[
aarx
];
}
ue
->
measurements
.
n0_power_tot_dB
=
(
unsigned
short
)
dB_fixed
(
ue
->
measurements
.
n0_power_tot
/
aarx
);
ue
->
measurements
.
n0_power_tot_dB
=
(
unsigned
short
)
dB_fixed
(
ue
->
measurements
.
n0_power_tot
);
#ifdef DEBUG_MEAS_RRC
const
int
psd_awgn
=
-
174
;
...
...
openair1/PHY/NR_UE_TRANSPORT/nr_dlsch_decoding.c
View file @
a0594dd6
...
...
@@ -44,6 +44,7 @@
#include "executables/nr-uesoftmodem.h"
#include "PHY/CODING/nrLDPC_extern.h"
#include "common/utils/nr/nr_common.h"
#include "openair1/PHY/TOOLS/phy_scope_interface.h"
//#define ENABLE_PHY_PAYLOAD_DEBUG 1
...
...
@@ -53,10 +54,16 @@
static
uint64_t
nb_total_decod
=
0
;
static
uint64_t
nb_error_decod
=
0
;
static
extended_kpi_ue
kpiStructure
=
{
0
};
notifiedFIFO_t
freeBlocks_dl
;
notifiedFIFO_elt_t
*
msgToPush_dl
;
int
nbDlProcessing
=
0
;
extended_kpi_ue
*
getKPIUE
(
void
)
{
return
&
kpiStructure
;
}
void
nr_ue_dlsch_init
(
NR_UE_DLSCH_t
*
dlsch_list
,
int
num_dlsch
,
uint8_t
max_ldpc_iterations
)
{
for
(
int
i
=
0
;
i
<
num_dlsch
;
i
++
)
{
NR_UE_DLSCH_t
*
dlsch
=
dlsch_list
+
i
;
...
...
@@ -99,6 +106,11 @@ bool nr_ue_postDecode(PHY_VARS_NR_UE *phy_vars_ue, notifiedFIFO_elt_t *req, bool
// if all segments are done
if
(
last
)
{
kpiStructure
.
nb_total
++
;
kpiStructure
.
blockSize
=
dlsch
->
dlsch_config
.
TBS
;
kpiStructure
.
dl_mcs
=
dlsch
->
dlsch_config
.
mcs
;
kpiStructure
.
nofRBs
=
dlsch
->
dlsch_config
.
number_rbs
;
if
(
decodeSuccess
)
{
//LOG_D(PHY,"[UE %d] DLSCH: Setting ACK for nr_slot_rx %d TBS %d mcs %d nb_rb %d harq_process->round %d\n",
// phy_vars_ue->Mod_id,nr_slot_rx,harq_process->TBS,harq_process->mcs,harq_process->nb_rb, harq_process->round);
...
...
@@ -114,6 +126,7 @@ bool nr_ue_postDecode(PHY_VARS_NR_UE *phy_vars_ue, notifiedFIFO_elt_t *req, bool
dlsch
->
last_iteration_cnt
=
rdata
->
decodeIterations
;
LOG_D
(
PHY
,
"DLSCH received ok
\n
"
);
}
else
{
kpiStructure
.
nb_nack
++
;
//LOG_D(PHY,"[UE %d] DLSCH: Setting NAK for SFN/SF %d/%d (pid %d, status %d, round %d, TBS %d, mcs %d) Kr %d r %d harq_process->round %d\n",
// phy_vars_ue->Mod_id, frame, nr_slot_rx, harq_pid,harq_process->status, harq_process->round,harq_process->TBS,harq_process->mcs,Kr,r,harq_process->round);
harq_process
->
ack
=
0
;
...
...
openair1/PHY/TOOLS/nr_phy_qt_scope.cpp
0 → 100644
View file @
a0594dd6
/*
* 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
*
* Authors and copyright: Bo Zhao, Marwan Hammouda, Thomas Schlichter (Fraunhofer IIS)
*
* 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 <QApplication>
#include <QtWidgets>
#include <QPainter>
#include <QtGui>
#include <QLineEdit>
#include <QFormLayout>
#include <QtCharts>
#include <QValueAxis>
#include <iostream>
#include <cassert>
#include <cmath>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include "nr_phy_qt_scope.h"
extern
"C"
{
#include "PHY/CODING/nrPolar_tools/nr_polar_defs.h"
#include <openair1/PHY/CODING/nrPolar_tools/nr_polar_defs.h>
}
#define ScaleZone 4;
#define SquaredNorm(VaR) ((VaR).r * (VaR).r + (VaR).i * (VaR).i)
/*
@gNB: These are the (default) lower and upper threshold values for BLER and Throughput at the gNB side.
These threshold values can be further updated in run-time through the option 'Configs' in the drop-down list.
*/
float
Limits_KPI_gNB
[
4
][
2
]
=
{
// {lower Limit, Upper Limit}
{
0.0
,
0.8
},
// UL BLER
{
0.2
,
10
},
// UL Throughput in Mbs
{
0.0
,
0.8
},
// DL BLER
{
0.2
,
10
}
// DL Throughput in Mbs
};
/*
@UE: These are the (default) lower and upper threshold values for BLER and Throughput at the UE side.
These threshold values can be further updated in run-time through the option 'Configs' in the drop-down list
*/
float
Limits_KPI_ue
[
2
][
2
]
=
{
// {lower Limit, Upper Limit}
{
0.0
,
0.8
},
// DL BLER
{
0.2
,
10
}
// Throughput in Mbs
};
/* This class creates the window when choosing the option 'Configs' to configure the threshold values. */
ConfigBoxFloat
::
ConfigBoxFloat
(
float
*
valuePtr
,
QWidget
*
parent
)
:
QLineEdit
(
parent
),
valuePtr
(
valuePtr
)
{
this
->
setText
(
QString
::
number
(
*
valuePtr
));
connect
(
this
,
&
ConfigBoxFloat
::
editingFinished
,
this
,
&
ConfigBoxFloat
::
readText
);
}
/* This function reads the input config values, entered by user, and update the Limits_KPI_* accordignly. */
void
ConfigBoxFloat
::
readText
()
{
QString
text_e1
=
this
->
text
();
*
this
->
valuePtr
=
text_e1
.
toFloat
();
}
/* @gNB: create configuration window */
KPIConfigGnb
::
KPIConfigGnb
(
QWidget
*
parent
)
:
QWidget
(
parent
)
{
this
->
resize
(
300
,
300
);
this
->
setWindowTitle
(
"gNB Configs"
);
ConfigBoxFloat
*
configItem1
=
new
ConfigBoxFloat
(
&
Limits_KPI_gNB
[
0
][
0
]);
ConfigBoxFloat
*
configItem2
=
new
ConfigBoxFloat
(
&
Limits_KPI_gNB
[
0
][
1
]);
ConfigBoxFloat
*
configItem3
=
new
ConfigBoxFloat
(
&
Limits_KPI_gNB
[
1
][
0
]);
ConfigBoxFloat
*
configItem4
=
new
ConfigBoxFloat
(
&
Limits_KPI_gNB
[
1
][
1
]);
ConfigBoxFloat
*
configItem5
=
new
ConfigBoxFloat
(
&
Limits_KPI_gNB
[
2
][
0
]);
ConfigBoxFloat
*
configItem6
=
new
ConfigBoxFloat
(
&
Limits_KPI_gNB
[
2
][
1
]);
ConfigBoxFloat
*
configItem7
=
new
ConfigBoxFloat
(
&
Limits_KPI_gNB
[
3
][
0
]);
ConfigBoxFloat
*
configItem8
=
new
ConfigBoxFloat
(
&
Limits_KPI_gNB
[
3
][
1
]);
QFormLayout
*
flo
=
new
QFormLayout
(
this
);
flo
->
addRow
(
"U-BLER lower"
,
configItem1
);
flo
->
addRow
(
"U-BLER upper"
,
configItem2
);
flo
->
addRow
(
"U-Throughput lower[Mbs]"
,
configItem3
);
flo
->
addRow
(
"U-Throughput upper[Mbs]"
,
configItem4
);
flo
->
addRow
(
"D-BLER lower"
,
configItem5
);
flo
->
addRow
(
"D-BLER upper"
,
configItem6
);
flo
->
addRow
(
"D-Throughput lower[Mbs]"
,
configItem7
);
flo
->
addRow
(
"D-Throughput upper[Mbs]"
,
configItem8
);
}
/* @UE: create configuration window */
KPIConfigUE
::
KPIConfigUE
(
QWidget
*
parent
)
:
QWidget
(
parent
)
{
this
->
resize
(
300
,
300
);
this
->
setWindowTitle
(
"UE Configs"
);
ConfigBoxFloat
*
configItem1
=
new
ConfigBoxFloat
(
&
Limits_KPI_ue
[
0
][
0
]);
ConfigBoxFloat
*
configItem2
=
new
ConfigBoxFloat
(
&
Limits_KPI_ue
[
0
][
1
]);
ConfigBoxFloat
*
configItem3
=
new
ConfigBoxFloat
(
&
Limits_KPI_ue
[
1
][
0
]);
ConfigBoxFloat
*
configItem4
=
new
ConfigBoxFloat
(
&
Limits_KPI_ue
[
1
][
1
]);
QFormLayout
*
flo
=
new
QFormLayout
(
this
);
flo
->
addRow
(
"BLER lower"
,
configItem1
);
flo
->
addRow
(
"BLER upper"
,
configItem2
);
flo
->
addRow
(
"Throughput lower[Mbs]"
,
configItem3
);
flo
->
addRow
(
"Throughput upper[Mbs]"
,
configItem4
);
}
/* @gNB: This class creates the drop-down list at the gNB side. Each item correspinds to an implemented KPI. */
KPIListSelectGnb
::
KPIListSelectGnb
(
QWidget
*
parent
)
:
QComboBox
(
parent
)
{
this
->
addItem
(
"- empty -"
,
static_cast
<
int
>
(
PlotTypeGnb
::
empty
));
this
->
addItem
(
"RX Signal-Time"
,
static_cast
<
int
>
(
PlotTypeGnb
::
waterFall
));
this
->
addItem
(
"Channel Response"
,
static_cast
<
int
>
(
PlotTypeGnb
::
CIR
));
this
->
addItem
(
"LLR PUSCH"
,
static_cast
<
int
>
(
PlotTypeGnb
::
puschLLR
));
this
->
addItem
(
"I/Q PUSCH"
,
static_cast
<
int
>
(
PlotTypeGnb
::
puschIQ
));
this
->
addItem
(
"UL SNR"
,
static_cast
<
int
>
(
PlotTypeGnb
::
puschSNR
));
this
->
addItem
(
"UL BLER"
,
static_cast
<
int
>
(
PlotTypeGnb
::
puschBLER
));
this
->
addItem
(
"UL MCS"
,
static_cast
<
int
>
(
PlotTypeGnb
::
puschMCS
));
this
->
addItem
(
"UL Retrans."
,
static_cast
<
int
>
(
PlotTypeGnb
::
puschRETX
));
this
->
addItem
(
"UL Throughput"
,
static_cast
<
int
>
(
PlotTypeGnb
::
puschThroughput
));
this
->
addItem
(
"DL SNR (CQI)"
,
static_cast
<
int
>
(
PlotTypeGnb
::
pdschSNR
));
this
->
addItem
(
"DL BLER"
,
static_cast
<
int
>
(
PlotTypeGnb
::
pdschBLER
));
this
->
addItem
(
"DL MCS"
,
static_cast
<
int
>
(
PlotTypeGnb
::
pdschMCS
));
this
->
addItem
(
"DL Retrans."
,
static_cast
<
int
>
(
PlotTypeGnb
::
pdschRETX
));
this
->
addItem
(
"DL Throughput"
,
static_cast
<
int
>
(
PlotTypeGnb
::
pdschThroughput
));
this
->
addItem
(
"Nof Sched. RBs"
,
static_cast
<
int
>
(
PlotTypeGnb
::
pdschRBs
));
this
->
addItem
(
"Configs"
,
static_cast
<
int
>
(
PlotTypeGnb
::
config
));
}
/* @UE: This class creates the drop-down list at the UE side. Each item correspinds to an implemented KPI. */
KPIListSelectUE
::
KPIListSelectUE
(
QWidget
*
parent
)
:
QComboBox
(
parent
)
{
this
->
addItem
(
"- empty -"
,
static_cast
<
int
>
(
PlotTypeUE
::
empty
));
this
->
addItem
(
"RX Signal-Time"
,
static_cast
<
int
>
(
PlotTypeUE
::
waterFall
));
this
->
addItem
(
"Channel Response"
,
static_cast
<
int
>
(
PlotTypeUE
::
CIR
));
this
->
addItem
(
"LLR PBCH"
,
static_cast
<
int
>
(
PlotTypeUE
::
pbchLLR
));
this
->
addItem
(
"I/Q PBCH"
,
static_cast
<
int
>
(
PlotTypeUE
::
pbchIQ
));
this
->
addItem
(
"LLR PDCCH"
,
static_cast
<
int
>
(
PlotTypeUE
::
pdcchLLR
));
this
->
addItem
(
"I/Q PDCCH"
,
static_cast
<
int
>
(
PlotTypeUE
::
pdcchIQ
));
this
->
addItem
(
"LLR PDSCH"
,
static_cast
<
int
>
(
PlotTypeUE
::
pdschLLR
));
this
->
addItem
(
"I/Q PDSCH"
,
static_cast
<
int
>
(
PlotTypeUE
::
pdschIQ
));
this
->
addItem
(
"DL SNR"
,
static_cast
<
int
>
(
PlotTypeUE
::
pdschSNR
));
this
->
addItem
(
"DL BLER"
,
static_cast
<
int
>
(
PlotTypeUE
::
pdschBLER
));
this
->
addItem
(
"DL MCS"
,
static_cast
<
int
>
(
PlotTypeUE
::
pdschMCS
));
this
->
addItem
(
"Throughput"
,
static_cast
<
int
>
(
PlotTypeUE
::
pdschThroughput
));
this
->
addItem
(
"Nof Sched. RBs"
,
static_cast
<
int
>
(
PlotTypeUE
::
pdschRBs
));
this
->
addItem
(
"Freq. Offset"
,
static_cast
<
int
>
(
PlotTypeUE
::
frequencyOffset
));
this
->
addItem
(
"Time Adv."
,
static_cast
<
int
>
(
PlotTypeUE
::
timingAdvance
));
this
->
addItem
(
"Configs"
,
static_cast
<
int
>
(
PlotTypeUE
::
config
));
}
WaterFall
::
WaterFall
(
complex16
*
values
,
NR_DL_FRAME_PARMS
*
frame_parms
,
QWidget
*
parent
)
:
QWidget
(
parent
),
values
(
values
),
frame_parms
(
frame_parms
)
{
this
->
iteration
=
0
;
this
->
image
=
nullptr
;
this
->
waterFallAvg
=
nullptr
;
startTimer
(
100
);
}
/* this function to plot the waterfall graph for the RX signal in time domain for one frame. x-axis shows the frame divided into slots
and the y-axis is a color map depending on the SquaredNorm of the received signal at the correspoinding slot. */
void
WaterFall
::
timerEvent
(
QTimerEvent
*
event
)
{
if
(
!
this
->
isVisible
())
return
;
const
int
datasize
=
frame_parms
->
samples_per_frame
;
const
int
samplesPerPixel
=
datasize
/
this
->
width
();
const
int
displayPart
=
this
->
height
()
-
ScaleZone
;
if
(
!
this
->
image
)
{
this
->
image
=
new
QImage
(
this
->
width
(),
this
->
height
(),
QImage
::
Format_RGB32
);
this
->
image
->
fill
(
QColor
(
240
,
240
,
240
));
this
->
iteration
=
0
;
this
->
waterFallAvg
=
(
double
*
)
realloc
(
this
->
waterFallAvg
,
this
->
height
()
*
sizeof
(
double
));
memset
(
this
->
waterFallAvg
,
0
,
this
->
height
()
*
sizeof
(
double
));
// Plot vertical Lines
QRgb
*
pixels
=
(
QRgb
*
)
this
->
image
->
bits
();
for
(
int
slot
=
1
;
slot
<
frame_parms
->
slots_per_frame
;
slot
++
)
{
int
lineX
=
frame_parms
->
get_samples_slot_timestamp
(
slot
,
frame_parms
,
0
)
/
samplesPerPixel
;
for
(
int
row
=
displayPart
;
row
<
this
->
height
();
row
++
)
pixels
[
row
*
this
->
width
()
+
lineX
]
=
0xFF000000
;
// black
}
this
->
update
();
}
QRgb
*
pixels
=
(
QRgb
*
)
this
->
image
->
bits
();
double
avg
=
0
;
for
(
int
i
=
0
;
i
<
displayPart
;
i
++
)
avg
+=
this
->
waterFallAvg
[
i
];
avg
/=
displayPart
;
const
int
row
=
this
->
iteration
%
displayPart
;
this
->
waterFallAvg
[
row
]
=
0
;
for
(
int
pix
=
0
;
pix
<
this
->
width
();
pix
++
)
{
complex16
*
end
=
values
+
(
pix
+
1
)
*
samplesPerPixel
;
end
-=
2
;
double
val
=
0
;
for
(
complex16
*
s
=
values
+
pix
*
samplesPerPixel
;
s
<
end
;
s
++
)
val
+=
SquaredNorm
(
*
s
);
val
/=
samplesPerPixel
;
this
->
waterFallAvg
[
row
]
+=
val
;
QRgb
col
;
if
(
val
>
avg
*
100
)
col
=
0xFFFF0000
;
// red
else
if
(
val
>
avg
*
10
)
col
=
0xFFFFFF00
;
// yellow
else
if
(
val
>
avg
)
col
=
0xFF00FF00
;
// green
else
col
=
0xFF0000FF
;
// blue
pixels
[
row
*
this
->
width
()
+
pix
]
=
col
;
}
this
->
waterFallAvg
[
row
]
/=
this
->
width
();
this
->
iteration
++
;
this
->
update
(
0
,
row
,
this
->
width
(),
1
);
}
void
WaterFall
::
paintEvent
(
QPaintEvent
*
event
)
{
if
(
!
this
->
image
)
return
;
QPainter
painter
(
this
);
painter
.
drawImage
(
event
->
rect
(),
*
this
->
image
,
event
->
rect
());
// paint image on widget
}
void
WaterFall
::
resizeEvent
(
QResizeEvent
*
event
)
{
if
(
this
->
image
)
{
delete
this
->
image
;
this
->
image
=
nullptr
;
}
}
CIRPlot
::
CIRPlot
(
complex16
*
data
,
int
len
)
:
data
(
data
),
len
(
len
)
{
this
->
legend
()
->
hide
();
// add new series to the chart
this
->
series
=
new
QLineSeries
();
this
->
series
->
setColor
(
Qt
::
blue
);
this
->
addSeries
(
series
);
// add new X axis
this
->
axisX
=
new
QValueAxis
();
this
->
axisX
->
setLabelFormat
(
"%d"
);
this
->
axisX
->
setRange
(
-
len
/
2
,
len
/
2
);
this
->
addAxis
(
this
->
axisX
,
Qt
::
AlignBottom
);
this
->
series
->
attachAxis
(
this
->
axisX
);
// add new Y axis
this
->
axisY
=
new
QValueAxis
();
this
->
axisY
->
setLabelFormat
(
"%.1e"
);
this
->
addAxis
(
this
->
axisY
,
Qt
::
AlignLeft
);
this
->
series
->
attachAxis
(
this
->
axisY
);
startTimer
(
1000
);
}
void
CIRPlot
::
timerEvent
(
QTimerEvent
*
event
)
{
if
(
!
this
->
isVisible
())
return
;
QVector
<
QPointF
>
points
(
this
->
len
);
float
maxY
=
this
->
axisY
->
max
();
for
(
int
i
=
0
;
i
<
this
->
len
/
2
;
i
++
)
{
float
value
=
SquaredNorm
(
this
->
data
[
i
+
this
->
len
/
2
]);
points
[
i
]
=
QPointF
(
i
-
this
->
len
/
2
,
value
);
maxY
=
std
::
max
(
maxY
,
value
);
}
for
(
int
i
=
0
;
i
<
this
->
len
/
2
;
i
++
)
{
float
value
=
SquaredNorm
(
this
->
data
[
i
]);
points
[
i
+
this
->
len
/
2
]
=
QPointF
(
i
,
value
);
maxY
=
std
::
max
(
maxY
,
value
);
}
this
->
axisY
->
setMax
(
maxY
);
this
->
series
->
replace
(
points
);
}
LLRPlot
::
LLRPlot
(
int16_t
*
data
,
int
len
)
:
data
(
data
),
len
(
len
)
{
this
->
legend
()
->
hide
();
// add new series to the chart
this
->
series
=
new
QScatterSeries
();
this
->
series
->
setMarkerSize
(
3
);
this
->
series
->
setMarkerShape
(
QScatterSeries
::
MarkerShapeRectangle
);
this
->
series
->
setColor
(
Qt
::
blue
);
this
->
series
->
setPen
(
Qt
::
NoPen
);
this
->
addSeries
(
series
);
// add new X axis
this
->
axisX
=
new
QValueAxis
();
this
->
axisX
->
setLabelFormat
(
"%d"
);
this
->
axisX
->
setRange
(
0
,
len
);
this
->
addAxis
(
this
->
axisX
,
Qt
::
AlignBottom
);
this
->
series
->
attachAxis
(
this
->
axisX
);
// add new Y axis
this
->
axisY
=
new
QValueAxis
();
this
->
addAxis
(
this
->
axisY
,
Qt
::
AlignLeft
);
this
->
series
->
attachAxis
(
this
->
axisY
);
startTimer
(
1000
);
}
void
LLRPlot
::
timerEvent
(
QTimerEvent
*
event
)
{
if
(
!
this
->
isVisible
())
return
;
QVector
<
QPointF
>
points
(
this
->
len
);
int
maxY
=
this
->
axisY
->
max
();
for
(
int
i
=
0
;
i
<
this
->
len
;
i
++
)
{
points
[
i
]
=
QPointF
(
i
,
this
->
data
[
i
]);
maxY
=
std
::
max
(
maxY
,
abs
(
this
->
data
[
i
]));
}
this
->
axisY
->
setRange
(
-
maxY
,
maxY
);
this
->
series
->
replace
(
points
);
}
IQPlot
::
IQPlot
(
complex16
*
data
,
int
len
)
:
data
(
data
),
len
(
len
)
{
this
->
legend
()
->
hide
();
// add new series to the chart
this
->
series
=
new
QScatterSeries
();
this
->
series
->
setMarkerSize
(
3
);
this
->
series
->
setMarkerShape
(
QScatterSeries
::
MarkerShapeRectangle
);
this
->
series
->
setColor
(
Qt
::
blue
);
this
->
series
->
setPen
(
Qt
::
NoPen
);
this
->
addSeries
(
series
);
// add new X axis
this
->
axisX
=
new
QValueAxis
();
this
->
addAxis
(
this
->
axisX
,
Qt
::
AlignBottom
);
this
->
series
->
attachAxis
(
this
->
axisX
);
// add new Y axis
this
->
axisY
=
new
QValueAxis
();
this
->
addAxis
(
this
->
axisY
,
Qt
::
AlignLeft
);
this
->
series
->
attachAxis
(
this
->
axisY
);
startTimer
(
1000
);
}
void
IQPlot
::
timerEvent
(
QTimerEvent
*
event
)
{
if
(
!
this
->
isVisible
())
return
;
QVector
<
QPointF
>
points
(
this
->
len
);
int
maxX
=
this
->
axisX
->
max
();
int
maxY
=
this
->
axisY
->
max
();
for
(
int
i
=
0
;
i
<
this
->
len
;
i
++
)
{
points
[
i
]
=
QPointF
(
this
->
data
[
i
].
r
,
this
->
data
[
i
].
i
);
maxX
=
std
::
max
(
maxX
,
abs
(
this
->
data
[
i
].
r
));
maxY
=
std
::
max
(
maxY
,
abs
(
this
->
data
[
i
].
i
));
}
this
->
axisX
->
setRange
(
-
maxX
,
maxX
);
this
->
axisY
->
setRange
(
-
maxY
,
maxY
);
this
->
series
->
replace
(
points
);
}
KPIPlot
::
KPIPlot
(
ValueProvider
*
valueProvider
,
float
*
limits
)
:
valueProvider
(
valueProvider
),
limits
(
limits
)
{
this
->
series
=
new
QLineSeries
();
this
->
series
->
setColor
(
Qt
::
blue
);
this
->
addSeries
(
series
);
this
->
seriesMin
=
new
QLineSeries
();
this
->
seriesMin
->
setColor
(
Qt
::
red
);
this
->
addSeries
(
seriesMin
);
this
->
seriesMax
=
new
QLineSeries
();
this
->
seriesMax
->
setColor
(
Qt
::
red
);
this
->
addSeries
(
seriesMax
);
this
->
seriesAvg
=
new
QLineSeries
();
this
->
seriesAvg
->
setColor
(
Qt
::
green
);
this
->
seriesAvg
->
setName
(
"Average"
);
this
->
addSeries
(
seriesAvg
);
if
(
limits
)
{
this
->
seriesMin
->
setName
(
"Upper Limit"
);
this
->
seriesMax
->
setName
(
"Lower Limit"
);
this
->
minValue
=
limits
[
0
];
this
->
maxValue
=
limits
[
1
];
}
else
{
this
->
seriesMin
->
setName
(
"Minimum"
);
this
->
seriesMax
->
setName
(
"Maximum"
);
this
->
minValue
=
0
;
this
->
maxValue
=
0
;
}
this
->
sumValue
=
0
;
this
->
plotIdx
=
0
;
// add new X axis
this
->
axisX
=
new
QValueAxis
();
this
->
axisX
->
setLabelFormat
(
"%d"
);
this
->
axisX
->
setRange
(
0
,
300
);
this
->
addAxis
(
this
->
axisX
,
Qt
::
AlignBottom
);
this
->
series
->
attachAxis
(
this
->
axisX
);
this
->
seriesMin
->
attachAxis
(
this
->
axisX
);
this
->
seriesMax
->
attachAxis
(
this
->
axisX
);
this
->
seriesAvg
->
attachAxis
(
this
->
axisX
);
// add new Y axis
this
->
axisY
=
new
QValueAxis
();
this
->
addAxis
(
this
->
axisY
,
Qt
::
AlignLeft
);
this
->
series
->
attachAxis
(
this
->
axisY
);
this
->
seriesMin
->
attachAxis
(
this
->
axisY
);
this
->
seriesMax
->
attachAxis
(
this
->
axisY
);
this
->
seriesAvg
->
attachAxis
(
this
->
axisY
);
startTimer
(
1000
);
}
void
KPIPlot
::
timerEvent
(
QTimerEvent
*
event
)
{
if
(
!
this
->
isVisible
())
return
;
if
(
this
->
plotIdx
>=
300
)
{
this
->
series
->
clear
();
this
->
sumValue
=
0
;
this
->
plotIdx
=
0
;
}
float
value
=
this
->
valueProvider
->
getValue
();
this
->
series
->
append
(
this
->
plotIdx
++
,
value
);
this
->
minValue
=
std
::
min
(
this
->
minValue
,
value
);
this
->
maxValue
=
std
::
max
(
this
->
maxValue
,
value
);
this
->
seriesMin
->
clear
();
this
->
seriesMax
->
clear
();
this
->
seriesAvg
->
clear
();
if
(
this
->
limits
)
{
this
->
seriesMin
->
append
(
0
,
this
->
limits
[
0
]);
this
->
seriesMin
->
append
(
300
,
this
->
limits
[
0
]);
this
->
seriesMax
->
append
(
0
,
this
->
limits
[
1
]);
this
->
seriesMax
->
append
(
300
,
this
->
limits
[
1
]);
}
else
{
this
->
seriesMin
->
append
(
0
,
this
->
minValue
);
this
->
seriesMin
->
append
(
300
,
this
->
minValue
);
this
->
seriesMax
->
append
(
0
,
this
->
maxValue
);
this
->
seriesMax
->
append
(
300
,
this
->
maxValue
);
}
this
->
sumValue
+=
value
;
float
average
=
this
->
sumValue
/
this
->
plotIdx
;
this
->
seriesAvg
->
append
(
0
,
average
);
this
->
seriesAvg
->
append
(
300
,
average
);
this
->
axisY
->
setRange
(
this
->
minValue
,
this
->
maxValue
);
}
RTXPlot
::
RTXPlot
(
uint64_t
*
rounds
)
:
rounds
(
rounds
)
{
for
(
int
i
=
0
;
i
<
4
;
i
++
)
this
->
lastRounds
[
i
]
=
rounds
[
i
];
this
->
maxValue
=
0
;
this
->
plotIdx
=
0
;
this
->
series
[
0
]
=
new
QLineSeries
();
this
->
series
[
0
]
->
setColor
(
Qt
::
blue
);
this
->
series
[
0
]
->
setName
(
"round 1"
);
this
->
addSeries
(
this
->
series
[
0
]);
this
->
series
[
1
]
=
new
QLineSeries
();
this
->
series
[
1
]
->
setColor
(
Qt
::
green
);
this
->
series
[
1
]
->
setName
(
"round 2"
);
this
->
addSeries
(
this
->
series
[
1
]);
this
->
series
[
2
]
=
new
QLineSeries
();
this
->
series
[
2
]
->
setColor
(
Qt
::
yellow
);
this
->
series
[
2
]
->
setName
(
"round 3"
);
this
->
addSeries
(
this
->
series
[
2
]);
this
->
series
[
3
]
=
new
QLineSeries
();
this
->
series
[
3
]
->
setColor
(
Qt
::
red
);
this
->
series
[
3
]
->
setName
(
"round 4"
);
this
->
addSeries
(
this
->
series
[
3
]);
// add new X axis
this
->
axisX
=
new
QValueAxis
();
this
->
axisX
->
setLabelFormat
(
"%d"
);
this
->
axisX
->
setRange
(
0
,
300
);
this
->
addAxis
(
this
->
axisX
,
Qt
::
AlignBottom
);
this
->
series
[
0
]
->
attachAxis
(
this
->
axisX
);
this
->
series
[
1
]
->
attachAxis
(
this
->
axisX
);
this
->
series
[
2
]
->
attachAxis
(
this
->
axisX
);
this
->
series
[
3
]
->
attachAxis
(
this
->
axisX
);
// add new Y axis
this
->
axisY
=
new
QValueAxis
();
this
->
addAxis
(
this
->
axisY
,
Qt
::
AlignLeft
);
this
->
series
[
0
]
->
attachAxis
(
this
->
axisY
);
this
->
series
[
1
]
->
attachAxis
(
this
->
axisY
);
this
->
series
[
2
]
->
attachAxis
(
this
->
axisY
);
this
->
series
[
3
]
->
attachAxis
(
this
->
axisY
);
startTimer
(
1000
);
}
void
RTXPlot
::
timerEvent
(
QTimerEvent
*
event
)
{
if
(
!
this
->
isVisible
())
return
;
if
(
this
->
plotIdx
>=
300
)
{
this
->
series
[
0
]
->
clear
();
this
->
series
[
1
]
->
clear
();
this
->
series
[
2
]
->
clear
();
this
->
series
[
3
]
->
clear
();
this
->
plotIdx
=
0
;
}
for
(
int
i
=
0
;
i
<
4
;
i
++
)
{
int
value
=
this
->
rounds
[
i
]
-
this
->
lastRounds
[
i
];
this
->
lastRounds
[
i
]
+=
value
;
this
->
maxValue
=
std
::
max
(
this
->
maxValue
,
value
);
this
->
series
[
i
]
->
append
(
this
->
plotIdx
,
value
);
}
this
->
plotIdx
++
;
this
->
axisY
->
setRange
(
0
,
this
->
maxValue
);
}
/* @gNB: This is the main function of the gNB sub-widgets, i.e., for each KPI. This function will be called
only once when the the sub-widget is created, and it mainly initializes the widget variables and structures. */
PainterWidgetGnb
::
PainterWidgetGnb
(
QWidget
*
config
,
QComboBox
*
comboBox
,
scopeData_t
*
p
)
:
config
(
config
),
comboBox
(
comboBox
),
p
(
p
)
{
this
->
chartView
=
new
QChartView
(
this
);
this
->
chartView
->
hide
();
this
->
plotType
=
PlotTypeGnb
::
empty
;
makeConnections
(
this
->
comboBox
->
currentIndex
());
connect
(
this
->
comboBox
,
static_cast
<
void
(
QComboBox
::*
)(
int
)
>
(
&
QComboBox
::
currentIndexChanged
),
this
,
&
PainterWidgetGnb
::
makeConnections
);
}
float
PainterWidgetGnb
::
getValue
()
{
NR_DL_FRAME_PARMS
*
frame_parms
=
&
this
->
p
->
gNB
->
frame_parms
;
gNB_MAC_INST
*
gNBMac
=
(
gNB_MAC_INST
*
)
RC
.
nrmac
[
0
];
NR_UE_info_t
*
targetUE
=
gNBMac
->
UE_info
.
list
[
0
];
NR_UE_sched_ctrl_t
*
sched_ctrl
=
&
targetUE
->
UE_sched_ctrl
;
NR_sched_pdsch_t
*
sched_pdsch
=
&
sched_ctrl
->
sched_pdsch
;
switch
(
this
->
plotType
)
{
case
PlotTypeGnb
:
:
puschSNR
:
return
sched_ctrl
->
pusch_snrx10
/
10.0
;
case
PlotTypeGnb
:
:
puschBLER
:
return
sched_ctrl
->
ul_bler_stats
.
bler
;
case
PlotTypeGnb
:
:
puschMCS
:
return
sched_ctrl
->
ul_bler_stats
.
mcs
;
case
PlotTypeGnb
:
:
puschThroughput
:
{
double
slotDuration
=
10.0
/
(
double
)
frame_parms
->
slots_per_frame
;
double
blerTerm
=
1.0
-
(
double
)
sched_ctrl
->
ul_bler_stats
.
bler
;
double
blockSizeBits
=
(
double
)(
targetUE
->
mac_stats
.
ul
.
current_bytes
<<
3
);
double
ThrouputKBitSec
=
blerTerm
*
blockSizeBits
/
slotDuration
;
return
(
float
)(
ThrouputKBitSec
/
1000
);
// Throughput in MBit/sec
}
case
PlotTypeGnb
:
:
pdschSNR
:
return
sched_ctrl
->
CSI_report
.
cri_ri_li_pmi_cqi_report
.
wb_cqi_1tb
;
case
PlotTypeGnb
:
:
pdschBLER
:
return
sched_ctrl
->
dl_bler_stats
.
bler
;
case
PlotTypeGnb
:
:
pdschMCS
:
return
sched_ctrl
->
dl_bler_stats
.
mcs
;
case
PlotTypeGnb
:
:
pdschThroughput
:
{
double
slotDuration
=
10.0
/
(
double
)
frame_parms
->
slots_per_frame
;
double
blerTerm
=
1.0
-
(
double
)
sched_ctrl
->
dl_bler_stats
.
bler
;
double
blockSizeBits
=
(
double
)(
targetUE
->
mac_stats
.
dl
.
current_bytes
<<
3
);
double
ThrouputKBitSec
=
blerTerm
*
blockSizeBits
/
slotDuration
;
return
(
float
)(
ThrouputKBitSec
/
1000
);
// Throughput in MBit/sec
}
case
PlotTypeGnb
:
:
pdschRBs
:
return
sched_ctrl
->
harq_processes
[
sched_pdsch
->
dl_harq_pid
].
sched_pdsch
.
rbSize
;
default:
return
0
;
}
}
/* @gNB Class: this function is called when a resize event is detected, then the widget will be adjusted accordignly. */
void
PainterWidgetGnb
::
resizeEvent
(
QResizeEvent
*
event
)
{
if
(
this
->
waterFall
)
this
->
waterFall
->
resize
(
event
->
size
());
this
->
chartView
->
resize
(
event
->
size
());
}
/* @gNB: this function is to check which KPI to plot in the current widget based on the drop-down list selection
Then the widget will be connected with the correspinding KPI function. */
void
PainterWidgetGnb
::
makeConnections
(
int
type
)
{
const
PlotTypeGnb
plotType
=
static_cast
<
PlotTypeGnb
>
(
type
);
if
(
plotType
==
this
->
plotType
)
return
;
if
(
plotType
==
PlotTypeGnb
::
config
)
{
config
->
show
();
this
->
comboBox
->
setCurrentIndex
(
static_cast
<
int
>
(
this
->
plotType
));
return
;
}
this
->
plotType
=
plotType
;
this
->
chartView
->
hide
();
QChart
*
prevChart
=
this
->
chartView
->
chart
();
if
(
plotType
==
PlotTypeGnb
::
waterFall
)
{
this
->
chartView
->
setChart
(
new
QChart
);
this
->
waterFall
=
new
WaterFall
((
complex16
*
)
this
->
p
->
ru
->
common
.
rxdata
[
0
],
&
this
->
p
->
gNB
->
frame_parms
,
this
);
this
->
waterFall
->
resize
(
this
->
size
());
this
->
waterFall
->
show
();
delete
prevChart
;
return
;
}
if
(
this
->
waterFall
)
{
this
->
waterFall
->
hide
();
delete
this
->
waterFall
;
this
->
waterFall
=
nullptr
;
}
NR_DL_FRAME_PARMS
*
frame_parms
=
&
this
->
p
->
gNB
->
frame_parms
;
gNB_MAC_INST
*
gNBMac
=
(
gNB_MAC_INST
*
)
RC
.
nrmac
[
0
];
NR_UE_info_t
*
targetUE
=
gNBMac
->
UE_info
.
list
[
0
];
QChart
*
newChart
=
nullptr
;
switch
(
plotType
)
{
case
PlotTypeGnb
:
:
empty
:
{
newChart
=
new
QChart
();
break
;
}
case
PlotTypeGnb
:
:
CIR
:
{
newChart
=
new
CIRPlot
((
complex16
*
)
p
->
gNB
->
pusch_vars
[
0
]
->
ul_ch_estimates_time
[
0
],
frame_parms
->
ofdm_symbol_size
);
break
;
}
case
PlotTypeGnb
:
:
puschLLR
:
{
int
num_re
=
frame_parms
->
N_RB_UL
*
12
*
frame_parms
->
symbols_per_slot
;
int
Qm
=
2
;
int
coded_bits_per_codeword
=
num_re
*
Qm
;
newChart
=
new
LLRPlot
((
int16_t
*
)
p
->
gNB
->
pusch_vars
[
0
]
->
llr
,
coded_bits_per_codeword
);
break
;
}
case
PlotTypeGnb
:
:
puschIQ
:
{
int
num_re
=
frame_parms
->
N_RB_UL
*
12
*
frame_parms
->
symbols_per_slot
;
newChart
=
new
IQPlot
((
complex16
*
)
p
->
gNB
->
pusch_vars
[
0
]
->
rxdataF_comp
[
0
],
num_re
);
break
;
}
case
PlotTypeGnb
:
:
puschSNR
:
{
newChart
=
new
KPIPlot
(
this
);
break
;
}
case
PlotTypeGnb
:
:
puschBLER
:
{
newChart
=
new
KPIPlot
(
this
,
Limits_KPI_gNB
[
0
]);
break
;
}
case
PlotTypeGnb
:
:
puschMCS
:
{
newChart
=
new
KPIPlot
(
this
);
break
;
}
case
PlotTypeGnb
:
:
puschRETX
:
{
newChart
=
new
RTXPlot
(
targetUE
->
mac_stats
.
ul
.
rounds
);
break
;
}
case
PlotTypeGnb
:
:
puschThroughput
:
{
newChart
=
new
KPIPlot
(
this
,
Limits_KPI_gNB
[
1
]);
break
;
}
case
PlotTypeGnb
:
:
pdschSNR
:
{
newChart
=
new
KPIPlot
(
this
);
break
;
}
case
PlotTypeGnb
:
:
pdschBLER
:
{
newChart
=
new
KPIPlot
(
this
,
Limits_KPI_gNB
[
2
]);
break
;
}
case
PlotTypeGnb
:
:
pdschMCS
:
{
newChart
=
new
KPIPlot
(
this
);
break
;
}
case
PlotTypeGnb
:
:
pdschRETX
:
{
newChart
=
new
RTXPlot
(
targetUE
->
mac_stats
.
dl
.
rounds
);
break
;
}
case
PlotTypeGnb
:
:
pdschThroughput
:
{
newChart
=
new
KPIPlot
(
this
,
Limits_KPI_gNB
[
3
]);
break
;
}
case
PlotTypeGnb
:
:
pdschRBs
:
{
newChart
=
new
KPIPlot
(
this
);
break
;
}
default:
break
;
}
this
->
chartView
->
setChart
(
newChart
);
this
->
chartView
->
show
();
delete
prevChart
;
}
/* @UE: This is the main function of the UE sub-widgets, i.e., for each KPI. This function will be called
only once when the the sub-widget is created, and it mainly initializes the widget variables and structures. */
PainterWidgetUE
::
PainterWidgetUE
(
QWidget
*
config
,
QComboBox
*
comboBox
,
PHY_VARS_NR_UE
*
ue
)
:
config
(
config
),
comboBox
(
comboBox
),
ue
(
ue
)
{
this
->
chartView
=
new
QChartView
(
this
);
this
->
chartView
->
hide
();
this
->
plotType
=
PlotTypeUE
::
empty
;
makeConnections
(
this
->
comboBox
->
currentIndex
());
connect
(
this
->
comboBox
,
static_cast
<
void
(
QComboBox
::*
)(
int
)
>
(
&
QComboBox
::
currentIndexChanged
),
this
,
&
PainterWidgetUE
::
makeConnections
);
}
float
PainterWidgetUE
::
getValue
()
{
NR_DL_FRAME_PARMS
*
frame_parms
=
&
this
->
ue
->
frame_parms
;
switch
(
this
->
plotType
)
{
case
PlotTypeUE
:
:
pdschSNR
:
return
(
float
)
this
->
ue
->
measurements
.
wideband_cqi_avg
[
0
];
case
PlotTypeUE
:
:
pdschBLER
:
{
if
(
getKPIUE
()
->
nb_total
==
0
)
return
0
;
return
(
float
)
getKPIUE
()
->
nb_nack
/
(
float
)
getKPIUE
()
->
nb_total
;
}
case
PlotTypeUE
:
:
pdschMCS
:
return
(
float
)
getKPIUE
()
->
dl_mcs
;
case
PlotTypeUE
:
:
pdschThroughput
:
{
if
(
getKPIUE
()
->
nb_total
==
0
)
return
0
;
double
slotDuration
=
10.0
/
(
double
)
frame_parms
->
slots_per_frame
;
double
blerTerm
=
1.0
-
(
double
)(
getKPIUE
()
->
nb_nack
)
/
(
double
)
getKPIUE
()
->
nb_total
;
double
blockSizeBits
=
(
double
)
getKPIUE
()
->
blockSize
;
double
ThrouputKBitSec
=
blerTerm
*
blockSizeBits
/
slotDuration
;
return
(
float
)(
ThrouputKBitSec
/
1000
);
// Throughput in MBit/sec
}
case
PlotTypeUE
:
:
pdschRBs
:
return
(
float
)
getKPIUE
()
->
nofRBs
;
case
PlotTypeUE
:
:
frequencyOffset
:
return
(
float
)
this
->
ue
->
common_vars
.
freq_offset
;
case
PlotTypeUE
:
:
timingAdvance
:
return
(
float
)
this
->
ue
->
timing_advance
;
default:
return
0
;
}
}
/* @UE Class: this function is called when a resize event is detected, then the widget will be adjusted accordignly. */
void
PainterWidgetUE
::
resizeEvent
(
QResizeEvent
*
event
)
{
if
(
this
->
waterFall
)
this
->
waterFall
->
resize
(
event
->
size
());
this
->
chartView
->
resize
(
event
->
size
());
}
/* @UE: this function is to check which KPI to plot in the current widget based on the drop-down list selection.
Then the widget will be connected with the correspinding KPI function. */
void
PainterWidgetUE
::
makeConnections
(
int
type
)
{
const
PlotTypeUE
plotType
=
static_cast
<
PlotTypeUE
>
(
type
);
if
(
plotType
==
this
->
plotType
)
return
;
if
(
plotType
==
PlotTypeUE
::
config
)
{
config
->
show
();
this
->
comboBox
->
setCurrentIndex
(
static_cast
<
int
>
(
this
->
plotType
));
return
;
}
this
->
plotType
=
plotType
;
this
->
chartView
->
hide
();
QChart
*
prevChart
=
this
->
chartView
->
chart
();
if
(
plotType
==
PlotTypeUE
::
waterFall
)
{
this
->
chartView
->
setChart
(
new
QChart
);
this
->
waterFall
=
new
WaterFall
((
complex16
*
)
this
->
ue
->
common_vars
.
rxdata
[
0
],
&
this
->
ue
->
frame_parms
,
this
);
this
->
waterFall
->
resize
(
this
->
size
());
this
->
waterFall
->
show
();
delete
prevChart
;
return
;
}
if
(
this
->
waterFall
)
{
this
->
waterFall
->
hide
();
delete
this
->
waterFall
;
this
->
waterFall
=
nullptr
;
}
scopeData_t
*
scope
=
(
scopeData_t
*
)
this
->
ue
->
scopeData
;
scopeGraphData_t
**
data
=
(
scopeGraphData_t
**
)
scope
->
liveData
;
QChart
*
newChart
=
nullptr
;
switch
(
plotType
)
{
case
PlotTypeUE
:
:
empty
:
{
newChart
=
new
QChart
();
break
;
}
case
PlotTypeUE
:
:
CIR
:
{
if
(
!
data
[
pbchDlChEstimateTime
])
{
newChart
=
new
QChart
();
this
->
plotType
=
PlotTypeUE
::
empty
;
this
->
comboBox
->
setCurrentIndex
(
static_cast
<
int
>
(
PlotTypeUE
::
empty
));
break
;
}
newChart
=
new
CIRPlot
((
complex16
*
)(
data
[
pbchDlChEstimateTime
]
+
1
),
data
[
pbchDlChEstimateTime
]
->
lineSz
);
break
;
}
case
PlotTypeUE
:
:
pbchLLR
:
{
if
(
!
data
[
pbchLlr
])
{
newChart
=
new
QChart
();
this
->
plotType
=
PlotTypeUE
::
empty
;
this
->
comboBox
->
setCurrentIndex
(
static_cast
<
int
>
(
PlotTypeUE
::
empty
));
break
;
}
newChart
=
new
LLRPlot
((
int16_t
*
)(
data
[
pbchLlr
]
+
1
),
data
[
pbchLlr
]
->
lineSz
);
break
;
}
case
PlotTypeUE
:
:
pbchIQ
:
{
if
(
!
data
[
pbchRxdataF_comp
])
{
newChart
=
new
QChart
();
this
->
plotType
=
PlotTypeUE
::
empty
;
this
->
comboBox
->
setCurrentIndex
(
static_cast
<
int
>
(
PlotTypeUE
::
empty
));
break
;
}
newChart
=
new
IQPlot
((
complex16
*
)(
data
[
pbchRxdataF_comp
]
+
1
),
data
[
pbchRxdataF_comp
]
->
lineSz
);
break
;
}
case
PlotTypeUE
:
:
pdcchLLR
:
{
if
(
!
data
[
pdcchLlr
])
{
newChart
=
new
QChart
();
this
->
plotType
=
PlotTypeUE
::
empty
;
this
->
comboBox
->
setCurrentIndex
(
static_cast
<
int
>
(
PlotTypeUE
::
empty
));
break
;
}
newChart
=
new
LLRPlot
((
int16_t
*
)(
data
[
pdcchLlr
]
+
1
),
data
[
pdcchLlr
]
->
lineSz
);
break
;
}
case
PlotTypeUE
:
:
pdcchIQ
:
{
if
(
!
data
[
pdcchRxdataF_comp
])
{
newChart
=
new
QChart
();
this
->
plotType
=
PlotTypeUE
::
empty
;
this
->
comboBox
->
setCurrentIndex
(
static_cast
<
int
>
(
PlotTypeUE
::
empty
));
break
;
}
newChart
=
new
IQPlot
((
complex16
*
)(
data
[
pdcchRxdataF_comp
]
+
1
),
data
[
pdcchRxdataF_comp
]
->
lineSz
);
break
;
}
case
PlotTypeUE
:
:
pdschLLR
:
{
if
(
!
data
[
pdschLlr
])
{
newChart
=
new
QChart
();
this
->
plotType
=
PlotTypeUE
::
empty
;
this
->
comboBox
->
setCurrentIndex
(
static_cast
<
int
>
(
PlotTypeUE
::
empty
));
break
;
}
newChart
=
new
LLRPlot
((
int16_t
*
)(
data
[
pdschLlr
]
+
1
),
data
[
pdschLlr
]
->
lineSz
);
break
;
}
case
PlotTypeUE
:
:
pdschIQ
:
{
if
(
!
data
[
pdschRxdataF_comp
])
{
newChart
=
new
QChart
();
this
->
plotType
=
PlotTypeUE
::
empty
;
this
->
comboBox
->
setCurrentIndex
(
static_cast
<
int
>
(
PlotTypeUE
::
empty
));
break
;
}
newChart
=
new
IQPlot
((
complex16
*
)(
data
[
pdschRxdataF_comp
]
+
1
),
data
[
pdschRxdataF_comp
]
->
lineSz
);
break
;
}
case
PlotTypeUE
:
:
pdschSNR
:
{
newChart
=
new
KPIPlot
(
this
);
break
;
}
case
PlotTypeUE
:
:
pdschBLER
:
{
newChart
=
new
KPIPlot
(
this
,
Limits_KPI_ue
[
0
]);
break
;
}
case
PlotTypeUE
:
:
pdschMCS
:
{
newChart
=
new
KPIPlot
(
this
);
break
;
}
case
PlotTypeUE
:
:
pdschThroughput
:
{
newChart
=
new
KPIPlot
(
this
,
Limits_KPI_ue
[
1
]);
break
;
}
case
PlotTypeUE
:
:
pdschRBs
:
{
newChart
=
new
KPIPlot
(
this
);
break
;
}
case
PlotTypeUE
:
:
frequencyOffset
:
{
newChart
=
new
KPIPlot
(
this
);
break
;
}
case
PlotTypeUE
:
:
timingAdvance
:
{
newChart
=
new
KPIPlot
(
this
);
break
;
}
default:
break
;
}
this
->
chartView
->
setChart
(
newChart
);
this
->
chartView
->
show
();
delete
prevChart
;
}
// main thread of gNB
void
*
nrgNBQtscopeThread
(
void
*
arg
)
{
scopeData_t
*
p
=
(
scopeData_t
*
)
arg
;
sleep
(
3
);
int
argc
=
1
;
char
*
argv
[]
=
{(
char
*
)
"nrqt_scopegNB"
};
QApplication
a
(
argc
,
argv
);
// Create a main window (widget)
QWidget
window
;
window
.
resize
(
800
,
800
);
window
.
setWindowTitle
(
"gNB Scope"
);
// Create gNB configuration window
KPIConfigGnb
config
;
// Main layout
QGridLayout
mainLayout
(
&
window
);
KPIListSelectGnb
combo1
;
combo1
.
setCurrentIndex
(
static_cast
<
int
>
(
PlotTypeGnb
::
waterFall
));
PainterWidgetGnb
pwidgetGnbCombo1
(
&
config
,
&
combo1
,
p
);
mainLayout
.
addWidget
(
&
combo1
,
0
,
0
);
mainLayout
.
addWidget
(
&
pwidgetGnbCombo1
,
1
,
0
);
KPIListSelectGnb
combo2
;
combo2
.
setCurrentIndex
(
static_cast
<
int
>
(
PlotTypeGnb
::
CIR
));
PainterWidgetGnb
pwidgetGnbCombo2
(
&
config
,
&
combo2
,
p
);
mainLayout
.
addWidget
(
&
combo2
,
0
,
1
);
mainLayout
.
addWidget
(
&
pwidgetGnbCombo2
,
1
,
1
);
KPIListSelectGnb
combo3
;
combo3
.
setCurrentIndex
(
static_cast
<
int
>
(
PlotTypeGnb
::
puschLLR
));
PainterWidgetGnb
pwidgetGnbCombo3
(
&
config
,
&
combo3
,
p
);
mainLayout
.
addWidget
(
&
combo3
,
2
,
0
);
mainLayout
.
addWidget
(
&
pwidgetGnbCombo3
,
3
,
0
);
KPIListSelectGnb
combo4
;
combo4
.
setCurrentIndex
(
static_cast
<
int
>
(
PlotTypeGnb
::
puschIQ
));
PainterWidgetGnb
pwidgetGnbCombo4
(
&
config
,
&
combo4
,
p
);
mainLayout
.
addWidget
(
&
combo4
,
2
,
1
);
mainLayout
.
addWidget
(
&
pwidgetGnbCombo4
,
3
,
1
);
KPIListSelectGnb
combo5
;
combo5
.
setCurrentIndex
(
static_cast
<
int
>
(
PlotTypeGnb
::
empty
));
PainterWidgetGnb
pwidgetGnbCombo5
(
&
config
,
&
combo5
,
p
);
mainLayout
.
addWidget
(
&
combo5
,
4
,
0
);
mainLayout
.
addWidget
(
&
pwidgetGnbCombo5
,
5
,
0
);
KPIListSelectGnb
combo6
;
combo6
.
setCurrentIndex
(
static_cast
<
int
>
(
PlotTypeGnb
::
empty
));
PainterWidgetGnb
pwidgetGnbCombo6
(
&
config
,
&
combo6
,
p
);
mainLayout
.
addWidget
(
&
combo6
,
4
,
1
);
mainLayout
.
addWidget
(
&
pwidgetGnbCombo6
,
5
,
1
);
// display the main window
window
.
show
();
a
.
exec
();
return
nullptr
;
}
// main thread of UE
void
*
nrUEQtscopeThread
(
void
*
arg
)
{
PHY_VARS_NR_UE
*
ue
=
(
PHY_VARS_NR_UE
*
)
arg
;
sleep
(
1
);
int
argc
=
1
;
char
*
argv
[]
=
{(
char
*
)
"nrqt_scopeUE"
};
QApplication
a
(
argc
,
argv
);
// Create a main window (widget)
QWidget
window
;
window
.
resize
(
800
,
800
);
window
.
setWindowTitle
(
"UE Scope"
);
// Create UE configuration window
KPIConfigUE
config
;
// Main layout
QGridLayout
mainLayout
(
&
window
);
KPIListSelectUE
combo1
;
combo1
.
setCurrentIndex
(
static_cast
<
int
>
(
PlotTypeUE
::
waterFall
));
PainterWidgetUE
pwidgetueCombo1
(
&
config
,
&
combo1
,
ue
);
mainLayout
.
addWidget
(
&
combo1
,
0
,
0
);
mainLayout
.
addWidget
(
&
pwidgetueCombo1
,
1
,
0
);
KPIListSelectUE
combo2
;
combo2
.
setCurrentIndex
(
static_cast
<
int
>
(
PlotTypeUE
::
CIR
));
PainterWidgetUE
pwidgetueCombo2
(
&
config
,
&
combo2
,
ue
);
mainLayout
.
addWidget
(
&
combo2
,
0
,
1
);
mainLayout
.
addWidget
(
&
pwidgetueCombo2
,
1
,
1
);
KPIListSelectUE
combo3
;
combo3
.
setCurrentIndex
(
static_cast
<
int
>
(
PlotTypeUE
::
pbchLLR
));
PainterWidgetUE
pwidgetueCombo3
(
&
config
,
&
combo3
,
ue
);
mainLayout
.
addWidget
(
&
combo3
,
2
,
0
);
mainLayout
.
addWidget
(
&
pwidgetueCombo3
,
3
,
0
);
KPIListSelectUE
combo4
;
combo4
.
setCurrentIndex
(
static_cast
<
int
>
(
PlotTypeUE
::
pbchIQ
));
PainterWidgetUE
pwidgetueCombo4
(
&
config
,
&
combo4
,
ue
);
mainLayout
.
addWidget
(
&
combo4
,
2
,
1
);
mainLayout
.
addWidget
(
&
pwidgetueCombo4
,
3
,
1
);
KPIListSelectUE
combo5
;
combo5
.
setCurrentIndex
(
static_cast
<
int
>
(
PlotTypeUE
::
pdschLLR
));
PainterWidgetUE
pwidgetueCombo5
(
&
config
,
&
combo5
,
ue
);
mainLayout
.
addWidget
(
&
combo5
,
4
,
0
);
mainLayout
.
addWidget
(
&
pwidgetueCombo5
,
5
,
0
);
KPIListSelectUE
combo6
;
combo6
.
setCurrentIndex
(
static_cast
<
int
>
(
PlotTypeUE
::
pdschIQ
));
PainterWidgetUE
pwidgetueCombo6
(
&
config
,
&
combo6
,
ue
);
mainLayout
.
addWidget
(
&
combo6
,
4
,
1
);
mainLayout
.
addWidget
(
&
pwidgetueCombo6
,
5
,
1
);
// display the main window
window
.
show
();
a
.
exec
();
return
nullptr
;
}
// gNB scope initialization
void
nrgNBinitQtScope
(
scopeParms_t
*
p
)
{
scopeData_t
*
scope
=
(
scopeData_t
*
)
malloc
(
sizeof
(
scopeData_t
));
scope
->
gNB
=
p
->
gNB
;
scope
->
argc
=
p
->
argc
;
scope
->
argv
=
p
->
argv
;
scope
->
ru
=
p
->
ru
;
p
->
gNB
->
scopeData
=
scope
;
pthread_t
qtscope_thread
;
threadCreate
(
&
qtscope_thread
,
nrgNBQtscopeThread
,
scope
,
(
char
*
)
"qtscope"
,
-
1
,
sched_get_priority_min
(
SCHED_RR
));
}
// UE scope initialization
void
nrUEinitQtScope
(
PHY_VARS_NR_UE
*
ue
)
{
scopeData_t
*
scope
=
(
scopeData_t
*
)
malloc
(
sizeof
(
scopeData_t
));
scope
->
liveData
=
calloc
(
sizeof
(
scopeGraphData_t
*
),
UEdataTypeNumberOfItems
);
scope
->
copyData
=
UEcopyData
;
ue
->
scopeData
=
scope
;
pthread_t
qtscope_thread
;
threadCreate
(
&
qtscope_thread
,
nrUEQtscopeThread
,
ue
,
(
char
*
)
"qtscope"
,
-
1
,
sched_get_priority_min
(
SCHED_RR
));
}
extern
"C"
void
nrqtscope_autoinit
(
void
*
dataptr
)
{
AssertFatal
((
IS_SOFTMODEM_GNB_BIT
||
IS_SOFTMODEM_5GUE_BIT
),
"Scope cannot find NRUE or GNB context"
);
if
(
IS_SOFTMODEM_GNB_BIT
)
nrgNBinitQtScope
((
scopeParms_t
*
)
dataptr
);
else
nrUEinitQtScope
((
PHY_VARS_NR_UE
*
)
dataptr
);
}
openair1/PHY/TOOLS/nr_phy_qt_scope.h
0 → 100644
View file @
a0594dd6
/*
* 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
*
* Authors and copyright: Bo Zhao, Marwan Hammouda, Thomas Schlichter (Fraunhofer IIS)
*
* 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
*/
#ifndef QT_SCOPE_MAINWINDOW_H
#define QT_SCOPE_MAINWINDOW_H
#include <QtCharts>
extern
"C"
{
#include <simple_executable.h>
#include <common/utils/system.h>
#include "common/ran_context.h"
#include <openair1/PHY/defs_gNB.h>
#include "PHY/defs_gNB.h"
#include "PHY/defs_nr_UE.h"
#include "PHY/defs_RU.h"
#include "executables/softmodem-common.h"
#include "phy_scope_interface.h"
#include <openair2/LAYER2/NR_MAC_gNB/mac_proto.h>
#include "PHY/CODING/nrPolar_tools/nr_polar_defs.h"
extern
RAN_CONTEXT_t
RC
;
}
/// This is an enum class for the different gNB plot types
enum
class
PlotTypeGnb
{
empty
,
waterFall
,
CIR
,
puschLLR
,
puschIQ
,
puschSNR
,
puschBLER
,
puschMCS
,
puschRETX
,
puschThroughput
,
pdschSNR
,
pdschBLER
,
pdschMCS
,
pdschRETX
,
pdschThroughput
,
pdschRBs
,
config
};
/// This is an enum class fpr the different UE plot types
enum
class
PlotTypeUE
{
empty
,
waterFall
,
CIR
,
pbchLLR
,
pbchIQ
,
pdcchLLR
,
pdcchIQ
,
pdschLLR
,
pdschIQ
,
pdschSNR
,
pdschBLER
,
pdschMCS
,
pdschThroughput
,
pdschRBs
,
frequencyOffset
,
timingAdvance
,
config
};
/// This abstract class defines an interface how the KPIPlot class can access values for the different KPI plot types
class
ValueProvider
{
public:
/// This pure virtual function is meant to provide the KPI value to be plotted
virtual
float
getValue
()
=
0
;
};
/// An editable GUI field for a dialog box to set certain KPI configurations
class
ConfigBoxFloat
:
public
QLineEdit
{
Q_OBJECT
public:
/// Constructor
/// \param valuePtr Pointer to a float value, which can be edited through this ConfigBoxFloat
/// \param parent Optional pointer to parent QWidget
ConfigBoxFloat
(
float
*
valuePtr
,
QWidget
*
parent
=
nullptr
);
public
slots
:
/// This function converts the value in the editable GUI field to float and writes it back to valuePtr
void
readText
();
private:
/// Member variable keeping the pointer to the float value
float
*
valuePtr
;
};
/// Dialog box for configuring gNB KPI Limits
class
KPIConfigGnb
:
public
QWidget
{
Q_OBJECT
public:
/// Constructor
/// \param parent Optional pointer to parent QWidget
explicit
KPIConfigGnb
(
QWidget
*
parent
=
nullptr
);
};
/// Dialog box for configuring UE KPI Limits
class
KPIConfigUE
:
public
QWidget
{
Q_OBJECT
public:
/// Constructor
/// \param parent Optional pointer to parent QWidget
explicit
KPIConfigUE
(
QWidget
*
parent
=
nullptr
);
};
/// drop-down list gNB
class
KPIListSelectGnb
:
public
QComboBox
{
Q_OBJECT
public:
/// Constructor
/// \param parent Optional pointer to parent QWidget
explicit
KPIListSelectGnb
(
QWidget
*
parent
=
nullptr
);
};
/// drop-down list UE
class
KPIListSelectUE
:
public
QComboBox
{
Q_OBJECT
public:
/// Constructor
/// \param parent Optional pointer to parent QWidget
explicit
KPIListSelectUE
(
QWidget
*
parent
=
nullptr
);
};
/// Waterfall plot of RX signal power
class
WaterFall
:
public
QWidget
{
Q_OBJECT
public:
/// Constructor
/// \param values Pointer to the digital I/Q samples
/// \param frame_parms Pointer to the NR_DL_FRAME_PARMS
/// \param parent Optional pointer to parent QWidget
WaterFall
(
complex16
*
values
,
NR_DL_FRAME_PARMS
*
frame_parms
,
QWidget
*
parent
=
nullptr
);
protected:
/// This function is triggered when the own timer expires. It reads data from values and updates the image
/// \param event Pointer to the timer event
virtual
void
timerEvent
(
QTimerEvent
*
event
)
override
;
/// This function is used to draw the image on the GUI
/// \param event Pointer to the paint event
virtual
void
paintEvent
(
QPaintEvent
*
event
)
override
;
/// This function is called to change the WaterFall size
/// \param event Pointer to the resize event
virtual
void
resizeEvent
(
QResizeEvent
*
event
)
override
;
private:
/// Pointer to the digital I/Q samples
complex16
*
values
;
/// Pointer to the NR_DL_FRAME_PARMS
NR_DL_FRAME_PARMS
*
frame_parms
;
/// Pointer to the image
QImage
*
image
;
/// Counter of the drawn lines
int
iteration
;
/// pointer to an array storing per-row average power values
double
*
waterFallAvg
;
};
/// Chart class for plotting the Channel Impulse Response
class
CIRPlot
:
public
QChart
{
Q_OBJECT
public:
/// Constructor
/// \param data Pointer to the CIR data
/// \param len Length of the CIR data
CIRPlot
(
complex16
*
data
,
int
len
);
protected:
/// This function is triggered when the own timer expires. It updates the plotted CIR
/// \param event Pointer to the timer event
virtual
void
timerEvent
(
QTimerEvent
*
event
)
override
;
private:
/// Pointer to the CIR data
complex16
*
data
;
/// Length of the CIR data
int
len
;
/// Line series used to plot the CIR in the chart
QLineSeries
*
series
;
/// Horizontal axis of the chart
QValueAxis
*
axisX
;
/// Vertical axis of the chart
QValueAxis
*
axisY
;
};
/// Chart class for plotting LLRs
class
LLRPlot
:
public
QChart
{
Q_OBJECT
public:
/// Constructor
/// \param data Pointer to the LLR data
/// \param len Length of the LLR data
LLRPlot
(
int16_t
*
data
,
int
len
);
protected:
/// This function is triggered when the own timer expires. It updates the plotted LLR
/// \param event Pointer to the timer event
virtual
void
timerEvent
(
QTimerEvent
*
event
)
override
;
private:
/// Pointer to the LLR data
int16_t
*
data
;
/// Length of the LLR data
int
len
;
/// Scatter series used to plot the LLR in the chart
QScatterSeries
*
series
;
/// Horizontal axis of the chart
QValueAxis
*
axisX
;
/// Vertical axis of the chart
QValueAxis
*
axisY
;
};
/// Chart class for plotting the I/Q constellation diagram
class
IQPlot
:
public
QChart
{
Q_OBJECT
public:
/// Constructor
/// \param data Pointer to the complex I/Q data
/// \param len Length of the I/Q data
IQPlot
(
complex16
*
data
,
int
len
);
protected:
/// This function is triggered when the own timer expires. It updates the plotted I/Q constellation diagram
/// \param event Pointer to the timer event
virtual
void
timerEvent
(
QTimerEvent
*
event
)
override
;
private:
/// Pointer to the I/Q data
complex16
*
data
;
/// Length of the I/Q data
int
len
;
/// Scatter series used to plot the I/Q constellation diagram
QScatterSeries
*
series
;
/// Horizontal axis of the chart
QValueAxis
*
axisX
;
/// Vertical axis of the chart
QValueAxis
*
axisY
;
};
/// Generic class for plotting KPI values with min., max. and average bars
class
KPIPlot
:
public
QChart
{
Q_OBJECT
public:
/// Constructor
/// \param valueProvider Pointer to an instance of a class that implements the ValueProvider interface
/// \param limits Optional parameter pointing to an array of two floating point values indicating lower and upper bounds
KPIPlot
(
ValueProvider
*
valueProvider
,
float
*
limits
=
nullptr
);
protected:
/// This function is triggered when the own timer expires. It updates the plotted KPI chart
/// \param event Pointer to the timer event
virtual
void
timerEvent
(
QTimerEvent
*
event
)
override
;
private:
/// Pointer to an instance of a class that implements the ValueProvider interface
ValueProvider
*
valueProvider
;
/// Pointer to an array of two floating point values indicating lower and upper bounds
float
*
limits
;
/// smallest observed KPI value
float
minValue
;
/// biggest observed KPI value
float
maxValue
;
/// Accumulated KPI valus, used to compute the average
float
sumValue
;
/// Index (horizontal position) of the last plotted KPI value
int
plotIdx
;
/// Line series used to plot the KPI values
QLineSeries
*
series
;
/// Line series used to plot an indication of the smallest observed KPI value
QLineSeries
*
seriesMin
;
/// Line series used to plot an indication of the biggest observed KPI value
QLineSeries
*
seriesMax
;
/// Line series used to plot an indication of the average KPI value
QLineSeries
*
seriesAvg
;
/// Horizontal axis of the chart
QValueAxis
*
axisX
;
/// Vertical axis of the chart
QValueAxis
*
axisY
;
};
/// Crart class for plotting HARQ retransmission counters
class
RTXPlot
:
public
QChart
{
Q_OBJECT
public:
/// Constructor
/// \param rounds Pointer to the HARQ round counters
RTXPlot
(
uint64_t
*
rounds
);
protected:
/// This function is triggered when the own timer expires. It updates the plotted HARQ retransmission counters
/// \param event Pointer to the timer event
virtual
void
timerEvent
(
QTimerEvent
*
event
)
override
;
private:
/// Pointer to the HARQ round counters
uint64_t
*
rounds
;
/// Last stored HARQ round counters
uint64_t
lastRounds
[
4
];
/// Maximum observed HARQ retransmissions
int
maxValue
;
/// Index (horizontal position) of the last plotted HARQ retransmission value
int
plotIdx
;
/// Line series used to plot the four HARQ retransmission counters
QLineSeries
*
series
[
4
];
/// Horizontal axis of the chart
QValueAxis
*
axisX
;
/// Vertical axis of the chart
QValueAxis
*
axisY
;
};
/// Widget showing one selectable gNB KPI
class
PainterWidgetGnb
:
public
QWidget
,
public
ValueProvider
{
Q_OBJECT
public:
/// Constructor
/// \param config Pointer to the dialog box for configuring gNB KPI Limits
/// \param comboBox Pointer to the drop-down list selecting the KPI to be shown here
/// \param p Pointer to the gNB parameters
PainterWidgetGnb
(
QWidget
*
config
,
QComboBox
*
comboBox
,
scopeData_t
*
p
);
/// This function provides the current KPI value to be plotted
virtual
float
getValue
()
override
;
protected:
/// This function is called to change the widget size
/// \param event Pointer to the resize event
virtual
void
resizeEvent
(
QResizeEvent
*
event
)
override
;
public
slots
:
/// This function is called when a different KPI is selected
/// \param type selected KPI type
void
makeConnections
(
int
type
);
private:
/// Pointer to the dialog box for configuring gNB KPI Limits
QWidget
*
config
;
/// Pointer to the drop-down list selecting the KPI to be shown here
QComboBox
*
comboBox
;
/// Pointer to the gNB parameters
scopeData_t
*
p
;
/// Pointer to a class to view all QChart based KPIs
QChartView
*
chartView
;
/// Pointer to the waterfall diagram
WaterFall
*
waterFall
;
/// Currently plotted KPI type
PlotTypeGnb
plotType
;
};
/// Widget showing one selectable UE KPI
class
PainterWidgetUE
:
public
QWidget
,
public
ValueProvider
{
Q_OBJECT
public:
/// Constructor
/// \param config Pointer to the dialog box for configuring UE KPI Limits
/// \param comboBox Pointer to the drop-down list selecting the KPI to be shown here
/// \param ue Pointer to the UE parameters
PainterWidgetUE
(
QWidget
*
config
,
QComboBox
*
comboBox
,
PHY_VARS_NR_UE
*
ue
);
/// This function provides the current KPI value to be plotted
virtual
float
getValue
()
override
;
protected:
/// This function is called to change the widget size
/// \param event Pointer to the resize event
virtual
void
resizeEvent
(
QResizeEvent
*
event
)
override
;
public
slots
:
/// This function is called when a different KPI is selected
/// \param type selected KPI type
void
makeConnections
(
int
type
);
private:
/// Pointer to the dialog box for configuring UE KPI Limits
QWidget
*
config
;
/// Pointer to the drop-down list selecting the KPI to be shown here
QComboBox
*
comboBox
;
/// Pointer to the UE parameters
PHY_VARS_NR_UE
*
ue
;
/// Pointer to a class to view all QChart based KPIs
QChartView
*
chartView
;
/// Pointer to the waterfall diagram
WaterFall
*
waterFall
;
/// Currently plotted KPI type
PlotTypeUE
plotType
;
};
#endif // QT_SCOPE_MAINWINDOW_H
openair1/PHY/TOOLS/nr_phy_scope.c
View file @
a0594dd6
...
...
@@ -1042,6 +1042,7 @@ STATICFORXSCOPE OAI_phy_scope_t *create_phy_scope_nrue(int ID)
// LLR of PDCCH
fdui
->
graph
[
5
]
=
nrUEcommonGraph
(
uePcchLLR
,
FL_POINTS_XYPLOT
,
0
,
curY
,
500
,
100
,
"PDCCH Log-Likelihood Ratios (LLR, mag)"
,
FL_CYAN
);
fl_set_xyplot_xgrid
(
fdui
->
graph
[
5
].
graph
,
FL_GRID_MAJOR
);
fdui
->
graph
[
5
].
chartid
=
SCOPEMSG_DATAID_LLR
;
// tells websrv frontend to use LLR chart for displaying
fdui
->
graph
[
5
].
datasetid
=
1
;
// tells websrv frontend to use dataset index 1 in LLR chart
// I/Q PDCCH comp
...
...
@@ -1054,6 +1055,7 @@ STATICFORXSCOPE OAI_phy_scope_t *create_phy_scope_nrue(int ID)
// LLR of PDSCH
fdui
->
graph
[
7
]
=
nrUEcommonGraph
(
uePdschLLR
,
FL_POINTS_XYPLOT
,
0
,
curY
,
500
,
200
,
"PDSCH Log-Likelihood Ratios (LLR, mag)"
,
FL_YELLOW
);
fl_set_xyplot_xgrid
(
fdui
->
graph
[
7
].
graph
,
FL_GRID_MAJOR
);
fdui
->
graph
[
7
].
chartid
=
SCOPEMSG_DATAID_LLR
;
// tells websrv frontend to use LLR chart for displaying
fdui
->
graph
[
7
].
datasetid
=
2
;
// tells websrv frontend to use dataset index 2 in LLR chart
// I/Q PDSCH comp
...
...
@@ -1139,49 +1141,6 @@ static void *nrUEscopeThread(void *arg) {
pthread_mutex_t
UEcopyDataMutex
;
void
UEcopyData
(
PHY_VARS_NR_UE
*
ue
,
enum
UEdataType
type
,
void
*
dataIn
,
int
elementSz
,
int
colSz
,
int
lineSz
)
{
// Local static copy of the scope data bufs
// The active data buf is alterned to avoid interference between the Scope thread (display) and the Rx thread (data input)
// Index of "2" could be set to the number of Rx threads + 1
static
scopeGraphData_t
*
copyDataBufs
[
UEdataTypeNumberOfItems
][
3
]
=
{
0
};
static
int
copyDataBufsIdx
[
UEdataTypeNumberOfItems
]
=
{
0
};
scopeData_t
*
tmp
=
(
scopeData_t
*
)
ue
->
scopeData
;
if
(
tmp
)
{
// Begin of critical zone between UE Rx threads that might copy new data at the same time:
pthread_mutex_lock
(
&
UEcopyDataMutex
);
int
newCopyDataIdx
=
(
copyDataBufsIdx
[
type
]
<
2
)
?
copyDataBufsIdx
[
type
]
+
1
:
0
;
copyDataBufsIdx
[
type
]
=
newCopyDataIdx
;
pthread_mutex_unlock
(
&
UEcopyDataMutex
);
// End of critical zone between UE Rx threads
// New data will be copied in a different buffer than the live one
scopeGraphData_t
*
copyData
=
copyDataBufs
[
type
][
newCopyDataIdx
];
if
(
copyData
==
NULL
||
copyData
->
dataSize
<
elementSz
*
colSz
*
lineSz
)
{
scopeGraphData_t
*
ptr
=
realloc
(
copyData
,
sizeof
(
scopeGraphData_t
)
+
elementSz
*
colSz
*
lineSz
);
if
(
!
ptr
)
{
LOG_E
(
PHY
,
"can't realloc
\n
"
);
return
;
}
else
{
copyData
=
ptr
;
}
}
copyData
->
dataSize
=
elementSz
*
colSz
*
lineSz
;
copyData
->
elementSz
=
elementSz
;
copyData
->
colSz
=
colSz
;
copyData
->
lineSz
=
lineSz
;
memcpy
(
copyData
+
1
,
dataIn
,
elementSz
*
colSz
*
lineSz
);
copyDataBufs
[
type
][
newCopyDataIdx
]
=
copyData
;
// The new data just copied in the local static buffer becomes live now
((
scopeGraphData_t
**
)
tmp
->
liveData
)[
type
]
=
copyData
;
}
}
STATICFORXSCOPE
void
nrUEinitScope
(
PHY_VARS_NR_UE
*
ue
)
{
AssertFatal
(
ue
->
scopeData
=
malloc
(
sizeof
(
scopeData_t
)),
""
);
...
...
openair1/PHY/TOOLS/phy_scope.h
View file @
a0594dd6
...
...
@@ -40,13 +40,6 @@
typedef
c16_t
scopeSample_t
;
#define SquaredNorm(VaR) ((VaR).r * (VaR).r + (VaR).i * (VaR).i)
typedef
struct
{
int
dataSize
;
int
elementSz
;
int
colSz
;
int
lineSz
;
}
scopeGraphData_t
;
typedef
struct
OAIgraph
{
FL_OBJECT
*
graph
;
FL_OBJECT
*
text
;
...
...
openair1/PHY/TOOLS/phy_scope_interface.c
View file @
a0594dd6
...
...
@@ -52,3 +52,44 @@ int end_forms(void) {
return
-
1
;
}
void
UEcopyData
(
PHY_VARS_NR_UE
*
ue
,
enum
UEdataType
type
,
void
*
dataIn
,
int
elementSz
,
int
colSz
,
int
lineSz
)
{
// Local static copy of the scope data bufs
// The active data buf is alterned to avoid interference between the Scope thread (display) and the Rx thread (data input)
// Index of "2" could be set to the number of Rx threads + 1
static
scopeGraphData_t
*
copyDataBufs
[
UEdataTypeNumberOfItems
][
2
]
=
{
0
};
static
int
copyDataBufsIdx
[
UEdataTypeNumberOfItems
]
=
{
0
};
scopeData_t
*
tmp
=
(
scopeData_t
*
)
ue
->
scopeData
;
if
(
tmp
)
{
// Begin of critical zone between UE Rx threads that might copy new data at the same time: might require a mutex
int
newCopyDataIdx
=
(
copyDataBufsIdx
[
type
]
==
0
)
?
1
:
0
;
copyDataBufsIdx
[
type
]
=
newCopyDataIdx
;
// End of critical zone between UE Rx threads
// New data will be copied in a different buffer than the live one
scopeGraphData_t
*
copyData
=
copyDataBufs
[
type
][
newCopyDataIdx
];
if
(
copyData
==
NULL
||
copyData
->
dataSize
<
elementSz
*
colSz
*
lineSz
)
{
scopeGraphData_t
*
ptr
=
(
scopeGraphData_t
*
)
realloc
(
copyData
,
sizeof
(
scopeGraphData_t
)
+
elementSz
*
colSz
*
lineSz
);
if
(
!
ptr
)
{
LOG_E
(
PHY
,
"can't realloc
\n
"
);
return
;
}
else
{
copyData
=
ptr
;
}
}
copyData
->
dataSize
=
elementSz
*
colSz
*
lineSz
;
copyData
->
elementSz
=
elementSz
;
copyData
->
colSz
=
colSz
;
copyData
->
lineSz
=
lineSz
;
memcpy
(
copyData
+
1
,
dataIn
,
elementSz
*
colSz
*
lineSz
);
copyDataBufs
[
type
][
newCopyDataIdx
]
=
copyData
;
// The new data just copied in the local static buffer becomes live now
((
scopeGraphData_t
**
)
tmp
->
liveData
)[
type
]
=
copyData
;
}
}
openair1/PHY/TOOLS/phy_scope_interface.h
View file @
a0594dd6
...
...
@@ -34,6 +34,14 @@
#include <openair1/PHY/defs_gNB.h>
#include <openair1/PHY/defs_nr_UE.h>
typedef
struct
{
uint32_t
nb_total
;
uint32_t
nb_nack
;
uint32_t
blockSize
;
// block size, to be used for throughput calculation
uint16_t
nofRBs
;
uint8_t
dl_mcs
;
}
extended_kpi_ue
;
typedef
struct
{
int
*
argc
;
char
**
argv
;
...
...
@@ -63,8 +71,19 @@ typedef struct scopeData_s {
void
(
*
copyData
)(
PHY_VARS_NR_UE
*
,
enum
UEdataType
,
void
*
data
,
int
elementSz
,
int
colSz
,
int
lineSz
);
}
scopeData_t
;
typedef
struct
{
int
dataSize
;
int
elementSz
;
int
colSz
;
int
lineSz
;
}
scopeGraphData_t
;
int
load_softscope
(
char
*
exectype
,
void
*
initarg
);
int
end_forms
(
void
)
;
void
UEcopyData
(
PHY_VARS_NR_UE
*
ue
,
enum
UEdataType
type
,
void
*
dataIn
,
int
elementSz
,
int
colSz
,
int
lineSz
);
#define UEscopeCopy(ue, type, ...) if(ue->scopeData) ((scopeData_t*)ue->scopeData)->copyData(ue, type, ##__VA_ARGS__);
extended_kpi_ue
*
getKPIUE
();
#endif
openair1/PHY/TOOLS/readme.md
View file @
a0594dd6
To use the scope, run the xNB or the UE with option "-d"
## xForms-based Scope
To use the scope, run the xNB or the UE with option "-d"
Usage in gdb
In gdb, when you break, you can refresh immediatly the scope view by calling the display function.
The first paramter is the graph context, nevertheless we keep the last value for a dirty call in gdb (so you can use '0')
The first paramter is the graph context, nevertheless we keep the last value for a dirty call in gdb (so you can use '0')
Example with no variable known
phy_scope_nrUE(0, PHY_vars_UE_g
[
0
][
0
]
, 0, 0, 0)
or
phy_scope_gNB(0, phy_vars_gnb, phy_vars_ru, UE_id)
# Qt-based Scope
## Building Instuctions
For the new qt-based scopo designed for NR, please consider the following:
1.
run the gNB or the UE with the option '--dqt'.
2.
make sure to install the Qt5 packages before running the scope. Otherwise, the scope will NOT be displayed!
3.
if you need only to build the new scope, then add 'nrqtscope' after the '--lib-build' option. So, the complete
command would be
```
./build_oai --gNB -w USRP --nrUE --build-lib nrqtscope
```
## New Features
1.
New KPIs for both gNB and UE, e.g., BLER, MCS, throughout, and number of scheduled RBs.
2.
For each of the gNB and UE, a main widget is created with a 3x2 grid of sub-widgets, each to dispaly one KPI.
3.
Each of the sub-widgets has a drop-down list to choose the KPI to show in that sub-widget.
4.
Both of the gNB and UE scopes can be resized using the mouse movement.
openair1/PHY/defs_nr_UE.h
View file @
a0594dd6
...
...
@@ -36,8 +36,6 @@
#include "defs_nr_common.h"
#include "CODING/nrPolar_tools/nr_polar_pbch_defs.h"
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
...
...
openair1/PHY/impl_defs_nr.h
View file @
a0594dd6
...
...
@@ -139,7 +139,7 @@ typedef struct TDD_UL_DL_configCommon_s {
struct
TDD_UL_DL_configCommon_s
*
p_next
;
}
TDD_UL_DL_configCommon_t
;
typedef
struct
{
typedef
struct
TDD_UL_DL_SlotConfig_s
{
/// \ Identifies a slot within a dl-UL-TransmissionPeriodicity (given in tdd-UL-DL-configurationCommon)
uint16_t
slotIndex
;
/// \ The direction (downlink or uplink) for the symbols in this slot. "allDownlink" indicates that all symbols in this slot are used
...
...
@@ -154,7 +154,7 @@ typedef struct {
/// Corresponds to L1 parameter 'number-of-UL-symbols-dedicated' (see 38.211, section FFS_Section)
uint16_t
nrofUplinkSymbols
;
/// \ for setting a sequence
struct
TDD_UL_DL_SlotConfig_
t
*
p_next_TDD_UL_DL_SlotConfig
;
struct
TDD_UL_DL_SlotConfig_
s
*
p_next_TDD_UL_DL_SlotConfig
;
}
TDD_UL_DL_SlotConfig_t
;
/***********************************************************************
...
...
openair1/PHY/phy_extern_nr_ue.h
View file @
a0594dd6
...
...
@@ -27,7 +27,7 @@
#ifdef XFORMS
#include "PHY/TOOLS/nr_phy_scope.h"
extern
char
do_forms
;
extern
uint32_t
do_forms
;
#endif
extern
char
*
namepointer_chMag
;
...
...
openair1/SCHED_NR/phy_frame_config_nr.c
View file @
a0594dd6
...
...
@@ -187,7 +187,7 @@ void add_tdd_dedicated_configuration_nr(NR_DL_FRAME_PARMS *frame_parms, int slot
if
(
next
==
0
)
{
frame_parms
->
p_TDD_UL_DL_ConfigDedicated
=
p_TDD_UL_DL_ConfigDedicated
;
}
else
{
p_previous_TDD_UL_DL_ConfigDedicated
->
p_next_TDD_UL_DL_SlotConfig
=
(
struct
TDD_UL_DL_SlotConfig_t
*
)
p_TDD_UL_DL_ConfigDedicated
;
p_previous_TDD_UL_DL_ConfigDedicated
->
p_next_TDD_UL_DL_SlotConfig
=
(
TDD_UL_DL_SlotConfig_t
*
)
p_TDD_UL_DL_ConfigDedicated
;
}
p_TDD_UL_DL_ConfigDedicated
->
slotIndex
=
slotIndex
;
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment