From 394431a594fbba8a094123136ac28ffa9878ea75 Mon Sep 17 00:00:00 2001 From: Thomas Schlichter Date: Fri, 12 Jul 2024 14:44:22 +0200 Subject: [PATCH] remove nrqtscope --- CMakeLists.txt | 15 - cmake_targets/build_oai | 2 +- cmake_targets/tools/build_helper | 1 - doc/BUILD.md | 6 +- doc/Doxyfile | 1 - doc/README.md | 2 +- docker/Dockerfile.build.ubuntu22 | 2 +- executables/nr-softmodem.c | 9 - executables/nr-uesoftmodem.c | 4 - executables/softmodem-common.c | 6 +- executables/softmodem-common.h | 4 - openair1/PHY/TOOLS/nr_phy_qt_scope.cpp | 1379 ------------------------ openair1/PHY/TOOLS/nr_phy_qt_scope.h | 563 ---------- openair1/PHY/TOOLS/readme.md | 63 -- 14 files changed, 6 insertions(+), 2051 deletions(-) delete mode 100644 openair1/PHY/TOOLS/nr_phy_qt_scope.cpp delete mode 100644 openair1/PHY/TOOLS/nr_phy_qt_scope.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 01f3fb17a0..94c49f8922 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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 diff --git a/cmake_targets/build_oai b/cmake_targets/build_oai index 5594b856af..3e688923fb 100755 --- a/cmake_targets/build_oai +++ b/cmake_targets/build_oai @@ -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() { diff --git a/cmake_targets/tools/build_helper b/cmake_targets/tools/build_helper index 9d28197450..9dffcd37a0 100755 --- a/cmake_targets/tools/build_helper +++ b/cmake_targets/tools/build_helper @@ -541,7 +541,6 @@ check_install_additional_tools (){ doxygen \ libgnutls28-dev \ libpthread-stubs0-dev \ - libqt5charts5-dev \ tshark \ uml-utilities \ iperf3 \ diff --git a/doc/BUILD.md b/doc/BUILD.md index fb41985bf9..e99f88fca4 100644 --- a/doc/BUILD.md +++ b/doc/BUILD.md @@ -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 diff --git a/doc/Doxyfile b/doc/Doxyfile index b08d01be0f..72c03efe96 100644 --- a/doc/Doxyfile +++ b/doc/Doxyfile @@ -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 \ diff --git a/doc/README.md b/doc/README.md index c4957566b7..3a71d9f51f 100644 --- a/doc/README.md +++ b/doc/README.md @@ -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) diff --git a/docker/Dockerfile.build.ubuntu22 b/docker/Dockerfile.build.ubuntu22 index 6d704cc341..6a4b44c794 100644 --- a/docker/Dockerfile.build.ubuntu22 +++ b/docker/Dockerfile.build.ubuntu22 @@ -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 \ diff --git a/executables/nr-softmodem.c b/executables/nr-softmodem.c index 730bfd1b76..ac6a2e88bd 100644 --- a/executables/nr-softmodem.c +++ b/executables/nr-softmodem.c @@ -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(); diff --git a/executables/nr-uesoftmodem.c b/executables/nr-uesoftmodem.c index 262f0b1631..b85b698021 100644 --- a/executables/nr-uesoftmodem.c +++ b/executables/nr-uesoftmodem.c @@ -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]); } diff --git a/executables/softmodem-common.c b/executables/softmodem-common.c index c4b25d7f20..6f5cabdede 100644 --- a/executables/softmodem-common.c +++ b/executables/softmodem-common.c @@ -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); } diff --git a/executables/softmodem-common.h b/executables/softmodem-common.h index 40a94e1ec1..b7baaea7da 100644 --- a/executables/softmodem-common.h +++ b/executables/softmodem-common.h @@ -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) diff --git a/openair1/PHY/TOOLS/nr_phy_qt_scope.cpp b/openair1/PHY/TOOLS/nr_phy_qt_scope.cpp deleted file mode 100644 index 7ea53b4a52..0000000000 --- a/openair1/PHY/TOOLS/nr_phy_qt_scope.cpp +++ /dev/null @@ -1,1379 +0,0 @@ -/* - * 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 -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include -#include -#include - -#include "nr_phy_qt_scope.h" - -extern "C" { -#include "PHY/CODING/nrPolar_tools/nr_polar_defs.h" -#include -} - -#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(PlotTypeGnb::empty)); - this->addItem("RX Signal-Time", static_cast(PlotTypeGnb::waterFall)); - this->addItem("Channel Response", static_cast(PlotTypeGnb::CIR)); - - this->addItem("LLR PUSCH", static_cast(PlotTypeGnb::puschLLR)); - this->addItem("I/Q PUSCH", static_cast(PlotTypeGnb::puschIQ)); - this->addItem("UL SNR", static_cast(PlotTypeGnb::puschSNR)); - this->addItem("UL BLER", static_cast(PlotTypeGnb::puschBLER)); - this->addItem("UL MCS", static_cast(PlotTypeGnb::puschMCS)); - this->addItem("UL Retrans.", static_cast(PlotTypeGnb::puschRETX)); - this->addItem("UL Throughput", static_cast(PlotTypeGnb::puschThroughput)); - - this->addItem("DL SNR (CQI)", static_cast(PlotTypeGnb::pdschSNR)); - this->addItem("DL BLER", static_cast(PlotTypeGnb::pdschBLER)); - this->addItem("DL MCS", static_cast(PlotTypeGnb::pdschMCS)); - this->addItem("DL Retrans.", static_cast(PlotTypeGnb::pdschRETX)); - this->addItem("DL Throughput", static_cast(PlotTypeGnb::pdschThroughput)); - this->addItem("Nof Sched. RBs", static_cast(PlotTypeGnb::pdschRBs)); - - this->addItem("Configs", static_cast(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(PlotTypeUE::empty)); - this->addItem("RX Signal-Time", static_cast(PlotTypeUE::waterFall)); - this->addItem("Channel Response", static_cast(PlotTypeUE::CIR)); - - this->addItem("LLR PBCH", static_cast(PlotTypeUE::pbchLLR)); - this->addItem("I/Q PBCH", static_cast(PlotTypeUE::pbchIQ)); - this->addItem("LLR PDCCH", static_cast(PlotTypeUE::pdcchLLR)); - this->addItem("I/Q PDCCH", static_cast(PlotTypeUE::pdcchIQ)); - this->addItem("LLR PDSCH", static_cast(PlotTypeUE::pdschLLR)); - this->addItem("I/Q PDSCH", static_cast(PlotTypeUE::pdschIQ)); - - this->addItem("DL SNR", static_cast(PlotTypeUE::pdschSNR)); - this->addItem("DL BLER", static_cast(PlotTypeUE::pdschBLER)); - this->addItem("DL MCS", static_cast(PlotTypeUE::pdschMCS)); - this->addItem("Throughput", static_cast(PlotTypeUE::pdschThroughput)); - this->addItem("Nof Sched. RBs", static_cast(PlotTypeUE::pdschRBs)); - this->addItem("Freq. Offset", static_cast(PlotTypeUE::frequencyOffset)); - this->addItem("Time Adv.", static_cast(PlotTypeUE::timingAdvance)); - - this->addItem("Configs", static_cast(PlotTypeUE::config)); - this->addItem("LLR PSBCH", static_cast(PlotTypeUE::psbchLLR)); - this->addItem("I/Q PSBCH", static_cast(PlotTypeUE::psbchIQ)); - this->addItem("PSBCH RSRP dB/RE", static_cast(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 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 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 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 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 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 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(&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(type); - - if (plotType == this->plotType) - return; - - if (plotType == PlotTypeGnb::config) { - config->show(); - this->comboBox->setCurrentIndex(static_cast(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(&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(type); - - if (plotType == this->plotType) - return; - - if (plotType == PlotTypeUE::config) { - config->show(); - this->comboBox->setCurrentIndex(static_cast(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(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(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(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(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(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(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(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(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(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(PlotTypeGnb::waterFall)); - PainterWidgetGnb pwidgetGnbCombo1(&config, &combo1, p); - - mainLayout.addWidget(&combo1, 0, 0); - mainLayout.addWidget(&pwidgetGnbCombo1, 1, 0); - - KPIListSelectGnb combo2; - combo2.setCurrentIndex(static_cast(PlotTypeGnb::CIR)); - PainterWidgetGnb pwidgetGnbCombo2(&config, &combo2, p); - - mainLayout.addWidget(&combo2, 0, 1); - mainLayout.addWidget(&pwidgetGnbCombo2, 1, 1); - - KPIListSelectGnb combo3; - combo3.setCurrentIndex(static_cast(PlotTypeGnb::puschLLR)); - PainterWidgetGnb pwidgetGnbCombo3(&config, &combo3, p); - - mainLayout.addWidget(&combo3, 2, 0); - mainLayout.addWidget(&pwidgetGnbCombo3, 3, 0); - - KPIListSelectGnb combo4; - combo4.setCurrentIndex(static_cast(PlotTypeGnb::puschIQ)); - PainterWidgetGnb pwidgetGnbCombo4(&config, &combo4, p); - - mainLayout.addWidget(&combo4, 2, 1); - mainLayout.addWidget(&pwidgetGnbCombo4, 3, 1); - - KPIListSelectGnb combo5; - combo5.setCurrentIndex(static_cast(PlotTypeGnb::empty)); - PainterWidgetGnb pwidgetGnbCombo5(&config, &combo5, p); - - mainLayout.addWidget(&combo5, 4, 0); - mainLayout.addWidget(&pwidgetGnbCombo5, 5, 0); - - KPIListSelectGnb combo6; - combo6.setCurrentIndex(static_cast(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(PlotTypeUE::waterFall)); - PainterWidgetUE pwidgetueCombo1(&config, &combo1, ue); - - mainLayout.addWidget(&combo1, 0, 0); - mainLayout.addWidget(&pwidgetueCombo1, 1, 0); - - KPIListSelectUE combo2; - combo2.setCurrentIndex(static_cast(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(PlotTypeUE::psbchLLR)); - else - combo3.setCurrentIndex(static_cast(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(PlotTypeUE::psbchIQ)); - else - combo4.setCurrentIndex(static_cast(PlotTypeUE::psbchIQ)); - PainterWidgetUE pwidgetueCombo4(&config, &combo4, ue); - - mainLayout.addWidget(&combo4, 2, 1); - mainLayout.addWidget(&pwidgetueCombo4, 3, 1); - - KPIListSelectUE combo5; - combo5.setCurrentIndex(static_cast(PlotTypeUE::pdschLLR)); - PainterWidgetUE pwidgetueCombo5(&config, &combo5, ue); - - mainLayout.addWidget(&combo5, 4, 0); - mainLayout.addWidget(&pwidgetueCombo5, 5, 0); - - KPIListSelectUE combo6; - combo6.setCurrentIndex(static_cast(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); -} diff --git a/openair1/PHY/TOOLS/nr_phy_qt_scope.h b/openair1/PHY/TOOLS/nr_phy_qt_scope.h deleted file mode 100644 index f00103aad7..0000000000 --- a/openair1/PHY/TOOLS/nr_phy_qt_scope.h +++ /dev/null @@ -1,563 +0,0 @@ -/* - * 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 - -extern "C" { -#include -#include -#include "common/ran_context.h" -#include -#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 -#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 diff --git a/openair1/PHY/TOOLS/readme.md b/openair1/PHY/TOOLS/readme.md index 0ad6347d58..9a0e2947f9 100644 --- a/openair1/PHY/TOOLS/readme.md +++ b/openair1/PHY/TOOLS/readme.md @@ -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 --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 ... -```