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
spbro
OpenXG-RAN
Commits
394431a5
Commit
394431a5
authored
Jul 12, 2024
by
Thomas Schlichter
Committed by
Robert Schmidt
Jul 13, 2024
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
remove nrqtscope
parent
ae2a79e5
Changes
14
Hide whitespace changes
Inline
Side-by-side
Showing
14 changed files
with
6 additions
and
2051 deletions
+6
-2051
CMakeLists.txt
CMakeLists.txt
+0
-15
cmake_targets/build_oai
cmake_targets/build_oai
+1
-1
cmake_targets/tools/build_helper
cmake_targets/tools/build_helper
+0
-1
doc/BUILD.md
doc/BUILD.md
+2
-4
doc/Doxyfile
doc/Doxyfile
+0
-1
doc/README.md
doc/README.md
+1
-1
docker/Dockerfile.build.ubuntu22
docker/Dockerfile.build.ubuntu22
+1
-1
executables/nr-softmodem.c
executables/nr-softmodem.c
+0
-9
executables/nr-uesoftmodem.c
executables/nr-uesoftmodem.c
+0
-4
executables/softmodem-common.c
executables/softmodem-common.c
+1
-5
executables/softmodem-common.h
executables/softmodem-common.h
+0
-4
openair1/PHY/TOOLS/nr_phy_qt_scope.cpp
openair1/PHY/TOOLS/nr_phy_qt_scope.cpp
+0
-1379
openair1/PHY/TOOLS/nr_phy_qt_scope.h
openair1/PHY/TOOLS/nr_phy_qt_scope.h
+0
-563
openair1/PHY/TOOLS/readme.md
openair1/PHY/TOOLS/readme.md
+0
-63
No files found.
CMakeLists.txt
View file @
394431a5
...
...
@@ -1885,21 +1885,6 @@ set (SIMUSRC
add_library
(
SIMU STATIC
${
SIMUSRC
}
)
target_include_directories
(
SIMU PUBLIC
${
OPENAIR1_DIR
}
/SIMULATION/TOOLS
${
OPENAIR1_DIR
}
/SIMULATION/RF
)
# 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
${
OPENAIR1_DIR
}
/PHY/TOOLS/phy_scope_interface.c
)
# 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_hdrs asn1_lte_rrc_hdrs
)
endif
()
add_library
(
SIMU_ETH STATIC
${
OPENAIR1_DIR
}
/SIMULATION/ETH_TRANSPORT/netlink_init.c
${
OPENAIR1_DIR
}
/SIMULATION/ETH_TRANSPORT/multicast_link.c
...
...
cmake_targets/build_oai
View file @
394431a5
...
...
@@ -46,7 +46,7 @@ BUILD_DOXYGEN=0
DISABLE_HARDWARE_DEPENDENCY
=
"False"
CMAKE_BUILD_TYPE
=
"RelWithDebInfo"
CMAKE_CMD
=
"
$CMAKE
"
OPTIONAL_LIBRARIES
=
"telnetsrv enbscope uescope nrscope
nrqtscope
ldpc_cuda ldpc_t2 websrv oai_iqplayer"
OPTIONAL_LIBRARIES
=
"telnetsrv enbscope uescope nrscope ldpc_cuda ldpc_t2 websrv oai_iqplayer"
TARGET_LIST
=
""
function
print_help
()
{
...
...
cmake_targets/tools/build_helper
View file @
394431a5
...
...
@@ -541,7 +541,6 @@ check_install_additional_tools (){
doxygen \
libgnutls28-dev \
libpthread-stubs0-dev \
libqt5charts5-dev \
tshark \
uml-utilities \
iperf3 \
...
...
doc/BUILD.md
View file @
394431a5
...
...
@@ -174,16 +174,14 @@ Using the help option of the build script you can get the list of available opti
```
bash
./build_oai
--build-lib
all
# build all
./build_oai
--build-lib
telnetsrv
# build only telnetsrv
./build_oai
--build-lib
"telnetsrv enbscope uescope nrscope nrqtscope"
./build_oai
--build-lib
telnetsrv
--build-lib
nrqtscope
./build_oai
--build-lib
"telnetsrv enbscope uescope nrscope"
```
The following libraries are build in CI and should always work:
`telnetsrv`
,
`enbscope`
,
`uescope`
,
`nrscope`
,
`nrqtscope`
.
`enbscope`
,
`uescope`
,
`nrscope`
.
Some libraries have further dependencies and might not build on every system:
-
`enbscope`
,
`uescope`
,
`nrscope`
: libforms/X
-
`nrqtscope`
: Qt5
-
`ldpc_cuda`
: CUDA
-
`websrv`
: npm and others
-
`ldpc_t2`
: DPDK with patch
...
...
doc/Doxyfile
View file @
394431a5
...
...
@@ -2291,7 +2291,6 @@ INPUT = \
@CMAKE_CURRENT_SOURCE_DIR@/../openair1/PHY/TOOLS/nr_phy_scope.c \
@CMAKE_CURRENT_SOURCE_DIR@/../openair1/PHY/TOOLS/nr_phy_scope.h \
@CMAKE_CURRENT_SOURCE_DIR@/../openair1/PHY/TOOLS/lte_phy_scope.c \
@CMAKE_CURRENT_SOURCE_DIR@/../openair1/PHY/TOOLS/nr_phy_qt_scope.h \
@CMAKE_CURRENT_SOURCE_DIR@/../openair1/PHY/TOOLS/cadd_vv.c \
@CMAKE_CURRENT_SOURCE_DIR@/../openair1/PHY/TOOLS/phy_scope.h \
@CMAKE_CURRENT_SOURCE_DIR@/../openair1/PHY/TOOLS/signal_energy.c \
...
...
doc/README.md
View file @
394431a5
...
...
@@ -105,7 +105,7 @@ The other SDRs (AW2S, LimeSDR, ...) have no READMEs.
## Special-purpose libraries
-
OAI has
two scopes, one based on Xforms, one on Qt5
, described in
[
this README
](
../openair1/PHY/TOOLS/readme.md
)
-
OAI has
a scope based on Xforms
, described in
[
this README
](
../openair1/PHY/TOOLS/readme.md
)
-
OAI comes with an integrated
[
telnet server
](
../common/utils/telnetsrv/DOC/telnethelp.md
)
to monitor and control
-
OAI comes with an integrated
[
web server
](
../common/utils/websrv/DOC/websrv.md
)
...
...
docker/Dockerfile.build.ubuntu22
View file @
394431a5
...
...
@@ -38,7 +38,7 @@ RUN /bin/sh oaienv && \
mkdir -p log && \
./build_oai -c --ninja \
--eNB --gNB --RU --UE --nrUE \
--build-lib "telnetsrv enbscope uescope nrscope
nrqtscope
" \
--build-lib "telnetsrv enbscope uescope nrscope" \
-w USRP -t Ethernet \
--build-e2 --cmake-opt -DXAPP_MULTILANGUAGE=OFF \
--noavx512 \
...
...
executables/nr-softmodem.c
View file @
394431a5
...
...
@@ -769,15 +769,6 @@ 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
&&
NFAPI_MODE
!=
NFAPI_MODE_AERIAL
)
{
printf
(
"Not NFAPI mode - call init_eNB_afterRU()
\n
"
);
init_eNB_afterRU
();
...
...
executables/nr-uesoftmodem.c
View file @
394431a5
...
...
@@ -594,10 +594,6 @@ int main(int argc, char **argv)
init_openair0
();
lock_memory_to_ram
();
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 @
394431a5
...
...
@@ -98,7 +98,7 @@ void get_common_options(configmodule_interface_t *cfg, 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
,
do_forms_qt
=
0
;
uint32_t
rfsim
=
0
,
do_forms
=
0
;
int
nfapi_index
=
0
;
char
*
logmem_filename
=
NULL
;
check_execmask
(
execmask
);
...
...
@@ -162,10 +162,6 @@ void get_common_options(configmodule_interface_t *cfg, 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 @
394431a5
...
...
@@ -70,7 +70,6 @@ extern "C"
#define CONFIG_HLP_SLF "Set the sidelink frequency 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"
...
...
@@ -168,7 +167,6 @@ extern int usrp_tx_thread;
{"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=&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, 0, .iptr=&NUMEROLOGY, .defintval=1, TYPE_INT, 0}, \
{"band" , CONFIG_HLP_BAND, 0, .iptr=&BAND, .defintval=78, TYPE_INT, 0}, \
...
...
@@ -298,7 +296,6 @@ 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}}
...
...
@@ -311,7 +308,6 @@ 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_IQRECORDER ( get_softmodem_optmask() & SOFTMODEM_RECRECORD_BIT)
#define IS_SOFTMODEM_TELNETCLT_BIT ( get_softmodem_optmask() & SOFTMODEM_TELNETCLT_BIT)
...
...
openair1/PHY/TOOLS/nr_phy_qt_scope.cpp
deleted
100644 → 0
View file @
ae2a79e5
/*
* 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
[
3
][
2
]
=
{
// {lower Limit, Upper Limit}
{
0.0
,
0.8
},
// DL BLER
{
0.2
,
10
},
// Throughput in Mbs
{
0
,
60
}
// psbch RSRP db/RE
};
// Plot updater
PlotUpdater
puschLlrUpdater
;
PlotUpdater
puschIqUpdater
;
void
scopeUpdaterGnb
(
enum
PlotTypeGnbIf
plotType
,
int
numElt
)
{
switch
(
plotType
)
{
case
puschLLRe
:
puschLlrUpdater
.
updatePlot
(
numElt
);
break
;
case
puschIQe
:
puschIqUpdater
.
updatePlot
(
numElt
);
break
;
}
}
/* 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
));
this
->
addItem
(
"LLR PSBCH"
,
static_cast
<
int
>
(
PlotTypeUE
::
psbchLLR
));
this
->
addItem
(
"I/Q PSBCH"
,
static_cast
<
int
>
(
PlotTypeUE
::
psbchIQ
));
this
->
addItem
(
"PSBCH RSRP dB/RE"
,
static_cast
<
int
>
(
PlotTypeUE
::
psbchRSRP
));
}
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
);
}
void
CIRPlotUE
::
timerEvent
(
QTimerEvent
*
event
)
{
if
(
!
this
->
isVisible
())
return
;
scopeGraphData_t
*
data
=
this
->
valueProvider
->
getPlotValue
();
if
(
data
)
{
this
->
data
=
(
complex16
*
)(
data
+
1
);
this
->
len
=
data
->
lineSz
;
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
->
axisX
->
setRange
(
-
this
->
len
/
2
,
this
->
len
/
2
);
this
->
axisY
->
setMax
(
maxY
);
this
->
series
->
replace
(
points
);
}
}
LLRPlot
::
LLRPlot
(
int16_t
*
data
,
int
len
,
int
interval
,
PlotUpdater
*
plotUpdater
)
:
data
(
data
),
len
(
len
),
plotUpdater
(
plotUpdater
)
{
this
->
legend
()
->
hide
();
// add new series to the chart
this
->
series
=
new
QScatterSeries
();
this
->
series
->
setUseOpenGL
();
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
->
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
);
if
(
interval
)
startTimer
(
interval
);
if
(
plotUpdater
)
connect
(
plotUpdater
,
&
PlotUpdater
::
updatePlot
,
this
,
&
LLRPlot
::
updatePlot
,
Qt
::
QueuedConnection
);
}
LLRPlot
::~
LLRPlot
()
{
if
(
this
->
plotUpdater
)
disconnect
(
this
->
plotUpdater
,
&
PlotUpdater
::
updatePlot
,
this
,
&
LLRPlot
::
updatePlot
);
}
void
LLRPlot
::
updatePlot
(
int
len
)
{
this
->
len
=
len
;
if
(
!
this
->
isVisible
())
return
;
QVector
<
QPointF
>
points
(
len
);
int
maxY
=
this
->
axisY
->
max
();
for
(
int
i
=
0
;
i
<
len
;
i
++
)
{
points
[
i
]
=
QPointF
(
i
,
this
->
data
[
i
]);
maxY
=
std
::
max
(
maxY
,
abs
(
this
->
data
[
i
]));
}
this
->
axisX
->
setRange
(
0
,
len
);
this
->
axisY
->
setRange
(
-
maxY
,
maxY
);
this
->
series
->
replace
(
points
);
}
void
LLRPlot
::
timerEvent
(
QTimerEvent
*
event
)
{
this
->
updatePlot
(
this
->
len
);
}
void
LLRPlotUE
::
timerEvent
(
QTimerEvent
*
event
)
{
if
(
!
this
->
isVisible
())
return
;
scopeGraphData_t
*
data
=
this
->
valueProvider
->
getPlotValue
();
if
(
data
)
{
this
->
data
=
(
int16_t
*
)(
data
+
1
);
this
->
len
=
data
->
lineSz
;
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
->
axisX
->
setRange
(
0
,
this
->
len
);
this
->
axisY
->
setRange
(
-
maxY
,
maxY
);
this
->
series
->
replace
(
points
);
}
}
IQPlot
::
IQPlot
(
complex16
*
data
,
int
len
,
int
interval
,
PlotUpdater
*
plotUpdater
)
:
data
(
data
),
len
(
len
),
plotUpdater
(
plotUpdater
)
{
this
->
legend
()
->
hide
();
// add new series to the chart
this
->
series
=
new
QScatterSeries
();
this
->
series
->
setUseOpenGL
();
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
);
if
(
interval
)
startTimer
(
interval
);
if
(
plotUpdater
)
connect
(
plotUpdater
,
&
PlotUpdater
::
updatePlot
,
this
,
&
IQPlot
::
updatePlot
,
Qt
::
QueuedConnection
);
}
IQPlot
::~
IQPlot
()
{
if
(
this
->
plotUpdater
)
disconnect
(
this
->
plotUpdater
,
&
PlotUpdater
::
updatePlot
,
this
,
&
IQPlot
::
updatePlot
);
}
void
IQPlot
::
updatePlot
(
int
len
)
{
this
->
len
=
len
;
if
(
!
this
->
isVisible
())
return
;
QVector
<
QPointF
>
points
(
len
);
int
maxX
=
this
->
axisX
->
max
();
int
maxY
=
this
->
axisY
->
max
();
for
(
int
i
=
0
;
i
<
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
);
}
void
IQPlot
::
timerEvent
(
QTimerEvent
*
event
)
{
this
->
updatePlot
(
this
->
len
);
}
void
IQPlotUE
::
timerEvent
(
QTimerEvent
*
event
)
{
if
(
!
this
->
isVisible
())
return
;
scopeGraphData_t
*
data
=
this
->
valueProvider
->
getPlotValue
();
if
(
data
)
{
this
->
data
=
(
complex16
*
)(
data
+
1
);
this
->
len
=
data
->
lineSz
;
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
init_coded_bits_per_codeword
=
100
;
newChart
=
new
LLRPlot
((
int16_t
*
)
p
->
gNB
->
pusch_vars
[
0
].
llr
,
init_coded_bits_per_codeword
,
0
,
&
puschLlrUpdater
);
break
;
}
case
PlotTypeGnb
:
:
puschIQ
:
{
int
init_num_re
=
100
;
newChart
=
new
IQPlot
((
complex16
*
)
p
->
gNB
->
pusch_vars
[
0
].
rxdataF_comp
[
0
],
init_num_re
,
0
,
&
puschIqUpdater
);
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
;
case
PlotTypeUE
:
:
psbchRSRP
:
return
(
float
)
this
->
ue
->
SL_UE_PHY_PARAMS
.
psbch
.
rsrp_dB_per_RE
;
default:
return
0
;
}
}
scopeGraphData_t
*
PainterWidgetUE
::
getPlotValue
()
{
scopeData_t
*
scope
=
(
scopeData_t
*
)
this
->
ue
->
scopeData
;
scopeGraphData_t
**
data
=
(
scopeGraphData_t
**
)
scope
->
liveData
;
bool
is_sl
=
this
->
ue
->
sl_mode
;
switch
(
this
->
plotType
)
{
case
PlotTypeUE
:
:
CIR
:
return
(
is_sl
?
data
[
psbchDlChEstimateTime
]
:
data
[
pbchDlChEstimateTime
]);
case
PlotTypeUE
:
:
pbchLLR
:
return
data
[
pbchLlr
];
case
PlotTypeUE
:
:
pbchIQ
:
return
data
[
pbchRxdataF_comp
];
case
PlotTypeUE
:
:
psbchLLR
:
return
data
[
psbchLlr
];
case
PlotTypeUE
:
:
psbchIQ
:
return
data
[
psbchRxdataF_comp
];
case
PlotTypeUE
:
:
pdcchLLR
:
return
data
[
pdcchLlr
];
case
PlotTypeUE
:
:
pdcchIQ
:
return
data
[
pdcchRxdataF_comp
];
case
PlotTypeUE
:
:
pdschLLR
:
return
data
[
pdschLlr
];
case
PlotTypeUE
:
:
pdschIQ
:
return
data
[
pdschRxdataF_comp
];
default:
return
nullptr
;
}
}
/* @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
:
{
enum
scopeDataType
typ
=
(
this
->
ue
->
sl_mode
)
?
psbchDlChEstimateTime
:
pbchDlChEstimateTime
;
if
(
!
data
[
typ
])
{
newChart
=
new
QChart
();
this
->
plotType
=
PlotTypeUE
::
empty
;
this
->
comboBox
->
setCurrentIndex
(
static_cast
<
int
>
(
PlotTypeUE
::
empty
));
break
;
}
newChart
=
new
CIRPlot
((
complex16
*
)(
data
[
typ
]
+
1
),
data
[
typ
]
->
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
LLRPlotUE
((
int16_t
*
)(
data
[
pbchLlr
]
+
1
),
data
[
pbchLlr
]
->
lineSz
,
this
);
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
IQPlotUE
((
complex16
*
)(
data
[
pbchRxdataF_comp
]
+
1
),
data
[
pbchRxdataF_comp
]
->
lineSz
,
this
);
break
;
}
case
PlotTypeUE
:
:
psbchLLR
:
{
if
(
!
data
[
psbchLlr
])
{
newChart
=
new
QChart
();
this
->
plotType
=
PlotTypeUE
::
empty
;
this
->
comboBox
->
setCurrentIndex
(
static_cast
<
int
>
(
PlotTypeUE
::
empty
));
break
;
}
newChart
=
new
LLRPlotUE
((
int16_t
*
)(
data
[
psbchLlr
]
+
1
),
data
[
psbchLlr
]
->
lineSz
,
this
);
break
;
}
case
PlotTypeUE
:
:
psbchIQ
:
{
if
(
!
data
[
psbchRxdataF_comp
])
{
newChart
=
new
QChart
();
this
->
plotType
=
PlotTypeUE
::
empty
;
this
->
comboBox
->
setCurrentIndex
(
static_cast
<
int
>
(
PlotTypeUE
::
empty
));
break
;
}
newChart
=
new
IQPlotUE
((
complex16
*
)(
data
[
psbchRxdataF_comp
]
+
1
),
data
[
psbchRxdataF_comp
]
->
lineSz
,
this
);
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
LLRPlotUE
((
int16_t
*
)(
data
[
pdcchLlr
]
+
1
),
data
[
pdcchLlr
]
->
lineSz
,
this
);
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
IQPlotUE
((
complex16
*
)(
data
[
pdcchRxdataF_comp
]
+
1
),
data
[
pdcchRxdataF_comp
]
->
lineSz
,
this
);
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
LLRPlotUE
((
int16_t
*
)(
data
[
pdschLlr
]
+
1
),
data
[
pdschLlr
]
->
lineSz
,
this
);
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
IQPlotUE
((
complex16
*
)(
data
[
pdschRxdataF_comp
]
+
1
),
data
[
pdschRxdataF_comp
]
->
lineSz
,
this
);
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
;
}
case
PlotTypeUE
:
:
psbchRSRP
:
{
newChart
=
new
KPIPlot
(
this
,
Limits_KPI_ue
[
2
]);
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
;
bool
is_sl
=
ue
->
sl_mode
;
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
;
if
(
is_sl
)
combo3
.
setCurrentIndex
(
static_cast
<
int
>
(
PlotTypeUE
::
psbchLLR
));
else
combo3
.
setCurrentIndex
(
static_cast
<
int
>
(
PlotTypeUE
::
psbchLLR
));
PainterWidgetUE
pwidgetueCombo3
(
&
config
,
&
combo3
,
ue
);
mainLayout
.
addWidget
(
&
combo3
,
2
,
0
);
mainLayout
.
addWidget
(
&
pwidgetueCombo3
,
3
,
0
);
KPIListSelectUE
combo4
;
if
(
is_sl
)
combo4
.
setCurrentIndex
(
static_cast
<
int
>
(
PlotTypeUE
::
psbchIQ
));
else
combo4
.
setCurrentIndex
(
static_cast
<
int
>
(
PlotTypeUE
::
psbchIQ
));
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
*
)
calloc
(
1
,
sizeof
(
scopeData_t
));
scope
->
gNB
=
p
->
gNB
;
scope
->
argc
=
p
->
argc
;
scope
->
argv
=
p
->
argv
;
scope
->
ru
=
p
->
ru
;
scope
->
scopeUpdater
=
scopeUpdaterGnb
;
scope
->
copyData
=
copyData
;
copyDataMutexInit
(
scope
);
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
*
)
calloc
(
1
,
sizeof
(
scopeData_t
));
scope
->
copyData
=
copyData
;
copyDataMutexInit
(
scope
);
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
deleted
100644 → 0
View file @
ae2a79e5
/*
* 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
,
psbchLLR
,
psbchIQ
,
psbchRSRP
,
};
/// 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
;
};
class
ValueProviderUE
:
public
ValueProvider
{
public:
/// This pure virtual function is meant to provide the graph values to be plotted
virtual
scopeGraphData_t
*
getPlotValue
()
{
return
nullptr
;
}
};
/// Class for emitting a queued signal to update plots
class
PlotUpdater
:
public
QObject
{
Q_OBJECT
signals:
void
updatePlot
(
int
numElt
);
};
/// 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
;
/// 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
;
};
class
CIRPlotUE
:
public
CIRPlot
{
Q_OBJECT
public:
CIRPlotUE
(
complex16
*
data
,
int
len
,
ValueProviderUE
*
valueProvider
)
:
CIRPlot
(
data
,
len
),
valueProvider
(
valueProvider
)
{
}
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:
ValueProviderUE
*
valueProvider
;
};
/// 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
/// \param interval update interval in ms (0 means no timer-triggered updates)
/// \param plotUpdater pointer to a PlotUpdater for update notifications
LLRPlot
(
int16_t
*
data
,
int
len
,
int
interval
=
1000
,
PlotUpdater
*
plotUpdater
=
nullptr
);
/// Destructor
~
LLRPlot
();
public
slots
:
/// This function updates the plotted LLR
void
updatePlot
(
int
len
);
protected:
/// This function is triggered when the own timer expires. It calls updatePlot() to update the plotted LLR
/// \param event Pointer to the timer event
virtual
void
timerEvent
(
QTimerEvent
*
event
)
override
;
/// Pointer to the LLR data
int16_t
*
data
;
/// Length of the LLR data
int
len
;
/// Pointer to a PlotUpdater for update notifications
PlotUpdater
*
plotUpdater
;
/// 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
;
};
class
LLRPlotUE
:
public
LLRPlot
{
Q_OBJECT
public:
LLRPlotUE
(
int16_t
*
data
,
int
len
,
ValueProviderUE
*
valueProvider
)
:
LLRPlot
(
data
,
len
),
valueProvider
(
valueProvider
)
{
}
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:
ValueProviderUE
*
valueProvider
;
};
/// 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
/// \param interval update interval in ms (0 means no timer-triggered updates)
/// \param plotUpdater pointer to a PlotUpdater for update notifications
IQPlot
(
complex16
*
data
,
int
len
,
int
interval
=
1000
,
PlotUpdater
*
plotUpdater
=
nullptr
);
/// Destructor
~
IQPlot
();
public
slots
:
/// This function updates the plotted I/Q constellation diagram
void
updatePlot
(
int
len
);
protected:
/// This function is triggered when the own timer expires. It calls updatePlot() to update the plotted I/Q constellation diagram
/// \param event Pointer to the timer event
virtual
void
timerEvent
(
QTimerEvent
*
event
)
override
;
/// Pointer to the I/Q data
complex16
*
data
;
/// Length of the I/Q data
int
len
;
/// Pointer to a PlotUpdater for update notifications
PlotUpdater
*
plotUpdater
;
/// 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
;
};
class
IQPlotUE
:
public
IQPlot
{
Q_OBJECT
public:
IQPlotUE
(
complex16
*
data
,
int
len
,
ValueProviderUE
*
valueProvider
)
:
IQPlot
(
data
,
len
),
valueProvider
(
valueProvider
)
{
}
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 an instance of a class that implements the ValueProvider interface
ValueProviderUE
*
valueProvider
;
};
/// 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
ValueProviderUE
{
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
;
virtual
scopeGraphData_t
*
getPlotValue
()
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/readme.md
View file @
394431a5
...
...
@@ -31,66 +31,3 @@ or
```
phy_scope_gNB(0, phy_vars_gnb, phy_vars_ru, UE_id)
```
# Qt-based Scope
## Building Instructions
For the new qt-based scope 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! Note that Qt6 does NOT work.
3.
To build the new scope, add 'nrqtscope' after the '--build-lib' 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 display 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.
## Troubleshoot
Similar as with the Xforms-based scope, you should allow root to open X
windows if using Xorg:
```
xhost +si:localuser:root
```
If you run on Wayland (e.g., Gnome on Ubuntu 22), you might get this error:
```
Warning: Ignoring XDG_SESSION_TYPE=wayland on Gnome. Use QT_QPA_PLATFORM=wayland to run on Wayland anyway.
Authorization required, but no authorization protocol specified
qt.qpa.xcb: could not connect to display :0
qt.qpa.plugin: Could not load the Qt platform plugin "xcb" in "" even though it was found.
This application failed to start because no Qt platform plugin could be initialized. Reinstalling the application may fix this problem.
```
We have to force the use of Wayland. Before that, make sure that the Qt-Wayland
plugin is installed. Also, there seems to be a problem with the qt5-gtk
platform theme, so remove it:
```
sudo apt-get install qtwayland5
sudo apt-get remove qt5-gtk-platformtheme
```
Now, force the Wayland plugin; also make sure to pass
`-E`
to sudo:
```
sudo -E QT_QPA_PLATFORM=wayland ./nr-softmodem --sa -O <conf> --dqt
```
Furthermore, if you get the following warning, _followed by a segfault_ (i.e.,
if it runs, you can ignore this)
```
QStandardPaths: wrong ownership on runtime directory /run/user/1000, 1000 instead of 0
```
You can set the XDG runtime directory like one of the following options (try in
order, keep
`QT_QPA_PLATFORM`
if necessary)
```
sudo -E XDG_RUNTIME_DIR=/run/user/0 ./nr-softmodem ...
sudo -E XDG_RUNTIME_DIR=/tmp/runtime-root ./nr-softmodem ...
sudo -E XDG_RUNTIME_DIR= ./nr-softmodem ...
```
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