First implementation for KPI GUI with Qt5

Authors: Bo Zhao, Marwan Hammouda, Thomas Schlichter (Fraunhofer IIS)

- CMakeLists modification: add QtWidgets library, add new source files, MOC (Meta-Object Compiler) on the given source file
- build_oai modification
- Activate new GUI with Qt on the UE side
- 2 x 3 widget with I/Q sample for PDSCH
- Drop-down list implementation
This commit is contained in:
Bo Zhao
2023-02-23 17:46:27 +01:00
committed by Thomas Schlichter
parent 646cec27d0
commit 4d0762e43d
21 changed files with 1786 additions and 71 deletions
+14 -1
View File
@@ -1005,7 +1005,7 @@ add_dependencies(ldpc_cl nrLDPC_decoder_kernels_CL)
# Base CUDA setting
##############################################
add_boolean_option(BUILD_CUDA False "Build support for CUDA" OFF)
add_boolean_option(ENABLE_LDPC_CUDA OFF "Build support for CUDA" OFF)
if (ENABLE_LDPC_CUDA)
find_package(CUDA REQUIRED)
SET(CUDA_NVCC_FLAG "${CUDA_NVCC_FLAGS};-arch=sm_60;")
@@ -2040,6 +2040,19 @@ add_library(SIMU STATIC ${SIMUSRC} )
target_include_directories(SIMU PUBLIC ${OPENAIR1_DIR}/SIMULATION/TOOLS ${OPENAIR1_DIR}/SIMULATION/RF)
target_link_libraries(SIMU PRIVATE asn1_nr_rrc asn1_lte_rrc)
# Qt-based scope
add_boolean_option(ENABLE_NRQTSCOPE OFF "Build the Qt-Scope" OFF)
if (ENABLE_NRQTSCOPE)
find_package(Qt5 REQUIRED COMPONENTS Widgets Charts)
message ("Qt5 Widgets and Charts found for nrqtscope")
set(QTSCOPE_SOURCE_NR ${OPENAIR1_DIR}/PHY/TOOLS/nr_phy_qt_scope.cpp)
# Creates rules for calling the Meta-Object Compiler (moc) on the given source files
qt5_wrap_cpp(QTSCOPE_SOURCE_NR ${OPENAIR1_DIR}/PHY/TOOLS/nr_phy_qt_scope.h)
add_library(nrqtscope MODULE ${QTSCOPE_SOURCE_NR})
target_link_libraries(nrqtscope PRIVATE Qt5::Widgets Qt5::Charts)
target_link_libraries(nrqtscope PRIVATE asn1_nr_rrc asn1_lte_rrc)
endif()
add_library(SIMU_ETH
${OPENAIR1_DIR}/SIMULATION/ETH_TRANSPORT/netlink_init.c
${OPENAIR1_DIR}/SIMULATION/ETH_TRANSPORT/multicast_link.c
+2 -1
View File
@@ -53,9 +53,10 @@ BUILD_COVERITY_SCAN=0
DISABLE_HARDWARE_DEPENDENCY="False"
CMAKE_BUILD_TYPE="RelWithDebInfo"
CMAKE_CMD="$CMAKE"
MAKE_CMD=make
BUILD_ECLIPSE=0
NR="False"
OPTIONAL_LIBRARIES="telnetsrv enbscope uescope nrscope ldpc_cuda ldpc_t1 websrv"
OPTIONAL_LIBRARIES="telnetsrv enbscope uescope nrscope nrqtscope ldpc_cuda ldpc_t1 websrv"
RU=0
CMAKE_C_FLAGS=()
CMAKE_CXX_FLAGS=()
+1
View File
@@ -551,6 +551,7 @@ check_install_additional_tools (){
PACKAGE_LIST="\
doxygen \
libpthread-stubs0-dev \
libqt5charts5-dev \
tshark \
uml-utilities \
iperf3 \
+4 -2
View File
@@ -2218,11 +2218,13 @@ INPUT = \
@CMAKE_CURRENT_SOURCE_DIR@/../openair1/PHY/LTE_ESTIMATION/lte_dl_bf_channel_estimation.c \
@CMAKE_CURRENT_SOURCE_DIR@/../openair1/PHY/LTE_ESTIMATION/lte_sync_timefreq.c \
@CMAKE_CURRENT_SOURCE_DIR@/../openair1/PHY/LTE_ESTIMATION/filt16_32.h \
@CMAKE_CURRENT_SOURCE_DIR@/../openair1/PHY/TOOLS/nr_phy_scope.c \
@CMAKE_CURRENT_SOURCE_DIR@/../openair1/PHY/TOOLS/cdot_prod.c \
@CMAKE_CURRENT_SOURCE_DIR@/../openair1/PHY/TOOLS/nr_phy_scope.h \
@CMAKE_CURRENT_SOURCE_DIR@/../openair1/PHY/TOOLS/phy_scope_interface.h \
@CMAKE_CURRENT_SOURCE_DIR@/../openair1/PHY/TOOLS/phy_scope_interface.c \
@CMAKE_CURRENT_SOURCE_DIR@/../openair1/PHY/TOOLS/nr_phy_scope.h \
@CMAKE_CURRENT_SOURCE_DIR@/../openair1/PHY/TOOLS/nr_phy_scope.c \
@CMAKE_CURRENT_SOURCE_DIR@/../openair1/PHY/TOOLS/nr_phy_qt_scope.h \
@CMAKE_CURRENT_SOURCE_DIR@/../openair1/PHY/TOOLS/nr_phy_qt_scope.cpp \
@CMAKE_CURRENT_SOURCE_DIR@/../openair1/PHY/TOOLS/file_output.c \
@CMAKE_CURRENT_SOURCE_DIR@/../openair1/PHY/TOOLS/dfts_load.c \
@CMAKE_CURRENT_SOURCE_DIR@/../openair1/PHY/TOOLS/alaw_lut.h \
+9
View File
@@ -711,6 +711,15 @@ int main( int argc, char **argv ) {
load_softscope("nr",&p);
}
if(IS_SOFTMODEM_DOSCOPE_QT) {
scopeParms_t p;
p.argc = &argc;
p.argv = argv;
p.gNB = RC.gNB[0];
p.ru = RC.ru[0];
load_softscope("nrqt", &p);
}
if (NFAPI_MODE != NFAPI_MODE_PNF && NFAPI_MODE != NFAPI_MODE_VNF) {
printf("Not NFAPI mode - call init_eNB_afterRU()\n");
init_eNB_afterRU();
+4
View File
@@ -522,6 +522,10 @@ int main( int argc, char **argv ) {
memset (&UE_PF_PO[0][0], 0, sizeof(UE_PF_PO_t)*NUMBER_OF_UE_MAX*MAX_NUM_CCs);
set_latency_target();
if(IS_SOFTMODEM_DOSCOPE_QT) {
load_softscope("nrqt",PHY_vars_UE_g[0][0]);
}
if(IS_SOFTMODEM_DOSCOPE) {
load_softscope("nr",PHY_vars_UE_g[0][0]);
}
+5 -1
View File
@@ -97,7 +97,7 @@ void get_common_options(uint32_t execmask) {
uint32_t start_telnetsrv = 0, start_telnetclt = 0;
uint32_t start_websrv = 0;
uint32_t noS1 = 0, nokrnmod = 1, nonbiot = 0;
uint32_t rfsim = 0, do_forms = 0;
uint32_t rfsim = 0, do_forms = 0, do_forms_qt = 0;
int nfapi_index = 0;
char *logmem_filename = NULL;
check_execmask(execmask);
@@ -161,6 +161,10 @@ void get_common_options(uint32_t execmask) {
set_softmodem_optmask(SOFTMODEM_DOSCOPE_BIT);
}
if (do_forms_qt) {
set_softmodem_optmask(SOFTMODEM_DOSCOPE_QT_BIT);
}
if (start_websrv) {
load_module_shlib("websrv", NULL, 0, NULL);
}
+11 -6
View File
@@ -67,6 +67,7 @@ extern "C"
#define CONFIG_HLP_ULF "Set the uplink frequency offset for all component carriers\n"
#define CONFIG_HLP_CHOFF "Channel id offset\n"
#define CONFIG_HLP_SOFTS "Enable soft scope and L1 and L2 stats (Xforms)\n"
#define CONFIG_HLP_SOFTS_QT "Enable soft scope and L1 and L2 stats (QT)\n"
#define CONFIG_HLP_ITTIL "Generate ITTI analyzser logs (similar to wireshark logs but with more details)\n"
#define CONFIG_HLP_DLMCS "Set the maximum downlink MCS\n"
#define CONFIG_HLP_STMON "Enable processing timing measurement of lte softmodem on per subframe basis \n"
@@ -151,17 +152,18 @@ extern int usrp_tx_thread;
{"C" , CONFIG_HLP_DLF, 0, u64ptr:&(downlink_frequency[0][0]), defuintval:0, TYPE_UINT64, 0}, \
{"CO" , CONFIG_HLP_ULF, 0, iptr:&(uplink_frequency_offset[0][0]), defintval:0, TYPE_INT, 0}, \
{"a" , CONFIG_HLP_CHOFF, 0, iptr:&CHAIN_OFFSET, defintval:0, TYPE_INT, 0}, \
{"d" , CONFIG_HLP_SOFTS, PARAMFLAG_BOOL, uptr:(uint32_t *)&do_forms, defintval:0, TYPE_INT8, 0}, \
{"d" , CONFIG_HLP_SOFTS, PARAMFLAG_BOOL, uptr:&do_forms, defintval:0, TYPE_UINT, 0}, \
{"dqt" , CONFIG_HLP_SOFTS_QT, PARAMFLAG_BOOL, uptr:&do_forms_qt, defintval:0, TYPE_UINT, 0}, \
{"q" , CONFIG_HLP_STMON, PARAMFLAG_BOOL, iptr:&opp_enabled, defintval:0, TYPE_INT, 0}, \
{"numerology" , CONFIG_HLP_NUMEROLOGY, PARAMFLAG_BOOL, iptr:&NUMEROLOGY, defintval:1, TYPE_INT, 0}, \
{"band" , CONFIG_HLP_BAND, PARAMFLAG_BOOL, iptr:&BAND, defintval:78, TYPE_INT, 0}, \
{"emulate-rf" , CONFIG_HLP_EMULATE_RF, PARAMFLAG_BOOL, iptr:&EMULATE_RF, defintval:0, TYPE_INT, 0}, \
{"parallel-config", CONFIG_HLP_PARALLEL_CMD, 0, strptr:&parallel_config, defstrval:NULL, TYPE_STRING, 0}, \
{"worker-config", CONFIG_HLP_WORKER_CMD, 0, strptr:&worker_config, defstrval:NULL, TYPE_STRING, 0}, \
{"noS1", CONFIG_HLP_NOS1, PARAMFLAG_BOOL, uptr:&noS1, defintval:0, TYPE_INT, 0}, \
{"rfsim", CONFIG_HLP_RFSIM, PARAMFLAG_BOOL, uptr:&rfsim, defintval:0, TYPE_INT, 0}, \
{"nokrnmod", CONFIG_HLP_NOKRNMOD, PARAMFLAG_BOOL, uptr:&nokrnmod, defintval:0, TYPE_INT, 0}, \
{"nbiot-disable", CONFIG_HLP_DISABLNBIOT, PARAMFLAG_BOOL, uptr:&nonbiot, defuintval:0, TYPE_INT, 0}, \
{"noS1", CONFIG_HLP_NOS1, PARAMFLAG_BOOL, uptr:&noS1, defintval:0, TYPE_UINT, 0}, \
{"rfsim", CONFIG_HLP_RFSIM, PARAMFLAG_BOOL, uptr:&rfsim, defintval:0, TYPE_UINT, 0}, \
{"nokrnmod", CONFIG_HLP_NOKRNMOD, PARAMFLAG_BOOL, uptr:&nokrnmod, defintval:0, TYPE_UINT, 0}, \
{"nbiot-disable", CONFIG_HLP_DISABLNBIOT, PARAMFLAG_BOOL, uptr:&nonbiot, defuintval:0, TYPE_UINT, 0}, \
{"chest-freq", CONFIG_HLP_CHESTFREQ, 0, iptr:&CHEST_FREQ, defintval:0, TYPE_INT, 0}, \
{"chest-time", CONFIG_HLP_CHESTTIME, 0, iptr:&CHEST_TIME, defintval:0, TYPE_INT, 0}, \
{"nsa", CONFIG_HLP_NSA, PARAMFLAG_BOOL, iptr:&NSA, defintval:0, TYPE_INT, 0}, \
@@ -208,6 +210,7 @@ extern int usrp_tx_thread;
{ .s5 = { NULL } }, \
{ .s5 = { NULL } }, \
{ .s5 = { NULL } }, \
{ .s5 = { NULL } }, \
{ .s3a = { config_checkstr_assign_integer, \
{"MONOLITHIC", "PNF", "VNF","UE_STUB_PNF","UE_STUB_OFFNET","STANDALONE_PNF"}, \
{NFAPI_MONOLITHIC, NFAPI_MODE_PNF, NFAPI_MODE_VNF,NFAPI_UE_STUB_PNF,NFAPI_UE_STUB_OFFNET,NFAPI_MODE_STANDALONE_PNF}, \
@@ -216,7 +219,6 @@ extern int usrp_tx_thread;
{ .s5 = { NULL } }, \
{ .s5 = { NULL } }, \
{ .s5 = { NULL } }, \
{ .s5 = { NULL } }, \
}
#define CONFIG_HLP_NSA "Enable NSA mode \n"
@@ -264,6 +266,8 @@ extern int usrp_tx_thread;
#define SOFTMODEM_4GUE_BIT (1<<22)
#define SOFTMODEM_5GUE_BIT (1<<23)
#define SOFTMODEM_NOSTATS_BIT (1<<24)
#define SOFTMODEM_DOSCOPE_QT_BIT (1<<25)
#define SOFTMODEM_FUNC_BITS (SOFTMODEM_ENB_BIT | SOFTMODEM_GNB_BIT | SOFTMODEM_5GUE_BIT | SOFTMODEM_4GUE_BIT)
#define MAPPING_SOFTMODEM_FUNCTIONS {{"enb",SOFTMODEM_ENB_BIT},{"gnb",SOFTMODEM_GNB_BIT},{"4Gue",SOFTMODEM_4GUE_BIT},{"5Gue",SOFTMODEM_5GUE_BIT}}
@@ -275,6 +279,7 @@ extern int usrp_tx_thread;
#define IS_SOFTMODEM_SIML1 ( get_softmodem_optmask() & SOFTMODEM_SIML1_BIT)
#define IS_SOFTMODEM_DLSIM ( get_softmodem_optmask() & SOFTMODEM_DLSIM_BIT)
#define IS_SOFTMODEM_DOSCOPE ( get_softmodem_optmask() & SOFTMODEM_DOSCOPE_BIT)
#define IS_SOFTMODEM_DOSCOPE_QT ( get_softmodem_optmask() & SOFTMODEM_DOSCOPE_QT_BIT)
#define IS_SOFTMODEM_IQPLAYER ( get_softmodem_optmask() & SOFTMODEM_RECPLAY_BIT)
#define IS_SOFTMODEM_TELNETCLT_BIT ( get_softmodem_optmask() & SOFTMODEM_TELNETCLT_BIT)
#define IS_SOFTMODEM_ENB_BIT ( get_softmodem_optmask() & SOFTMODEM_ENB_BIT)
@@ -300,6 +300,7 @@ void nr_sort_asc_int16_1D_array_ind(int32_t *matrix,
void nr_free_double_2D_array(double **input, uint16_t xlen);
#ifndef __cplusplus
void updateLLR(uint8_t listSize,
uint16_t row,
uint16_t col,
@@ -327,7 +328,7 @@ void updatePathMetric2(double *pathMetric,
int ylen,
int zlen,
double llr[xlen][ylen][zlen]);
#endif
//Also nr_polar_rate_matcher
static inline void nr_polar_interleaver(uint8_t *input,
uint8_t *output,
@@ -44,6 +44,7 @@
#include "executables/nr-uesoftmodem.h"
#include "PHY/CODING/nrLDPC_extern.h"
#include "common/utils/nr/nr_common.h"
#include "openair1/PHY/TOOLS/phy_scope_interface.h"
//#define ENABLE_PHY_PAYLOAD_DEBUG 1
@@ -53,10 +54,16 @@
static uint64_t nb_total_decod =0;
static uint64_t nb_error_decod =0;
static extended_kpi_ue kpiStructure = {0};
notifiedFIFO_t freeBlocks_dl;
notifiedFIFO_elt_t *msgToPush_dl;
int nbDlProcessing =0;
extended_kpi_ue* getKPIUE(void) {
return &kpiStructure;
}
void nr_ue_dlsch_init(NR_UE_DLSCH_t *dlsch_list, int num_dlsch, uint8_t max_ldpc_iterations) {
for (int i=0; i < num_dlsch; i++) {
NR_UE_DLSCH_t *dlsch = dlsch_list + i;
@@ -99,6 +106,11 @@ bool nr_ue_postDecode(PHY_VARS_NR_UE *phy_vars_ue, notifiedFIFO_elt_t *req, bool
// if all segments are done
if (last) {
kpiStructure.nb_total++;
kpiStructure.blockSize = dlsch->dlsch_config.TBS;
kpiStructure.dl_mcs = dlsch->dlsch_config.mcs;
kpiStructure.nofRBs = dlsch->dlsch_config.number_rbs;
if (decodeSuccess) {
//LOG_D(PHY,"[UE %d] DLSCH: Setting ACK for nr_slot_rx %d TBS %d mcs %d nb_rb %d harq_process->round %d\n",
// phy_vars_ue->Mod_id,nr_slot_rx,harq_process->TBS,harq_process->mcs,harq_process->nb_rb, harq_process->round);
@@ -114,6 +126,7 @@ bool nr_ue_postDecode(PHY_VARS_NR_UE *phy_vars_ue, notifiedFIFO_elt_t *req, bool
dlsch->last_iteration_cnt = rdata->decodeIterations;
LOG_D(PHY, "DLSCH received ok \n");
} else {
kpiStructure.nb_nack++;
//LOG_D(PHY,"[UE %d] DLSCH: Setting NAK for SFN/SF %d/%d (pid %d, status %d, round %d, TBS %d, mcs %d) Kr %d r %d harq_process->round %d\n",
// phy_vars_ue->Mod_id, frame, nr_slot_rx, harq_pid,harq_process->status, harq_process->round,harq_process->TBS,harq_process->mcs,Kr,r,harq_process->round);
harq_process->ack = 0;
File diff suppressed because it is too large Load Diff
+468
View File
@@ -0,0 +1,468 @@
/*
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The OpenAirInterface Software Alliance licenses this file to You under
* the OAI Public License, Version 1.1 (the "License"); you may not use this file
* except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.openairinterface.org/?page_id=698
*
* Authors and copyright: Bo Zhao, Marwan Hammouda, Thomas Schlichter (Fraunhofer IIS)
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*-------------------------------------------------------------------------------
* For more information about the OpenAirInterface (OAI) Software Alliance:
* contact@openairinterface.org
*/
#ifndef QT_SCOPE_MAINWINDOW_H
#define QT_SCOPE_MAINWINDOW_H
#include <QtCharts>
extern "C" {
#include <simple_executable.h>
#include <common/utils/system.h>
#include "common/ran_context.h"
#include <openair1/PHY/defs_gNB.h>
#include "PHY/defs_gNB.h"
#include "PHY/defs_nr_UE.h"
#include "PHY/defs_RU.h"
#include "executables/softmodem-common.h"
#include "phy_scope_interface.h"
#include <openair2/LAYER2/NR_MAC_gNB/mac_proto.h>
#include "PHY/CODING/nrPolar_tools/nr_polar_defs.h"
extern RAN_CONTEXT_t RC;
}
/// This is an enum class for the different gNB plot types
enum class PlotTypeGnb {
empty,
waterFall,
CIR,
puschLLR,
puschIQ,
puschSNR,
puschBLER,
puschMCS,
puschRETX,
puschThroughput,
pdschSNR,
pdschBLER,
pdschMCS,
pdschRETX,
pdschThroughput,
pdschRBs,
config
};
/// This is an enum class fpr the different UE plot types
enum class PlotTypeUE {
empty,
waterFall,
CIR,
pbchLLR,
pbchIQ,
pdcchLLR,
pdcchIQ,
pdschLLR,
pdschIQ,
pdschSNR,
pdschBLER,
pdschMCS,
pdschThroughput,
pdschRBs,
frequencyOffset,
timingAdvance,
config
};
/// This abstract class defines an interface how the KPIPlot class can access values for the different KPI plot types
class ValueProvider {
public:
/// This pure virtual function is meant to provide the KPI value to be plotted
virtual float getValue() = 0;
};
/// An editable GUI field for a dialog box to set certain KPI configurations
class ConfigBoxFloat : public QLineEdit {
Q_OBJECT
public:
/// Constructor
/// \param valuePtr Pointer to a float value, which can be edited through this ConfigBoxFloat
/// \param parent Optional pointer to parent QWidget
ConfigBoxFloat(float *valuePtr, QWidget *parent = nullptr);
public slots:
/// This function converts the value in the editable GUI field to float and writes it back to valuePtr
void readText();
private:
/// Member variable keeping the pointer to the float value
float *valuePtr;
};
/// Dialog box for configuring gNB KPI Limits
class KPIConfigGnb : public QWidget {
Q_OBJECT
public:
/// Constructor
/// \param parent Optional pointer to parent QWidget
explicit KPIConfigGnb(QWidget *parent = nullptr);
};
/// Dialog box for configuring UE KPI Limits
class KPIConfigUE : public QWidget {
Q_OBJECT
public:
/// Constructor
/// \param parent Optional pointer to parent QWidget
explicit KPIConfigUE(QWidget *parent = nullptr);
};
/// drop-down list gNB
class KPIListSelectGnb : public QComboBox {
Q_OBJECT
public:
/// Constructor
/// \param parent Optional pointer to parent QWidget
explicit KPIListSelectGnb(QWidget *parent = nullptr);
};
/// drop-down list UE
class KPIListSelectUE : public QComboBox {
Q_OBJECT
public:
/// Constructor
/// \param parent Optional pointer to parent QWidget
explicit KPIListSelectUE(QWidget *parent = nullptr);
};
/// Waterfall plot of RX signal power
class WaterFall : public QWidget {
Q_OBJECT
public:
/// Constructor
/// \param values Pointer to the digital I/Q samples
/// \param frame_parms Pointer to the NR_DL_FRAME_PARMS
/// \param parent Optional pointer to parent QWidget
WaterFall(complex16 *values, NR_DL_FRAME_PARMS *frame_parms, QWidget *parent = nullptr);
protected:
/// This function is triggered when the own timer expires. It reads data from values and updates the image
/// \param event Pointer to the timer event
virtual void timerEvent(QTimerEvent *event) override;
/// This function is used to draw the image on the GUI
/// \param event Pointer to the paint event
virtual void paintEvent(QPaintEvent *event) override;
/// This function is called to change the WaterFall size
/// \param event Pointer to the resize event
virtual void resizeEvent(QResizeEvent *event) override;
private:
/// Pointer to the digital I/Q samples
complex16 *values;
/// Pointer to the NR_DL_FRAME_PARMS
NR_DL_FRAME_PARMS *frame_parms;
/// Pointer to the image
QImage *image;
/// Counter of the drawn lines
int iteration;
/// pointer to an array storing per-row average power values
double *waterFallAvg;
};
/// Chart class for plotting the Channel Impulse Response
class CIRPlot : public QChart {
Q_OBJECT
public:
/// Constructor
/// \param data Pointer to the CIR data
/// \param len Length of the CIR data
CIRPlot(complex16 *data, int len);
protected:
/// This function is triggered when the own timer expires. It updates the plotted CIR
/// \param event Pointer to the timer event
virtual void timerEvent(QTimerEvent *event) override;
private:
/// Pointer to the CIR data
complex16 *data;
/// Length of the CIR data
int len;
/// Line series used to plot the CIR in the chart
QLineSeries *series;
/// Horizontal axis of the chart
QValueAxis *axisX;
/// Vertical axis of the chart
QValueAxis *axisY;
};
/// Chart class for plotting LLRs
class LLRPlot : public QChart {
Q_OBJECT
public:
/// Constructor
/// \param data Pointer to the LLR data
/// \param len Length of the LLR data
LLRPlot(int16_t *data, int len);
protected:
/// This function is triggered when the own timer expires. It updates the plotted LLR
/// \param event Pointer to the timer event
virtual void timerEvent(QTimerEvent *event) override;
private:
/// Pointer to the LLR data
int16_t *data;
/// Length of the LLR data
int len;
/// Scatter series used to plot the LLR in the chart
QScatterSeries *series;
/// Horizontal axis of the chart
QValueAxis *axisX;
/// Vertical axis of the chart
QValueAxis *axisY;
};
/// Chart class for plotting the I/Q constellation diagram
class IQPlot : public QChart {
Q_OBJECT
public:
/// Constructor
/// \param data Pointer to the complex I/Q data
/// \param len Length of the I/Q data
IQPlot(complex16 *data, int len);
protected:
/// This function is triggered when the own timer expires. It updates the plotted I/Q constellation diagram
/// \param event Pointer to the timer event
virtual void timerEvent(QTimerEvent *event) override;
private:
/// Pointer to the I/Q data
complex16 *data;
/// Length of the I/Q data
int len;
/// Scatter series used to plot the I/Q constellation diagram
QScatterSeries *series;
/// Horizontal axis of the chart
QValueAxis *axisX;
/// Vertical axis of the chart
QValueAxis *axisY;
};
/// Generic class for plotting KPI values with min., max. and average bars
class KPIPlot : public QChart {
Q_OBJECT
public:
/// Constructor
/// \param valueProvider Pointer to an instance of a class that implements the ValueProvider interface
/// \param limits Optional parameter pointing to an array of two floating point values indicating lower and upper bounds
KPIPlot(ValueProvider *valueProvider, float *limits = nullptr);
protected:
/// This function is triggered when the own timer expires. It updates the plotted KPI chart
/// \param event Pointer to the timer event
virtual void timerEvent(QTimerEvent *event) override;
private:
/// Pointer to an instance of a class that implements the ValueProvider interface
ValueProvider *valueProvider;
/// Pointer to an array of two floating point values indicating lower and upper bounds
float *limits;
/// smallest observed KPI value
float minValue;
/// biggest observed KPI value
float maxValue;
/// Accumulated KPI valus, used to compute the average
float sumValue;
/// Index (horizontal position) of the last plotted KPI value
int plotIdx;
/// Line series used to plot the KPI values
QLineSeries *series;
/// Line series used to plot an indication of the smallest observed KPI value
QLineSeries *seriesMin;
/// Line series used to plot an indication of the biggest observed KPI value
QLineSeries *seriesMax;
/// Line series used to plot an indication of the average KPI value
QLineSeries *seriesAvg;
/// Horizontal axis of the chart
QValueAxis *axisX;
/// Vertical axis of the chart
QValueAxis *axisY;
};
/// Crart class for plotting HARQ retransmission counters
class RTXPlot : public QChart {
Q_OBJECT
public:
/// Constructor
/// \param rounds Pointer to the HARQ round counters
RTXPlot(uint64_t *rounds);
protected:
/// This function is triggered when the own timer expires. It updates the plotted HARQ retransmission counters
/// \param event Pointer to the timer event
virtual void timerEvent(QTimerEvent *event) override;
private:
/// Pointer to the HARQ round counters
uint64_t *rounds;
/// Last stored HARQ round counters
uint64_t lastRounds[4];
/// Maximum observed HARQ retransmissions
int maxValue;
/// Index (horizontal position) of the last plotted HARQ retransmission value
int plotIdx;
/// Line series used to plot the four HARQ retransmission counters
QLineSeries *series[4];
/// Horizontal axis of the chart
QValueAxis *axisX;
/// Vertical axis of the chart
QValueAxis *axisY;
};
/// Widget showing one selectable gNB KPI
class PainterWidgetGnb : public QWidget, public ValueProvider {
Q_OBJECT
public:
/// Constructor
/// \param config Pointer to the dialog box for configuring gNB KPI Limits
/// \param comboBox Pointer to the drop-down list selecting the KPI to be shown here
/// \param p Pointer to the gNB parameters
PainterWidgetGnb(QWidget *config, QComboBox *comboBox, scopeData_t *p);
/// This function provides the current KPI value to be plotted
virtual float getValue() override;
protected:
/// This function is called to change the widget size
/// \param event Pointer to the resize event
virtual void resizeEvent(QResizeEvent *event) override;
public slots:
/// This function is called when a different KPI is selected
/// \param type selected KPI type
void makeConnections(int type);
private:
/// Pointer to the dialog box for configuring gNB KPI Limits
QWidget *config;
/// Pointer to the drop-down list selecting the KPI to be shown here
QComboBox *comboBox;
/// Pointer to the gNB parameters
scopeData_t *p;
/// Pointer to a class to view all QChart based KPIs
QChartView *chartView;
/// Pointer to the waterfall diagram
WaterFall *waterFall;
/// Currently plotted KPI type
PlotTypeGnb plotType;
};
/// Widget showing one selectable UE KPI
class PainterWidgetUE : public QWidget, public ValueProvider {
Q_OBJECT
public:
/// Constructor
/// \param config Pointer to the dialog box for configuring UE KPI Limits
/// \param comboBox Pointer to the drop-down list selecting the KPI to be shown here
/// \param ue Pointer to the UE parameters
PainterWidgetUE(QWidget *config, QComboBox *comboBox, PHY_VARS_NR_UE *ue);
/// This function provides the current KPI value to be plotted
virtual float getValue() override;
protected:
/// This function is called to change the widget size
/// \param event Pointer to the resize event
virtual void resizeEvent(QResizeEvent *event) override;
public slots:
/// This function is called when a different KPI is selected
/// \param type selected KPI type
void makeConnections(int type);
private:
/// Pointer to the dialog box for configuring UE KPI Limits
QWidget *config;
/// Pointer to the drop-down list selecting the KPI to be shown here
QComboBox *comboBox;
/// Pointer to the UE parameters
PHY_VARS_NR_UE *ue;
/// Pointer to a class to view all QChart based KPIs
QChartView *chartView;
/// Pointer to the waterfall diagram
WaterFall *waterFall;
/// Currently plotted KPI type
PlotTypeUE plotType;
};
#endif // QT_SCOPE_MAINWINDOW_H
+2 -43
View File
@@ -1042,6 +1042,7 @@ STATICFORXSCOPE OAI_phy_scope_t *create_phy_scope_nrue(int ID)
// LLR of PDCCH
fdui->graph[5] = nrUEcommonGraph(uePcchLLR,
FL_POINTS_XYPLOT, 0, curY, 500, 100, "PDCCH Log-Likelihood Ratios (LLR, mag)", FL_CYAN );
fl_set_xyplot_xgrid(fdui->graph[5].graph,FL_GRID_MAJOR);
fdui->graph[5].chartid = SCOPEMSG_DATAID_LLR; // tells websrv frontend to use LLR chart for displaying
fdui->graph[5].datasetid = 1; // tells websrv frontend to use dataset index 1 in LLR chart
// I/Q PDCCH comp
@@ -1054,6 +1055,7 @@ STATICFORXSCOPE OAI_phy_scope_t *create_phy_scope_nrue(int ID)
// LLR of PDSCH
fdui->graph[7] = nrUEcommonGraph(uePdschLLR,
FL_POINTS_XYPLOT, 0, curY, 500, 200, "PDSCH Log-Likelihood Ratios (LLR, mag)", FL_YELLOW );
fl_set_xyplot_xgrid(fdui->graph[7].graph,FL_GRID_MAJOR);
fdui->graph[7].chartid = SCOPEMSG_DATAID_LLR; // tells websrv frontend to use LLR chart for displaying
fdui->graph[7].datasetid = 2; // tells websrv frontend to use dataset index 2 in LLR chart
// I/Q PDSCH comp
@@ -1139,49 +1141,6 @@ static void *nrUEscopeThread(void *arg) {
pthread_mutex_t UEcopyDataMutex;
void UEcopyData(PHY_VARS_NR_UE *ue, enum UEdataType type, void *dataIn, int elementSz, int colSz, int lineSz) {
// Local static copy of the scope data bufs
// The active data buf is alterned to avoid interference between the Scope thread (display) and the Rx thread (data input)
// Index of "2" could be set to the number of Rx threads + 1
static scopeGraphData_t *copyDataBufs[UEdataTypeNumberOfItems][3] = {0};
static int copyDataBufsIdx[UEdataTypeNumberOfItems] = {0};
scopeData_t *tmp=(scopeData_t *)ue->scopeData;
if (tmp) {
// Begin of critical zone between UE Rx threads that might copy new data at the same time:
pthread_mutex_lock(&UEcopyDataMutex);
int newCopyDataIdx = (copyDataBufsIdx[type]<2)?copyDataBufsIdx[type]+1:0;
copyDataBufsIdx[type] = newCopyDataIdx;
pthread_mutex_unlock(&UEcopyDataMutex);
// End of critical zone between UE Rx threads
// New data will be copied in a different buffer than the live one
scopeGraphData_t *copyData= copyDataBufs[type][newCopyDataIdx];
if (copyData == NULL || copyData->dataSize < elementSz*colSz*lineSz) {
scopeGraphData_t *ptr=realloc(copyData, sizeof(scopeGraphData_t) + elementSz*colSz*lineSz);
if (!ptr) {
LOG_E(PHY,"can't realloc\n");
return;
} else {
copyData=ptr;
}
}
copyData->dataSize=elementSz*colSz*lineSz;
copyData->elementSz=elementSz;
copyData->colSz=colSz;
copyData->lineSz=lineSz;
memcpy(copyData+1, dataIn, elementSz*colSz*lineSz);
copyDataBufs[type][newCopyDataIdx] = copyData;
// The new data just copied in the local static buffer becomes live now
((scopeGraphData_t **)tmp->liveData)[type]=copyData;
}
}
STATICFORXSCOPE void nrUEinitScope(PHY_VARS_NR_UE *ue)
{
AssertFatal(ue->scopeData=malloc(sizeof(scopeData_t)),"");
-7
View File
@@ -40,13 +40,6 @@
typedef c16_t scopeSample_t;
#define SquaredNorm(VaR) ((VaR).r * (VaR).r + (VaR).i * (VaR).i)
typedef struct {
int dataSize;
int elementSz;
int colSz;
int lineSz;
} scopeGraphData_t;
typedef struct OAIgraph {
FL_OBJECT *graph;
FL_OBJECT *text;
+41
View File
@@ -52,3 +52,44 @@ int end_forms(void) {
return -1;
}
void UEcopyData(PHY_VARS_NR_UE *ue, enum UEdataType type, void *dataIn, int elementSz, int colSz, int lineSz) {
// Local static copy of the scope data bufs
// The active data buf is alterned to avoid interference between the Scope thread (display) and the Rx thread (data input)
// Index of "2" could be set to the number of Rx threads + 1
static scopeGraphData_t *copyDataBufs[UEdataTypeNumberOfItems][2] = {0};
static int copyDataBufsIdx[UEdataTypeNumberOfItems] = {0};
scopeData_t *tmp = (scopeData_t *)ue->scopeData;
if (tmp) {
// Begin of critical zone between UE Rx threads that might copy new data at the same time: might require a mutex
int newCopyDataIdx = (copyDataBufsIdx[type]==0)?1:0;
copyDataBufsIdx[type] = newCopyDataIdx;
// End of critical zone between UE Rx threads
// New data will be copied in a different buffer than the live one
scopeGraphData_t *copyData = copyDataBufs[type][newCopyDataIdx];
if (copyData == NULL || copyData->dataSize < elementSz*colSz*lineSz) {
scopeGraphData_t *ptr = (scopeGraphData_t*) realloc(copyData, sizeof(scopeGraphData_t) + elementSz*colSz*lineSz);
if (!ptr) {
LOG_E(PHY,"can't realloc\n");
return;
} else {
copyData = ptr;
}
}
copyData->dataSize = elementSz*colSz*lineSz;
copyData->elementSz = elementSz;
copyData->colSz = colSz;
copyData->lineSz = lineSz;
memcpy(copyData+1, dataIn, elementSz*colSz*lineSz);
copyDataBufs[type][newCopyDataIdx] = copyData;
// The new data just copied in the local static buffer becomes live now
((scopeGraphData_t **)tmp->liveData)[type] = copyData;
}
}
+19
View File
@@ -34,6 +34,14 @@
#include <openair1/PHY/defs_gNB.h>
#include <openair1/PHY/defs_nr_UE.h>
typedef struct {
uint32_t nb_total;
uint32_t nb_nack;
uint32_t blockSize; // block size, to be used for throughput calculation
uint16_t nofRBs;
uint8_t dl_mcs;
} extended_kpi_ue;
typedef struct {
int *argc;
char **argv;
@@ -63,8 +71,19 @@ typedef struct scopeData_s {
void (*copyData)(PHY_VARS_NR_UE *,enum UEdataType, void *data, int elementSz, int colSz, int lineSz);
} scopeData_t;
typedef struct {
int dataSize;
int elementSz;
int colSz;
int lineSz;
} scopeGraphData_t;
int load_softscope(char *exectype, void *initarg);
int end_forms(void) ;
void UEcopyData(PHY_VARS_NR_UE *ue, enum UEdataType type, void *dataIn, int elementSz, int colSz, int lineSz);
#define UEscopeCopy(ue, type, ...) if(ue->scopeData) ((scopeData_t*)ue->scopeData)->copyData(ue, type, ##__VA_ARGS__);
extended_kpi_ue* getKPIUE();
#endif
+22 -3
View File
@@ -1,12 +1,31 @@
To use the scope, run the xNB or the UE with option "-d"
## xForms-based Scope
To use the scope, run the xNB or the UE with option "-d"
Usage in gdb
In gdb, when you break, you can refresh immediatly the scope view by calling the display function.
The first paramter is the graph context, nevertheless we keep the last value for a dirty call in gdb (so you can use '0')
The first paramter is the graph context, nevertheless we keep the last value for a dirty call in gdb (so you can use '0')
Example with no variable known
phy_scope_nrUE(0, PHY_vars_UE_g[0][0], 0, 0, 0)
or
phy_scope_gNB(0, phy_vars_gnb, phy_vars_ru, UE_id)
# Qt-based Scope
## Building Instuctions
For the new qt-based scopo designed for NR, please consider the following:
1. run the gNB or the UE with the option '--dqt'.
2. make sure to install the Qt5 packages before running the scope. Otherwise, the scope will NOT be displayed!
3. if you need only to build the new scope, then add 'nrqtscope' after the '--lib-build' option. So, the complete
command would be
```
./build_oai --gNB -w USRP --nrUE --build-lib nrqtscope
```
## New Features
1. New KPIs for both gNB and UE, e.g., BLER, MCS, throughout, and number of scheduled RBs.
2. For each of the gNB and UE, a main widget is created with a 3x2 grid of sub-widgets, each to dispaly one KPI.
3. Each of the sub-widgets has a drop-down list to choose the KPI to show in that sub-widget.
4. Both of the gNB and UE scopes can be resized using the mouse movement.
-2
View File
@@ -36,8 +36,6 @@
#include "defs_nr_common.h"
#include "CODING/nrPolar_tools/nr_polar_pbch_defs.h"
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
+2 -2
View File
@@ -139,7 +139,7 @@ typedef struct TDD_UL_DL_configCommon_s {
struct TDD_UL_DL_configCommon_s *p_next;
} TDD_UL_DL_configCommon_t;
typedef struct {
typedef struct TDD_UL_DL_SlotConfig_s {
/// \ Identifies a slot within a dl-UL-TransmissionPeriodicity (given in tdd-UL-DL-configurationCommon)
uint16_t slotIndex;
/// \ The direction (downlink or uplink) for the symbols in this slot. "allDownlink" indicates that all symbols in this slot are used
@@ -154,7 +154,7 @@ typedef struct {
/// Corresponds to L1 parameter 'number-of-UL-symbols-dedicated' (see 38.211, section FFS_Section)
uint16_t nrofUplinkSymbols;
/// \ for setting a sequence
struct TDD_UL_DL_SlotConfig_t *p_next_TDD_UL_DL_SlotConfig;
struct TDD_UL_DL_SlotConfig_s *p_next_TDD_UL_DL_SlotConfig;
} TDD_UL_DL_SlotConfig_t;
/***********************************************************************
+1 -1
View File
@@ -27,7 +27,7 @@
#ifdef XFORMS
#include "PHY/TOOLS/nr_phy_scope.h"
extern char do_forms;
extern uint32_t do_forms;
#endif
extern char* namepointer_chMag ;
+1 -1
View File
@@ -187,7 +187,7 @@ void add_tdd_dedicated_configuration_nr(NR_DL_FRAME_PARMS *frame_parms, int slot
if (next == 0) {
frame_parms->p_TDD_UL_DL_ConfigDedicated = p_TDD_UL_DL_ConfigDedicated;
} else {
p_previous_TDD_UL_DL_ConfigDedicated->p_next_TDD_UL_DL_SlotConfig = (struct TDD_UL_DL_SlotConfig_t *)p_TDD_UL_DL_ConfigDedicated;
p_previous_TDD_UL_DL_ConfigDedicated->p_next_TDD_UL_DL_SlotConfig = (TDD_UL_DL_SlotConfig_t *)p_TDD_UL_DL_ConfigDedicated;
}
p_TDD_UL_DL_ConfigDedicated->slotIndex = slotIndex;